{"version":3,"sources":["webpack:///./behaviors/markdown/paste_markdown_table.js","webpack:///./dropzone_input.js","webpack:///./gl_form.js","webpack:///./lib/utils/file_upload.js","webpack:///./lib/utils/file_utility.js","webpack:///./lib/utils/vector.js","webpack:///./lib/utils/image_utils.js","webpack:///./lib/utils/text_markdown.js","webpack:///./behaviors/shortcuts/shortcuts.js"],"names":["PasteMarkdownTable","constructor","clipboardData","this","data","columnWidths","rows","tableFound","parseTable","isTable","convertToTableMarkdown","_this","calculateColumnWidths","markdownRows","map","row","column","index","formatColumn","join","splice","generateHeaderBreak","types","includes","htmlData","getData","doc","DOMParser","parseFromString","querySelectorAll","length","splitRows","trim","split","normalizeRows","columnCountsMatch","rowLengths","maxLength","Math","max","forEach","push","_this2","_column","columnIndex","maxColumnWidth","textColumnCount","htmlColumnCount","cells","Array","_this3","width","dropzoneInput","form","config","parallelUploads","_form$get$querySelect","iconPaperclip","spriteIcon","$attachingFileMessage","find","$cancelButton","$retryLink","$uploadProgress","$uploadingErrorContainer","$uploadingErrorMessage","$uploadingProgressContainer","uploadsPath","window","uploads_path","maxFileSize","gon","max_file_size","formTextarea","handlePaste","pasteText","addFileToForm","updateAttachingMessage","uploadFile","hasPlainText","wrap","$mdArea","closest","$formDropzone","parent","addClass","append","on","event","dropzone","url","dictDefaultMessage","clickable","get","querySelector","paramName","maxFilesize","uploadMultiple","headers","csrf","previewContainer","dragover","css","dragleave","removeClass","drop","focus","success","header","response","shouldPad","getQueuedFiles","getUploadingFiles","link","size","markdown","error","file","errorMessage","__","xhr","message","res","responseText","html","totaluploadprogress","totalUploadProgress","files","text","round","sending","removedfile","queuecomplete","$","remove","trigger","child","children","e","preventDefault","stopPropagation","Dropzone","forElement","removeAllFiles","dropzoneInstance","target","failedFiles","failedFile","status","ERROR","undefined","accepted","addFile","pasteEvent","originalEvent","items","converter","type","indexOf","MAX_FILE_NAME_LENGTH","filename","getFilename","truncateFilename","truncate","some","item","formattedText","textarea","insertText","dispatchEvent","Event","path","_escape","closeSpinner","handleAttachFile","click","formData","FormData","axios","post","then","async","md","height","getRetinaDimensions","$child","replaceStart","value","replaceEnd","setSelectionRange","insertToTextArea","catch","messageContainer","filesCount","filter","attachingMessage","n__","$attachFileButton","addEventListener","autoDiscover","GLForm","enableGFM","forceNew","gfmDataSources","defaultAutocompleteConfig","dataSources","gl","GfmAutoComplete","_isEmpty","Object","keys","destroy","setupForm","clearEventListeners","autoComplete","formDropzone","isNewForm","is","disableButtonIfEmptyField","setup","autosize","addEventListeners","addMarkdownListeners","show","isAutosizeable","setupAutosize","updateAutocompleteDataSources","updateDataSources","off","setHeightData","bind","destroyAutosize","setTimeout","outerHeight","removeMarkdownListeners","Boolean","buttonSelector","fileSelector","btn","document","fileInput","textContent","replace","fileName","name","validateImageName","test","validateFileFromAllowList","allowList","parts","ext","readFileAsDataURL","Promise","resolve","reader","FileReader","result","once","readAsDataURL","Vector","x","y","eq","x1","y1","neq","gte","gt","lte","lt","fn","mul","scalar","div","add","sub","floor","ceil","toSize","vector","stringToUInt32","str","buffer","char","charCodeAt","pngFile","pngImage","dataUrl","atob","fileToPngImage","pixelsPerInch","physPosition","phys","substring","getPixelsPerInch","ihdrPosition","ihdr","domElementToBlob","domElement","domToBlob","ALLOWED_UNDO_TAGS","HR_PATTERN","INDENTED_LINE_PATTERN","LIST_LINE_HEAD_PATTERN","QUOTE_LINE_PATTERN","compositioningNoteText","selectedText","selectionStart","selectionEnd","addBlockTags","blockTag","selected","isSelectionBracketed","textArea","before","characterBeforeSelection","after","min","characterAfterSelection","linesFromSelection","startPos","lastIndexOf","endPos","lines","setNewSelectionRange","lineStart","firstLineChange","totalChanged","newStart","newEnd","getEditorSelectionRange","editor","start","sel","getSelection","startLineNumber","startColumn","end","endLineNumber","endColumn","insertMarkdownText","tag","cursorOffset","select","editorSelectionStart","editorSelectionEnd","lastNewLine","textToUpdate","removedLastNewLine","removedFirstNewLine","currentLineEmpty","toString","selectionRange","isValidURL","selectedSplit","getValue","substr","isBeginning","startChar","shouldUseSelectedWithoutTags","removeTagFromLine","shouldRemoveTagFromLine","getSelectedWithoutTags","shouldRemoveQuote","every","val","slice","match","line","startsWith","endsWith","prepareInsertMarkdownText","lastLine","rangeWithBlockTags","Range","editorBlockTagText","lineBefore","lineBeforeSelection","lineAfterSelection","blockTagText","String","replaceSelectedText","positionBetweenTags","pos","startPosition","endPosition","selectWithinSelection","moveCursor","updateText","tagContent","$textArea","shouldHandleIndentation","key","altKey","ctrlKey","metaKey","shiftKey","isEnterPressedOnly","createOlText","listLineMatch","num","indent","leader","groups","postfix","updateOlLineNumbersAfterSelection","from","condition","selectionEndOfLine","remainingText","currentPos","linesAfterSelection","_textMatch$groups","textMatch","nextIndent","isOl","nextIsOl","incrementedLines","orderingNumber","lineMatch","lineContent","incrementedLine","textToInsert","updateOlLineNumbers","handleContinueList","markdown_automatic_lists","selectedLines","firstSelectedLine","content","emptyListItem","prefixLength","itemToInsert","nextNum","parseInt","parseOlNum","keypressNoteText","_$$atwho","_$","atwho","call","isDefaultPrevented","_gon$features","features","continueIndentedText","handleContinueIndentedText","markdown_surround_selection","_","handleSurroundSelectedText","compositionStartNoteText","compositionEndNoteText","handlePasteModifications","pastedText","stopImmediatePropagation","handleMarkdownPasteUrl","updateTextForToolbarBtn","$toolbarBtn","shiftedLines","totalAdded","repeat","indentLines","totalRemoved","removedFromFirstline","removedFromLine","outdentLines","attr","each","Shortcuts","initMarkdownEditorShortcuts","addEditorMarkdownListeners","mdTag","mdBlock","mdPrepend","mdSelect","currentTarget","getSelectedText","removeMarkdownEditorShortcuts","resolveSelectedImage","markdownPreviewPath","selectedLine","lineSelectionStart","preExlm","postClose","imageMarkdown","imageURL","body","getAttribute","repeatCodeBackticks","_content$match","numBackticks","sort","a","b","LOCAL_MOUSETRAP_DATA_KEY","getToolbarBtnToShortcutsMap","$textarea","$allToolbarBtns","Map","keyboardShortcuts","set","_gon","extensions","onToggleHelp","helpModalElement","helpModalVueInstance","addAll","TOGGLE_KEYBOARD_SHORTCUTS_DIALOG","START_SEARCH_PROJECT_FILE","focusSearchFile","START_SEARCH","focusSearch","FOCUS_FILTER_BAR","focusFilter","TOGGLE_PERFORMANCE_BAR","onTogglePerfBar","HIDE_APPEARING_CONTENT","hideAppearingContent","TOGGLE_CANARY","onToggleCanary","GO_TO_YOUR_TODO_LIST","findAndFollowLink","GO_TO_ACTIVITY_FEED","GO_TO_YOUR_ISSUES","GO_TO_YOUR_MERGE_REQUESTS","GO_TO_YOUR_REVIEW_REQUESTS","GO_TO_YOUR_PROJECTS","GO_TO_YOUR_GROUPS","GO_TO_MILESTONE_LIST","GO_TO_YOUR_SNIPPETS","TOGGLE_MARKDOWN_PREVIEW","toggleMarkdownPreview","addStopCallback","element","combo","keysFor","findAndReplace","FIND_AND_REPLACE","toggleFindAndReplaceBar","shouldDisableShortcuts","disableShortcuts","filterSelectors","addExtension","Extension","args","extensionsCurrentlyLoading","Set","instance","Dep","_Extension$dependenci","dependencies","has","delete","command","callback","Mousetrap","commandsAndCallbacks","commandAndCallback","$destroy","createElement","Vue","el","components","ShortcutsHelp","render","hidden","parseBoolean","getCookie","setCookie","refreshCurrentPage","currentValue","expires","triggerHandler","elements","visibleElement","offsetParent","_document$querySelect","InternalEvents","trackEvent","_document$querySelect2","_document$querySelect3","FIND_FILE_SHORTCUT_CLICK","searchInput","waitForElement","currentPath","dataset","style","display","handler","toolbarBtnToShortcutsMap","localMousetrap","allShortcuts","_flatten","values","originalStopCallback","prototype","stopCallback","unbind"],"mappings":"6aAGe,MAAMA,EACnBC,YAAYC,GACVC,KAAKC,KAAOF,EACZC,KAAKE,aAAe,GACpBF,KAAKG,KAAO,GACZH,KAAKI,WAAaJ,KAAKK,aAGzBC,UACE,OAAON,KAAKI,WAGdG,yBAAyB,IAAAC,EAAA,KACvBR,KAAKS,wBAEL,MAAMC,EAAeV,KAAKG,KAAKQ,KAC7B,SAACC,GAAG,WAMGA,EAAID,KAAI,SAACE,EAAQC,GAAK,OAAKN,EAAKO,aAAaF,EAAQC,MAAQE,KAAK,cAM3E,OAFAN,EAAaO,OAAO,EAAG,EAAGjB,KAAKkB,uBAExBR,EAAaM,KAAK,MAY3BX,aACE,IAAKL,KAAKC,KAAKkB,MAAMC,SAAS,eAAiBpB,KAAKC,KAAKkB,MAAMC,SAAS,cACtE,OAAO,EAGT,MAAMC,EAAWrB,KAAKC,KAAKqB,QAAQ,aACnCtB,KAAKuB,KAAM,IAAIC,WAAYC,gBAAgBJ,EAAU,aAQrD,GAAsB,IANPrB,KAAKuB,IAAIG,iBAAiB,+BAM9BC,OACT,OAAO,EAGT,MACMC,EADO5B,KAAKC,KAAKqB,QAAQ,cAAcO,OACtBC,MAAM,iCAG7B,OAAI9B,KAAKuB,IAAIG,iBAAiB,MAAMC,SAAWC,EAAUD,SAIzD3B,KAAKG,KAAOyB,EAAUjB,KAAI,SAACC,GAAG,OAAKA,EAAIkB,MAAM,SAC7C9B,KAAK+B,kBAKA/B,KAAKgC,qBAQZD,gBACE,MAAME,EAAajC,KAAKG,KAAKQ,KAAI,SAACC,GAAG,OAAKA,EAAIe,UACxCO,EAAYC,KAAKC,OAAOH,GAE9BjC,KAAKG,KAAKkC,SAAQ,SAACzB,GACjB,KAAOA,EAAIe,OAASO,GAClBtB,EAAI0B,KAAK,OAKf7B,wBAAwB,IAAA8B,EAAA,KACtBvC,KAAKE,aAAeF,KAAKG,KAAK,GAAGQ,KAAI,SAAC6B,EAASC,GAAW,OA/FvC,SAACtC,EAAMsC,GAAW,OACvCN,KAAKC,OAAOjC,EAAKQ,KAAI,SAACC,GAAG,OAAKA,EAAI6B,GAAad,WA+F3Ce,CAAeH,EAAKpC,KAAMsC,MAI9BT,oBACE,MAAMW,EAAkB3C,KAAKG,KAAK,GAAGwB,OACrC,IAAIiB,EAAkB,EAMtB,OAJA5C,KAAKuB,IAAIG,iBAAiB,YAAYW,SAAQ,SAACzB,GAC7CgC,EAAkBT,KAAKC,IAAIxB,EAAIiC,MAAMlB,OAAQiB,MAGxCD,IAAoBC,EAG7B7B,aAAaF,EAAQC,GAEnB,OAAOD,EADQiC,MAAM9C,KAAKE,aAAaY,GAASD,EAAOc,OAAS,GAAGX,KAAK,KAI1EE,sBAAsB,IAAA6B,EAAA,KAKpB,MAAO,IAHQ/C,KAAKE,aAAaS,KAAI,SAACqC,EAAOlC,GAAK,OAChDgC,MAAMC,EAAK7C,aAAaY,GAAS,GAAGE,KAAK,QAEzBA,KAAK,S,4BC7FZ,SAASiC,EAAcC,EAAMC,EAAS,CAAEC,gBAAiB,IAAK,IAAAC,EAC3E,MACMC,EAAgBC,YAAW,YAAa,yBACxCC,EAAwBN,EAAKO,KAAK,2BAClCC,EAAgBR,EAAKO,KAAK,kCAC1BE,EAAaT,EAAKO,KAAK,yBACvBG,EAAkBV,EAAKO,KAAK,uBAC5BI,EAA2BX,EAAKO,KAAK,8BACrCK,EAAyBZ,EAAKO,KAAK,4BACnCM,EAA8Bb,EAAKO,KAAK,iCACxCO,EAAcd,EAAKjD,KAAK,iBAAmBgE,OAAOC,cAAgB,KAClEC,EAAcC,IAAIC,eAAiB,GACnCC,EAAepB,EAAKO,KAAK,iBAC/B,IAAIc,EACAC,EACAC,EACAC,EACAC,EACAC,EAEJN,EAAaO,KAAK,oCAGlB,MAAMC,EAAUR,EAAaS,QAAQ,YAC/BC,EAAgB9B,EAAKO,KAAK,iBAKhC,GAJAuB,EAAcC,SAASC,SAAS,wBAChCF,EAAcG,OAzBG,0CA0BjBH,EAAcvB,KAAK,uBAAuB0B,OAAO7B,IAE5CU,EAEH,OADAgB,EAAcE,SAAS,uBAChB,KAGTZ,EAAac,GAAG,SAAS,SAACC,GAAK,OAAKd,EAAYc,MAEhD,MAAMC,EAAWN,EAAcM,SAAS,CACtCC,IAAKvB,EACLwB,mBAAoB,GACpBC,UAAwE,QAA/DpC,EAAEH,EAAKwC,IAAI,GAAGC,cAAc,2CAAmC,IAAAtC,KACxEuC,UAAW,OACXC,YAAa1B,EACb2B,gBAAgB,EAChBC,QAASC,IAAKD,QACdE,kBAAkB,KACf9C,EACH+C,SAAU,WACRpB,EAAQI,SAAS,qBACjBhC,EAAKO,KAAK,uBAAuB0C,IAAI,UAAW,KAElDC,UAAW,WACTtB,EAAQuB,YAAY,qBACpBnD,EAAKO,KAAK,uBAAuB0C,IAAI,UAAW,IAElDG,KAAM,WACJxB,EAAQuB,YAAY,qBACpBnD,EAAKO,KAAK,uBAAuB0C,IAAI,UAAW,GAChD7B,EAAaiC,SAEfC,QAAQC,EAAQC,GACd,MACMC,EADsB3G,KAAK4G,iBAAiBjF,OAAS3B,KAAK6G,oBAAoBlF,QAC3C,EAEzC8C,EAAciC,EAASI,KAAKvB,IAAKkB,EAAOM,MACxCvC,EAAUkC,EAASI,KAAKE,SAAUL,IAEpCM,MAAO,SAACC,EAAMC,EAAeC,aAAG,8BAA+BC,GAO7D,MAAMC,GAjFaC,EAiFaJ,GAAgBE,EAAIG,eAhF7B,iBAARD,EAIZA,EAAID,QAHFC,EAFX,IAAyBA,EAmFnB1D,EAAyBwC,YAAY,QACrCvC,EAAuB2D,KAAKH,GAC5B5D,EAAcwB,SAAS,SAEzBwC,oBAAoBC,GAClBjD,EAAuB1E,KAAK4H,MAAOpE,GACnCI,EAAgBiE,KAAQ1F,KAAK2F,MAAMH,GAAd,MAEvBI,QAAS,WAIPlE,EAAyBqB,SAAS,QAClCnB,EAA4BsC,YAAY,QACxC3C,EAAc2C,YAAY,SAE5B2B,YAAa,WACXtE,EAAcwB,SAAS,QACvBnB,EAA4BmB,SAAS,QACrCrB,EAAyBqB,SAAS,SAEpC+C,cAAe,WACbC,IAAE,eAAeC,SACjBD,IAAE,kBAAkBE,QAAQ,SAE5BrE,EAA4BmB,SAAS,QACrCxB,EAAcwB,SAAS,WAIrBmD,EAAQH,IAAE5C,EAAS,IAAIgD,SAAS,YAItC5E,EAAc0B,GAAG,SAAS,SAACmD,GACzBA,EAAEC,iBACFD,EAAEE,kBACFC,IAASC,WAAW3D,EAAcU,IAAI,IAAIkD,gBAAe,MAO3DjF,EAAWyB,GAAG,SAAS,SAACmD,GACtB,MAAMM,EAAmBH,IAASC,WAChCJ,EAAEO,OAAO/D,QAAQ,wBAAwBY,cAAc,kBAEnDoD,EAAcF,EAAiBjB,MAErCW,EAAEC,iBAIFK,EAAiBD,gBAAe,GAEhCG,EAAYpI,KAAI,SAACqI,GACf,MAAM9B,EAAO8B,EAOb,OALI9B,EAAK+B,SAAWP,IAASQ,QAC3BhC,EAAK+B,YAASE,EACdjC,EAAKkC,cAAWD,GAGXN,EAAiBQ,QAAQnC,SAIpC3C,EAAc,SAACc,GACb,MAAMiE,EAAajE,EAAMkE,eACnB,cAAExJ,GAAkBuJ,EAC1B,GAAIvJ,GAAiBA,EAAcyJ,MAAO,CACxC,MAAMC,EAAY,IAAI5J,EAAmBE,GAGzC,GAAI0J,EAAUnJ,UAAW,CACvB+E,EAAMmD,iBACN,MAAMX,EAAO4B,EAAUlJ,yBACvBiE,EAAUqD,QACL,IAAKjD,EAAa0E,GAAa,CACnB,IAAIvJ,EAAc6H,OAC1BvF,SAAQ,SAAC6E,GAChB,IAAoC,IAAhCA,EAAKwC,KAAKC,QAAQ,SAAiB,CACrCtE,EAAMmD,iBACN,MAAMoB,EAAuB,IAEvBC,EAAWC,YAAY5C,IAAS,YAChC6C,EAAmBC,YAASH,EAAUD,GAE5CpF,EADa,KAAKuF,OAGlBpF,EAAWuC,EAAM6C,UAO3BnF,EAAe,SAAC3E,GAEd,MAD0B,IAAIA,EAAKF,cAAcyJ,OACxBS,MAAK,SAACC,GAAI,MAAmB,eAAdA,EAAKR,SAG/ClF,EAAY,SAACqD,EAAMlB,GACjB,IAAIwD,EAAgBtC,EAChBlB,IACFwD,GAAiB,QAEnB,MAAMC,EAAW/B,EAAM3C,IAAI,GAC3B2E,YAAWD,EAAUD,GACrB7F,EAAaoB,IAAI,GAAG4E,cAAc,IAAIC,MAAM,UAC5CjG,EAAa8D,QAAQ,UAGvB3D,EAAgB,SAAC+F,GACftC,IAAEhF,GAAMiC,OAAO,8CAA8CsF,IAAOD,SAGtE,MAEME,EAAe,kBAAM3G,EAA4BmB,SAAS,SAuDhE,SAASyF,EAAiBpC,GACxBA,EAAEC,iBACFN,IAAElI,MAAM+E,QAAQ,aAAatB,KAAK,iBAAiBmH,QACnDtG,EAAaiC,QArCf5B,EAAa,SAACuF,EAAML,GAClB,MAAMgB,EAAW,IAAIC,SACrBD,EAAS1F,OAAO,OAAQ+E,EAAML,GAzBN9F,EAA4BsC,YAAY,QA6BhE0E,IACGC,KAAKhH,EAAa6G,GAClBI,MAAKC,gBAAO,KAAEjL,IACb,IAAIkL,EAAKlL,EAAK6G,KAAKE,SAEnB,MAAM,MAAEhE,EAAK,OAAEoI,SAAkBC,YAAoBnB,IAAU,GAC3DlH,GAASoI,IAEXD,GAAM,UAAUnI,YAAgBoI,MA5Bf,SAACvB,EAAUtE,GAClC,MAAM+F,EAASpD,IAAEG,GACX+B,EAAWkB,EAAO5F,IAAI,GACtByE,EAAgB,KAAKN,MAErB0B,EAAenB,EAASoB,MAAM7B,QAAQQ,GAC5C,IAAsB,IAAlBoB,EAAqB,CACvB,MAAME,EAAaF,EAAepB,EAAcxI,OAChDyI,EAASsB,kBAAkBH,EAAcE,GAE3CpB,YAAWD,EAAU7E,GACrB+F,EAAOlD,QAAQ,UAmBXuD,CAAiB9B,EAAUsB,GAC3BT,OAEDkB,OAAM,SAACrD,GAtCM,IAACjB,IAuCHiB,EAAE7B,SAASzG,KAAKqH,QAtC9BzD,EAAyBwC,YAAY,QACrCvC,EAAuB2D,KAAKH,GAsCxBoD,QAINhG,EAAyB,SAACkD,EAAOiE,GAC/B,MAAMC,EAAalE,EAAMmE,QACvB,SAAC7E,GAAI,MAAqB,cAAhBA,EAAK+B,QAA0C,WAAhB/B,EAAK+B,UAC9CtH,OACIqK,EAAmBC,cAAI,mBAAoB,qBAAsBH,GAEvED,EAAiBhE,KAAQmE,EAAH,OASxB9I,EAAKO,KAAK,sBAAsBmH,MAAMD,GAEtC,MAAMuB,EAAoBhJ,EAAKO,KAAK,0BAKpC,OAJIyI,EAAkBvK,QACpBuK,EAAkBxG,IAAI,GAAGyG,iBAAiB,QAASxB,GAG9C3F,EAAcU,IAAI,GAAKgD,IAASC,WAAW3D,EAAcU,IAAI,IAAM,KAtR5EgD,IAAS0D,cAAe,E,gBCLT,MAAMC,EAYnBvM,YAAYoD,EAAMoJ,EAAY,GAAIC,GAAW,EAAOC,EAAiB,IAAI,IAAAhM,EAAA,KACvER,KAAKkD,KAAOA,EACZlD,KAAKoK,SAAWpK,KAAKkD,KAAKO,KAAK,yBAC/BzD,KAAKsM,UAAY,IAAKG,OAA8BH,GAGpD,IAAII,EAAeC,GAAGC,iBAAmBD,GAAGC,gBAAgBF,aAAgB,GAEvEG,IAAQL,KACXE,EAAcF,GAGhBM,OAAOC,KAAK/M,KAAKsM,WAAWjK,SAAQ,SAAC6H,GACtB,WAATA,GAAsBwC,EAAYxC,KACpC1J,EAAK8L,UAAUpC,IAAQ,MAK3BlK,KAAKgN,UAELhN,KAAKiN,UAAUP,EAAaH,GAC5BvM,KAAKkD,KAAKjD,KAAK,SAAUD,MAG3BgN,UAEEhN,KAAKkN,sBACDlN,KAAKmN,cACPnN,KAAKmN,aAAaH,UAEhBhN,KAAKoN,cACPpN,KAAKoN,aAAaJ,UAGpBhN,KAAKkD,KAAKjD,KAAK,SAAU,MAG3BgN,UAAUP,EAAaH,GAAW,GAChC,MAAMc,EAAYrN,KAAKkD,KAAKoK,GAAG,oBAAsBf,EACrDvM,KAAKkD,KAAKmD,YAAY,oBAClBgH,IACFrN,KAAKkD,KAAKO,KAAK,iBAAiB0E,SAChCnI,KAAKkD,KAAKgC,SAAS,YAEnBqI,YACEvN,KAAKkD,KAAKO,KAAK,iBACfzD,KAAKkD,KAAKO,KAAK,gDAEjBzD,KAAKmN,aAAe,IAAIP,IAAgBF,GACxC1M,KAAKmN,aAAaK,MAAMxN,KAAKkD,KAAKO,KAAK,iBAAkBzD,KAAKsM,WAC9DtM,KAAKoN,aAAenK,EAAcjD,KAAKkD,KAAM,CAAEE,gBAAiB,IAE5DpD,KAAKkD,KAAKoK,GAAG,0BACfG,YAASzN,KAAKoK,WAIlBpK,KAAK0N,oBACLC,YAAqB3N,KAAKkD,MAC1BlD,KAAKkD,KAAK0K,OACN5N,KAAK6N,gBAAgB7N,KAAK8N,iBACU,IAApC9N,KAAKoK,SAASnK,KAAK,cAAuBD,KAAKoK,SAAS7D,QAG9DwH,8BAA8BrB,GACxB1M,KAAKmN,cACPnN,KAAKmN,aAAaa,kBAAkBtB,GAIxCoB,gBAAgB,IAAAvL,EAAA,KAEdvC,KAAKoK,SAAS6D,IAAI,oBAAoB7I,GAAG,mBAAoBpF,KAAKkO,cAAcC,KAAKnO,OAGrFA,KAAKoK,SAAS6D,IAAI,oBAAoB7I,GAAG,mBAAoBpF,KAAKoO,gBAAgBD,KAAKnO,OAEvFqO,YAAW,WACTZ,YAASlL,EAAK6H,UACd7H,EAAK6H,SAASjE,IAAI,SAAU,cAC3B,GAGL+H,gBACElO,KAAKoK,SAASnK,KAAK,SAAUD,KAAKoK,SAASkE,eAG7CF,kBACE,MAAME,EAActO,KAAKoK,SAASkE,cAE9BtO,KAAKoK,SAASnK,KAAK,YAAcqO,IAErCb,IAAST,QAAQhN,KAAKoK,UAEtBpK,KAAKoK,SAASnK,KAAK,SAAUqO,GAC7BtO,KAAKoK,SAASkE,YAAYA,GAC1BtO,KAAKoK,SAASjE,IAAI,aAAclC,OAAOqK,cAGzCpB,sBAEElN,KAAKoK,SAAS6D,IAAI,SAElBjO,KAAKoK,SAAS6D,IAAI,QAClBM,YAAwBvO,KAAKkD,MAG/BwK,oBACE1N,KAAKoK,SAAShF,GAAG,SAAS,WACxB8C,IAAElI,MAAM+E,QAAQ,YAAYG,SAAS,iBAEvClF,KAAKoK,SAAShF,GAAG,QAAQ,WACvB8C,IAAElI,MAAM+E,QAAQ,YAAYsB,YAAY,iBAI5C,2BACE,OAAOmI,QAAQxO,KAAKoK,SAASnK,KAAK,8B,kCC1ItC,sGAAe,aAACwO,EAAgBC,GAC9B,MAAMC,EAAMC,SAASjJ,cAAc8I,GAC7BI,EAAYD,SAASjJ,cAAc+I,GAEzC,IAAKC,IAAQE,EAAW,OAExB,MAAM3L,EAAOyL,EAAI5J,QAAQ,QAEzB4J,EAAIxC,iBAAiB,SAAS,WAC5B0C,EAAUjE,WAGZiE,EAAU1C,iBAAiB,UAAU,WACnCjJ,EAAKyC,cAAc,gBAAgBmJ,YAAcD,EAAUrD,MAAMuD,QAAQ,YAAa,QAInF,MAAMjF,EAAc,SAAC5C,GAC1B,IAAI8H,EAKJ,OAJI9H,IACF8H,EAAW9H,EAAK+H,MAGXD,GAGIE,EAAoB,SAAChI,GAChC,MAAM8H,EAAW9H,EAAK+H,KAAO/H,EAAK+H,KAAO,YAEzC,MADwB,oDACDE,KAAKH,GAAYA,EAAW,aAGxCI,EAA4B,SAACJ,EAAUK,GAClD,MAAMC,EAAQN,EAASlN,MAAM,KACvByN,EAAM,IAAID,EAAMA,EAAM3N,OAAS,GAErC,OAAO0N,EAAUjO,SAASmO,K,kCC/BrB,SAASC,EAAkBtI,GAChC,OAAO,IAAIuI,SAAQ,SAACC,GAClB,MAAMC,EAAS,IAAIC,WACnBD,EAAOxD,iBAAiB,QAAQ,SAAC5D,GAAC,OAAKmH,EAAQnH,EAAEO,OAAO+G,UAAS,CAAEC,MAAM,IACzEH,EAAOI,cAAc7I,MATzB,mC,oJCAA,MAAM8I,EACJlQ,YAAYmQ,EAAGC,GACblQ,KAAKiQ,EAAIA,EACTjQ,KAAKkQ,EAAIA,EAGXC,GAAGC,EAAIC,GACL,OAAOrQ,KAAKiQ,IAAMG,GAAMpQ,KAAKkQ,IAAMG,EAGrCC,IAAIF,EAAIC,GACN,OAAOrQ,KAAKiQ,IAAMG,GAAMpQ,KAAKkQ,IAAMG,EAGrCE,IAAIH,EAAIC,GACN,OAAOrQ,KAAKiQ,GAAKG,GAAMpQ,KAAKkQ,GAAKG,EAGnCG,GAAGJ,EAAIC,GACL,OAAOrQ,KAAKiQ,EAAIG,GAAMpQ,KAAKkQ,EAAIG,EAGjCI,IAAIL,EAAIC,GACN,OAAOrQ,KAAKiQ,GAAKG,GAAMpQ,KAAKkQ,GAAKG,EAGnCK,GAAGN,EAAIC,GACL,OAAOrQ,KAAKiQ,EAAIG,GAAMpQ,KAAKkQ,EAAIG,EAGjC1P,IAAIgQ,GACF,OAAO,IAAIX,EAAOW,EAAG3Q,KAAKiQ,GAAIU,EAAG3Q,KAAKkQ,IAGxCU,IAAIC,GACF,OAAO,IAAIb,EAAOhQ,KAAKiQ,EAAIY,EAAQ7Q,KAAKkQ,EAAIW,GAG9CC,IAAID,GACF,OAAO,IAAIb,EAAOhQ,KAAKiQ,EAAIY,EAAQ7Q,KAAKkQ,EAAIW,GAG9CE,IAAIX,EAAIC,GACN,OAAO,IAAIL,EAAOhQ,KAAKiQ,EAAIG,EAAIpQ,KAAKkQ,EAAIG,GAG1CW,IAAIZ,EAAIC,GACN,OAAO,IAAIL,EAAOhQ,KAAKiQ,EAAIG,EAAIpQ,KAAKkQ,EAAIG,GAG1CvI,QACE,OAAO,IAAIkI,EAAO7N,KAAK2F,MAAM9H,KAAKiQ,GAAI9N,KAAK2F,MAAM9H,KAAKkQ,IAGxDe,QACE,OAAO,IAAIjB,EAAO7N,KAAK8O,MAAMjR,KAAKiQ,GAAI9N,KAAK8O,MAAMjR,KAAKkQ,IAGxDgB,OACE,OAAO,IAAIlB,EAAO7N,KAAK+O,KAAKlR,KAAKiQ,GAAI9N,KAAK+O,KAAKlR,KAAKkQ,IAGtDiB,SACE,MAAO,CAAEnO,MAAOhD,KAAKiQ,EAAG7E,OAAQpL,KAAKkQ,IAM1BkB,MAFA,SAACnB,EAAGC,GAAC,OAAK,IAAIF,EAAOC,EAAGC,I,YC9DvC,MAIMmB,EAAiB,SAACC,GACtB,MAAMC,EAASD,EAAIxP,MAAM,IAAInB,KAAI,SAAC6Q,GAAI,OAAKA,EAAKC,WAAW,MAE3D,OAAQF,EAAO,IAAM,KAAOA,EAAO,IAAM,KAAOA,EAAO,IAAM,GAAKA,EAAO,IA4B9DlG,EAAsBH,eAAOwG,GACxC,IACE,MAAMC,QATazG,eAAOhE,GAC5B,GAAkB,cAAdA,EAAKwC,KAAsB,OAAO,KAEtC,MAAMkI,QAAgBpC,YAAkBtI,GACxC,OAAO2K,KAAKD,EAAQ9P,MAAM,KAAK,IAAIA,MAAM,QAAQ,GAKxBgQ,CAAeJ,GAChCK,EA5Be,SAACJ,GAGxB,MAAMK,EAAeL,EAAShI,QAAQ,QACtC,IAAsB,IAAlBqI,EAAqB,OAAO,KAKhC,MAAMC,EAAON,EAASO,UAAUF,EAAe,EAAGA,EAAe,EAAI,GACrE,OAnBkB,IAmBdC,EAAKR,WAAW,GAA2B,KAExCL,EAAOa,EAAKC,UAAU,EAAG,GAAID,EAAKC,UAAU,EAAG,IACnDvR,IAAI0Q,GACJP,IAxBmB,SAyBnBhJ,QAaqBqK,CAAiBR,GACvC,GAAII,EAActB,IArCE,OAqCqC,OAAO,KAKhE,MAAM2B,EAAeT,EAASO,UAAU,EAAG,IAAIvI,QAAQ,QACvD,IAAsB,IAAlByI,EAAqB,OAAO,KAKhC,MAAMC,EAAOV,EAASO,UAAUE,EAAe,EAAGA,EAAe,EAAI,GAErE,OAAOhB,EAAOiB,EAAKH,UAAU,EAAG,GAAIG,EAAKH,UAAU,EAAG,IACnDvR,IAAI0Q,GACJT,IApDiB,IAqDjBE,IAAI3O,KAAKC,IAAI2P,EAAc9B,EAAG8B,EAAc7B,IAC5CgB,OACAC,SACH,MAAO5I,GACP,OAAO,OAIJ,SAAS+J,EAAiBC,GAC/B,OAAOC,oBAAUD,K,6bC9DnB,MAOME,EAAoB,CAPD,KACO,IACL,IAEO,MAc5BC,EAAa,uEAEbC,EAAwB,gCAMxBC,EACJ,6FAEIC,EAAqB,eAE3B,IAAIC,GAAyB,EAE7B,SAASC,EAAalL,EAAMuC,GAC1B,OAAOvC,EAAKqK,UAAU9H,EAAS4I,eAAgB5I,EAAS6I,cAG1D,SAASC,EAAaC,EAAUC,GAC9B,MAAO,GAAGD,MAAaC,MAAaD,IAgHtC,SAASE,EAAqBxL,EAAMyL,GAClC,MAAMC,EAzBR,SAAkC1L,EAAMyL,GACtC,OAAOzL,EAAKqK,UAAU/P,KAAKC,IAAI,EAAGkR,EAASN,eAAiB,GAAIM,EAASN,gBAwB1DQ,CAAyB3L,EAAMyL,GACxCG,EAdR,SAAiC5L,EAAMyL,GACrC,OAAOzL,EAAKqK,UAAUoB,EAASL,aAAc9Q,KAAKuR,IAAIJ,EAASL,aAAe,EAAGpL,EAAKlG,SAaxEgS,CAAwB9L,EAAMyL,GAK5C,MAJuB,CACrB,IAAK,IACL,IAAK,KAEeC,KAAYE,EASpC,SAASG,EAAmBN,GAC1B,MAAMzL,EAAOyL,EAAS9H,OAChB,eAAEwH,EAAc,aAAEC,GAAiBK,EAEzC,IAAIO,EAAoC,OAAzBhM,EAAKmL,GAA2BA,EAAiB,EAAIA,EACpEa,EAAWhM,EAAKiM,YAAY,KAAMD,GAAY,EAE9C,IAAIE,EAASd,IAAiBD,EAAiBC,EAAeA,EAAe,EAC7Ec,EAASlM,EAAK8B,QAAQ,KAAMoK,GACxBA,EAAS,IAAGA,EAASlM,EAAKlG,QAK9B,MAAO,CACLqS,MAJoBnM,EAAKqK,UAAU2B,EAAUE,GACnBjS,MAAM,MAIhCkR,iBACAC,eACAY,WACAE,UAgBJ,SAASE,EACPX,EACAN,EACAC,EACAiB,EACAC,EACAC,GAEA,IAAIC,EAAWlS,KAAKC,IAAI8R,EAAWlB,EAAiBmB,GAChDG,EAASnS,KAAKC,IAAI8R,EAAWjB,EAAemB,GAE5CpB,IAAmBC,EACrBqB,EAASD,EACArB,IAAmBkB,IAC5BG,EAAWH,GAGbZ,EAAS5H,kBAAkB2I,EAAUC,GAgBvC,SAASC,EAAwBC,GAC/B,MAbO,CACLC,MAAO,CACL7T,KAHqC8T,EAcAF,EAAOG,gBAXnCC,gBACT/T,OAAQ6T,EAAIG,aAEdC,IAAK,CACHlU,IAAK8T,EAAIK,cACTlU,OAAQ6T,EAAIM,YARlB,IAA2CN,EA6KpC,SAASO,GAAmB,SACjC3B,EAAQ,KACRzL,EAAI,IACJqN,EAAG,aACHC,EAAY,SACZhC,EAAQ,SACRC,EAAW,GAAE,KACbvO,EAAI,OACJuQ,EAAM,OACNZ,IAKA,IAAKU,IAAQ/B,IAAaC,EACxB,OAGF,IAGIiC,EACAC,EACAC,EACAC,EANAC,GAAqB,EACrBC,GAAsB,EACtBC,GAAmB,EAOvB,GAFAvC,EAAWA,EAASwC,WAEhBpB,EAAQ,CACV,MAAMqB,EAAiBtB,EAAwBC,GAE/Ca,EAAuBQ,EAAepB,MACtCa,EAAqBO,EAAef,IAtaf,kBA2anBI,GACEY,YAAW1C,KACb8B,EAAM,iBACNE,EAAS,QAKkB,IAA3BhC,EAASzJ,QAAQ,QACnB+L,GAAsB,EACtBtC,EAAWA,EAASrE,QAAQ,MAAO,KAIjCuE,EACEA,EAASL,aAAeK,EAASN,eAAiBI,EAASrE,QAAQ,MAAO,IAAIpN,SAChF8T,GAAqB,EACrBrC,EAAWA,EAASrE,QAAQ,MAAO,KAE5ByF,GACLa,EAAqBzU,MAAQ0U,EAAmB1U,MAClD6U,GAAqB,EACrBrC,EAAWA,EAASrE,QAAQ,MAAO,KAIvC,MAAMgH,EAAgB3C,EAAStR,MAAM,MAEjC0S,IAAW3P,GACb0Q,EAAcf,EAAOwB,WAAWlU,MAAM,MAAMuT,EAAqBzU,KAE7D,QAAQuO,KAAKoG,KACfI,GAAmB,IAEZrC,IAAazO,IACtB0Q,EAAcjC,EAAS9H,MAAMyK,OAAO,EAAG3C,EAASN,gBAAgBc,YAAY,MAGxE,QAAQ3E,KAAKmE,EAAS9H,MAAM0G,UAAUqD,EAAajC,EAASN,mBAC9D2C,GAAmB,IAIvB,MAAMO,EACH5C,GAAwC,IAA5BA,EAASN,gBACrBwB,GAA0C,IAAhCa,EAAqBxU,QAA6C,IAA7BwU,EAAqBzU,IAEjEuV,EAAatR,GAAS8Q,GAAqBO,EAAqB,GAAP,MAGzD,6BACJE,EAA4B,kBAC5BC,EAAiB,wBACjBC,EAAuB,uBACvBC,GAtIJ,UAAmC,SAAEnD,EAAQ,IAAE8B,EAAG,cAAEa,IAClD,GA1VwB,OA0VpBb,EAA2B,CAC7B,MAAMsB,EAAoBT,EAAcU,OAAM,SAACC,GAAG,OAAK7D,EAAmB1D,KAAKuH,MAE/E,MAAO,CACLH,uBAAwB,kBACtBnD,EAASuD,MAAMvD,EAASwD,MAAM/D,GAAoB,GAAGlR,OAAQyR,EAASzR,SACxEyU,6BAA8BvD,EAAmB1D,KAAKiE,GACtDiD,kBAAmB,SAACQ,GAAI,OAAKA,EAAK9H,QAAQ8D,EAAoB,KAC9DyD,wBAAyB,kBAAME,IAInC,MAAO,CACLD,uBAAwB,kBAAMnD,EAASuD,MAAMzB,EAAIvT,OAAQyR,EAASzR,OAASuT,EAAIvT,SAC/EyU,6BACEhD,EAASzR,QAAuB,EAAbuT,EAAIvT,QACvByR,EAAS0D,WAAW5B,IACpB9B,EAAS2D,SAAS7B,IAClBzC,EAAkBrR,SAAS8T,GAC7BmB,kBAAmB,SAACQ,GAAI,OAAKA,EAAK9H,QAAQmG,EAAK,KAC/CoB,wBAAyB,SAACO,GAAI,OAA2B,IAAtBA,EAAKlN,QAAQuL,KAkH9C8B,CAA0B,CAAE5D,WAAU8B,MAAKa,kBAM3CP,EAFAO,EAAcpU,OAAS,KAAOkD,GAAqB,MAAZsO,GAAiC,KAAbA,GAC7C,MAAZA,GAAiC,KAAbA,EACPqB,EA5PrB,SAA4B3M,EAAMsL,EAAUC,EAAUoB,GACpD,MAAMR,EAAQnM,EAAK/F,MAAM,MACnB+T,EAAiBtB,EAAwBC,GAK/C,GAHER,EAAM6B,EAAepB,MAAM7T,IAAM,KAAOuS,GACxCa,EAAM6B,EAAef,IAAIlU,IAAM,KAAOuS,EAEjB,CACrB,GAAiB,OAAbA,EAAmB,CACrB,MAAM8D,EAAWjD,EAAM6B,EAAef,IAAIlU,IAAM,GAC1CsW,EAAqB,IAAIC,MAC7BnD,EAAM6B,EAAepB,MAAM7T,IAAM,GACjC,EACAiV,EAAef,IAAIlU,IAAM,EACzBqW,EAAStV,QAEX6S,EAAOG,eAAejJ,kBAAkBwL,GAE1C,OAAO9D,EAET,OAAOF,EAAaC,EAAUC,GAyOtBgE,CAAmBvP,EAAMsL,EAAUC,EAAUoB,GArOvD,SAAsB3M,EAAMyL,EAAUH,EAAUC,GAK9C,OA3NF,SAA6BvL,EAAMyL,GACjC,IAAIxR,EAAQ+F,EAAKqK,UAAU,EAAGoB,EAASN,gBAEvClR,EAAQA,EAAMA,MAAM,MAIpB,MAAMuV,EAAavV,EAAMA,EAAMH,OAAS,GAExC,YAAsBwH,IAAfkO,EAA2B,GAAKA,EA+MrCC,CAAoBzP,EAAMyL,KAAcH,GApM5C,SAA4BtL,EAAMyL,GAChC,IAAIxR,EAAQ+F,EAAKqK,UAAUoB,EAASL,cAMpC,OAHAnR,EAAQA,EAAMiN,QAAQ,MAAO,IAC7BjN,EAAQA,EAAMA,MAAM,MAEbA,EAAM,GA8LXyV,CAAmB1P,EAAMyL,KAAcH,GAIvB,MAAZA,IACFG,EAASN,eAAiBM,EAASN,gBAAkBG,EAASxR,OAAS,GACvE2R,EAASL,aAAeK,EAASL,cAAgBE,EAASxR,OAAS,IAE9DyR,GAEFF,EAAaC,EAAUC,GAyNtBoE,CAAa3P,EAAMyL,EAAUH,EAAUC,GAE5B2C,EACZpV,KAAI,SAACkW,GACJ,OAAI3B,EAAIvL,QAnBQ,WAmBoB,EAC3BuL,EAAInG,QApBG,SAoBsB8H,GAElCP,EAAwBO,GACnBR,EAAkBQ,GAEpBY,OAAOvC,GAAO2B,KAEtB7V,KAAK,MAEDkU,EAAIvL,QA7BS,WA6BmB,EAC1BuL,EAAInG,QA9BG,UA8BsB,kBAC1CqE,EAASrE,QAAQ,OAAQ,MAAMA,QAAQ,OAAQ,UAExCqH,EACMG,IAzBiB,GAAGJ,IAAYjB,IAAM9B,IAAWvO,EAAOqQ,EAAM,KA8B3EQ,IACFF,EAAe,KAAKA,GAGlBC,IACFD,GAAgB,MAGdhB,EACFA,EAAOkD,oBAAoBlC,EAAcJ,GAEzC/K,YAAWiJ,EAAUkC,GAzPzB,UAAoB,SAClBlC,EAAQ,IACR4B,EAAG,aACHC,EAAY,oBACZwC,EAAmB,mBACnBlC,EAAkB,OAClBL,EAAM,OACNZ,EAAM,qBACNa,EAAoB,mBACpBC,IAEA,IAAIsC,EACJ,IAAItE,GAAaA,EAAS5H,kBAA1B,CAGA,GAAI0J,GAAUA,EAAOzT,OAAS,EAAG,CAC/B,GAAI2R,EAAU,CAEZ,MAAMuE,EAAgBvE,EAASN,gBAAkBkC,EAAIvT,OAASuT,EAAIvL,QAAQyL,IACpE0C,EAAcD,EAAgBzC,EAAOzT,OAC3C,OAAO2R,EAAS5H,kBAAkBmM,EAAeC,GAEnD,GAAItD,EAEF,YADAA,EAAOuD,sBAAsB3C,EAAQF,GAIzC,GAAI5B,GACF,GAAIA,EAASN,iBAAmBM,EAASL,aAErC2E,EADED,EACIrE,EAASN,eAAiBkC,EAAIvT,OAE9B2R,EAASN,eAGbyC,IACFmC,GAAO,GAGLzC,IACFyC,GAAOzC,GAGF7B,EAAS5H,kBAAkBkM,EAAKA,QAEhCpD,GAAUa,EAAqBzU,MAAQ0U,EAAmB1U,KAC/D+W,GACFnD,EAAOwD,YAAyB,EAAd9C,EAAIvT,SA6M1BqW,CAAW,CACT1E,WACA4B,IAAKA,EAAInG,QAvDa,SAuDYqE,GAClC+B,eACAwC,oBAAqB9S,GAA4B,IAApBuO,EAASzR,OACtC8T,qBACAL,SACAZ,SACAa,uBACAC,uBAIG,SAAS2C,GAAW,SAAE3E,EAAQ,IAAE4B,EAAG,aAAEC,EAAY,SAAEhC,EAAQ,KAAEtO,EAAI,OAAEuQ,EAAM,WAAE8C,IAChF,MAAMC,EAAYjQ,IAAEoL,GACpBA,EAAW6E,EAAUzS,IAAI,GACzB,MAAMmC,EAAOsQ,EAAUzB,MACjBtD,EAAWL,EAAalL,EAAMyL,IAAa4E,EACjD5E,EAAS/M,QACT0O,EAAmB,CACjB3B,WACAzL,OACAqN,MACAC,eACAhC,WACAC,WACAvO,OACAuQ,WAEF9B,EAAS1I,QAsHX,SAASwN,EAAwB7P,EAAG+K,GAClC,OALF,SAA4B/K,GAC1B,QAAiB,UAAVA,EAAE8P,KAAoB9P,EAAE+P,QAAW/P,EAAEgQ,SAAYhQ,EAAEiQ,SAAYjQ,EAAEkQ,UAKtEC,CAAmBnQ,IACnB+K,EAASN,iBAAmBM,EAASL,eACpCH,EAWL,SAAS6F,EAAaC,EAAeC,GACnC,MAAM,OAAEC,EAAM,OAAEC,GAAWH,EAAcI,QAClC,CAAEC,EAAU,IAAMF,EAAOjX,MAAM,KACtC,MAAO,GAAGgX,IAASD,KAAOI,IAsD5B,SAASC,EAAkC5F,EAAUsF,EAAeO,GAClE,MAAM,OAAEL,GAAWF,EAAcI,QAC3B,MAAEhF,EAAK,SAAEH,EAAQ,OAAEE,GAlpB3B,SAA6BT,EAAU8F,GACrC,MAAM,aAAEnG,EAAY,MAAEzH,GAAU8H,EAE1B+F,EAAqB7N,EAAM7B,QAAQ,KAAMsJ,GAC/C,IAA4B,IAAxBoG,EACF,MAAO,CAAErF,MAAO,GAAIH,UAAW,EAAGE,QAAS,GAG7C,MAAMuF,EAAgB9N,EAAM0G,UAAUmH,GAEhCrF,EAAQ,GACRH,EAAWwF,EAAqB,EACtC,IAAIE,EAAa1F,EAEjB,IAAK,MAAMgD,KAAQyC,EAAcvK,QAAQ,MAAO,IAAIjN,MAAM,MAAO,CAC/D,IAAKsX,EAAUvC,GAAO,MAEtB7C,EAAM1R,KAAKuU,GACX0C,GAAc1C,EAAKlV,OAAS,EAG9B,MAAO,CAAEqS,QAAOH,WAAUE,OAAQwF,EAAa,GA6nBXC,CAAoBlG,GAAU,SAACzL,GAAS,IAAA4R,EAC1E,MAAMC,EAAY7R,EAAK+O,MAAMhE,IACrBkG,OAAQa,EAAYC,KAAMC,GAA8B,QAApBJ,EAAGC,aAAS,EAATA,EAAWV,cAAM,IAAAS,IAAI,GACpE,OAAOI,GAAYF,IAAeb,MArCtC,UAA6B,SAAExF,EAAQ,MAAEU,EAAK,SAAEH,EAAQ,OAAEE,EAAM,KAAEoF,IAChE,GAAqB,IAAjBnF,EAAMrS,OACR,OAGF,MAAMmY,EAAmB,IACnB,eAAE9G,EAAc,aAAEC,GAAiBK,EAEzC,IAAIyG,EAAiBZ,EACrBnF,EAAM3R,SAAQ,SAACwU,GACb,MAAMmD,EAAYnD,EAAKD,MAAMhE,GACvBqH,EAAcpD,EAAKF,MAAMqD,EAAUhB,OAAOD,OAAOpX,OAASqY,EAAUhB,OAAOF,OAAOnX,QAElFuY,EAAkBvB,EAAaqB,EAAWD,GAAkBE,EAClEH,EAAiBxX,KAAK4X,GACtBH,GAAkC,KAGpCzG,EAAS/M,QACT+M,EAAS5H,kBAAkBmI,EAAUE,GACrC,MAAMoG,EAAeL,EAAiB9Y,KAAK,MAC3CqJ,YAAWiJ,EAAU6G,GACrB7G,EAAS5H,kBAAkBsH,EAAgBC,GAkB3CmH,CAAoB,CAAE9G,WAAUU,QAAOH,WAAUE,SAAQoF,SAG3D,SAASkB,EAAmB9R,EAAG+K,GAC7B,IAAKlP,IAAIkW,yBAA0B,OAEnC,IAAKlC,EAAwB7P,EAAG+K,GAC9B,OAGF,MAAMiH,EAAgB3G,EAAmBN,GACnCkH,EAAoBD,EAAcvG,MAAM,GACxC4E,EAAgB4B,EAAkB5D,MAAMhE,GAE9C,GAAIgG,EAAe,CACjB,MAAM,OAAEG,EAAM,OAAED,EAAM,QAAE2B,EAAO,KAAEb,GAAShB,EAAcI,OAClD0B,GAAiBD,EACjBE,EAAe5B,EAAOpX,OAASmX,EAAOnX,OAE5C,GAAI4Y,EAAcvH,eAAiBuH,EAAc1G,SAAW8G,EAE1D,OAGF,GAAID,EAKF,OAFApH,EAASN,eAAiBM,EAASN,eAAiB4F,EAAc,GAAGjX,YACrEuX,EAAkC5F,EAAUsF,EAAe,GAI7D,IAAIgC,EAGJ,GAAIhB,EAAM,CACR,MAAMiB,EAzFZ,UAAsB7B,QAAQ,OAAED,KAC9B,OAAO+B,SAAS/B,EAAQ,IAwFJgC,CAAWnC,GAAiB,EAC5CgC,EAAejC,EAAaC,EAAeiC,GAC3C3B,EAAkC5F,EAAUsF,EAAeiC,EAAU,OAChE,CACL,GAAIL,EAAkB5D,MAAMlE,GAAa,OAEzCkI,EAAe,GAAG9B,IAASC,IAG7B6B,EAAeA,EAAa7L,QAAQ,YAAa,OAEjDxG,EAAEC,iBAEFyP,EAAW,CACT/C,IAAK0F,EACLtH,WACAH,SAAU,GACVtO,MAAM,EACNuQ,OAAQ,GACR8C,WAAY,MA0CX,SAAS8C,EAAiBzS,GAAG,IAAA0S,EAAAC,EAClC,MAAM5H,EAAWtT,KAEI,QAArBib,GAAIC,EAAAhT,IAAEoL,IAAU6H,aAAK,IAAAF,GAAjBA,EAAAG,KAAAF,EAAoB,iBAExBb,EAAmB9R,EAAG+K,GAGjB/K,EAAE8S,sBA7CT,SAAoC9S,EAAG+K,GAAU,IAAAgI,EAC/C,GAAiB,QAAbA,EAAClX,IAAImX,gBAAQ,IAAAD,IAAZA,EAAcE,qBAAsB,OAEzC,IAAKpD,EAAwB7P,EAAG+K,GAC9B,OAGF,MAAMiH,EAAgB3G,EAAmBN,GAEnC0G,EADoBO,EAAcvG,MAAM,GACV4C,MAAMjE,GAE1C,IAAKqH,EAAW,OAEhB,MAAM,OAAElB,EAAM,QAAE2B,GAAYT,EAAUhB,OAEpCuB,EAAcvH,eAAiBuH,EAAc1G,SAAWiF,EAAOnX,SAK5D8Y,GAKLlS,EAAEC,iBAEFyP,EAAW,CACT/C,IAAK4D,EACLxF,WACAH,SAAU,GACVtO,MAAM,EACNuQ,OAAQ,GACR8C,WAAY,MAZZ5E,EAASN,gBAAkBgH,EAAU,GAAGrY,QAyBxC8Z,CAA2BlT,EAAG+K,GAhOlC,SAAoC/K,EAAG+K,GACrC,IAAKlP,IAAIsX,4BAA6B,OACtC,GAAInT,EAAEiQ,SAAWjQ,EAAEgQ,QAAS,OAC5B,GAAIjF,EAASN,iBAAmBM,EAASL,aAAc,OAEvD,MAWMiC,EAXO,CACX,IAAK,aACLyG,EAAG,WACH,IAAK,WACL,IAAK,WACL,IAAK,WACL,IAAK,WACL,IAAK,WACL,IAAK,WACL,IAAK,YAEUpT,EAAE8P,KAEfnD,IACF3M,EAAEC,iBAEFyP,EAAW,CACT/C,MACA5B,WACAH,SAAU,GACVtO,MAAM,EACNuQ,OAAQ,GACR8C,WAAY,MAuMhB0D,CAA2BrT,EAAG+K,IAGzB,SAASuI,IACd/I,GAAyB,EAGpB,SAASgJ,IACdhJ,GAAyB,EA0BpB,SAASiJ,EAAyBxT,IAvBzC,SAAgCA,GAC9B,MAAM+K,EAAW/K,EAAEO,OACnB,GAAIwK,EAASN,iBAAmBM,EAASL,aAAc,OACvD,MAAMpL,EAAOyL,EAAS9H,MAGtB,GAAI6H,EAAqBxL,EAAMyL,GAAW,OAC1C,IAAK/K,EAAExI,cAAe,OAEtB,IAAIic,EAAazT,EAAExI,cAAcuB,QAAQ,QACzC,GAAK0a,IACLA,EAAaA,EAAWna,OACpBiU,YAAWkG,IAAa,CAC1BzT,EAAEC,iBACFD,EAAE0T,2BACF,MAEM9B,EAAe,IAFJpH,EAAalL,EAAMyL,OAEE0I,KACtC3R,YAAWiJ,EAAU6G,IASvB+B,CADc3T,WAAGgB,cAAgBhB,EAAEgB,cAAgBhB,GAI9C,SAAS4T,EAAwBC,GACtC,MAAMjE,EAAYiE,EAAYrX,QAAQ,YAAYtB,KAAK,YACvD,GAAK0U,EAAUxW,OAEf,OAAQya,EAAYnc,KAAK,cACvB,IAAK,eAxVT,SAAqBkY,GACnB,MAAM7E,EAAW6E,EAAUzS,IAAI,IACzB,MAAEsO,EAAK,eAAEhB,EAAc,aAAEC,EAAY,SAAEY,EAAQ,OAAEE,GAAWH,EAAmBN,GAC/E+I,EAAe,GACrB,IAAIC,EAAa,EAEjBhJ,EAAS/M,QACT+M,EAAS5H,kBAAkBmI,EAAUE,GAErCC,EAAM3R,SAAQ,SAACwU,GACbA,EApjBgB,IAojBG0F,OAnjBD,GAmjByB1F,EAC3CyF,GApjBkB,EAsjBlBD,EAAa/Z,KAAKuU,MAGpB,MAAMsD,EAAekC,EAAarb,KAAK,MAEvCqJ,YAAWiJ,EAAU6G,GACrBlG,EAAqBX,EAAUN,EAAgBC,EAAcY,EA5jBzC,EA4jBkEyI,GAsUlFE,CAAYrE,GACZ,MACF,IAAK,gBAhUT,SAAsBA,GACpB,MAAM7E,EAAW6E,EAAUzS,IAAI,IACzB,MAAEsO,EAAK,eAAEhB,EAAc,aAAEC,EAAY,SAAEY,EAAQ,OAAEE,GAAWH,EAAmBN,GAC/E+I,EAAe,GACrB,IAAII,EAAe,EACfC,GAAwB,EACxBC,EAAkB,EAEtBrJ,EAAS/M,QACT+M,EAAS5H,kBAAkBmI,EAAUE,GAErCC,EAAM3R,SAAQ,SAACwU,GAGb,GAFA8F,EAAkB,EAEd9F,EAAKlV,OAAS,EAAG,CAEnB,KAAOgb,EAplBS,GADF,MAqlB4B9F,EAAK8F,IAC7CA,GAAmB,EAGjBA,EAAkB,IACpB9F,EAAOA,EAAKF,MAAMgG,GAClBF,GAAgBE,IAIU,IAA1BD,IAA6BA,EAAuBC,GACxDN,EAAa/Z,KAAKuU,MAGpB,MAAMsD,EAAekC,EAAarb,KAAK,MAEnCyb,EAAe,GAAGpS,YAAWiJ,EAAU6G,GAE3ClG,EACEX,EACAN,EACAC,EACAY,GACC6I,GACAD,GAyRCG,CAAazE,GACb,MACF,QACE,OAAOF,EAAW,CAChB3E,SAAU6E,EACVjD,IAAKkH,EAAYnc,KAAK,SACtBkV,aAAciH,EAAYnc,KAAK,kBAC/BkT,SAAUiJ,EAAYnc,KAAK,WAC3B4E,MAAOuX,EAAYnc,KAAK,aACxBmV,OAAQgH,EAAYnc,KAAK,YACzBiY,WAAYkE,EAAYS,KAAK,0BAK9B,SAASlP,EAAqBzK,GACnCgF,IAAE,iBAAkBhF,GACjBkC,GAAG,UAAW4V,GACd5V,GAAG,mBAAoByW,GACvBzW,GAAG,iBAAkB0W,GACrB1W,GAAG,QAAS2W,GACZe,MAAK,WACJC,UAAUC,4BAA4B9U,IAAElI,MAAOmc,MAUnD,OAPwBjU,IAAEhF,GACvB+K,IAAI,QAAS,UACb7I,GAAG,QAAS,UAAU,WAErB,OAAO+W,EADajU,IAAElI,UAOrB,SAASid,EAA2BzI,GAEzCtM,IAAE,UACC+F,IAAI,SACJ7I,GAAG,SAAS,SAACmD,GACZ,MAAM,MAAE2U,EAAK,QAAEC,EAAO,UAAEC,EAAS,SAAEC,GAAanV,IAAEK,EAAE+U,eAAerd,OAEnEgV,EAAmB,CACjBC,IAAKgI,EACL/J,SAAUgK,EACVtY,MAAOuY,EACPhI,OAAQiI,EACRjK,SAAUoB,EAAO+I,kBACjB1V,KAAM2M,EAAOwB,WACbxB,WAEFA,EAAOjO,WAIN,SAASgI,EAAwBrL,GAWtC,OAVAgF,IAAE,iBAAkBhF,GACjB+K,IAAI,UAAW+M,GACf/M,IAAI,mBAAoB4N,GACxB5N,IAAI,iBAAkB6N,GACtB7N,IAAI,QAAS8N,GACbe,MAAK,WACJC,UAAUS,8BAA8BtV,IAAElI,UAIvCkI,IAAE,SAAUhF,GAAM+K,IAAI,SAYxB,MAAMwP,EAAuBvS,eAAOoI,EAAUoK,EAAsB,IACzE,MAAM,MAAE1J,EAAK,SAAEH,GAAaD,EAAmBN,GAG/C,GAAIU,EAAQ,EACV,OAAO,KAGT,MAAM2J,EAAe3J,EAAM,GAE3B,IAAK,kBAAkB7E,KAAKwO,GAAe,OAAO,KAElD,MAAMC,EAAqBtK,EAASN,eAAiBa,EAC/CgK,EAAUF,EAAazL,UAAU,EAAG0L,GAAoB9J,YAAY,KACpEgK,EAAYH,EAAazL,UAAU0L,GAAoBjU,QAAQ,KAErE,GAAIkU,GAAW,GAAKC,GAAa,EAAG,CAClC,MAAMC,EAAgBJ,EAAazL,UAAU2L,EAASD,EAAqBE,EAAY,IACjF,KAAE7d,SAAe8K,IAAMC,KAAK0S,EAAqB,CAAE7V,KAAMkW,IAIzDC,GAHS,IAAIxc,WAEAC,gBAAgBxB,EAAKge,KAAM,aACzBA,KAAKtY,cAAc,KAAKuY,aAAa,QAE1D,GAAIF,EAAU,CACZ,MAAMnU,EAAWmU,EAAS9L,UAAU8L,EAASlK,YAAY,KAAO,GAEhE,MAAO,CACLiK,gBACAC,WACAnU,aAKN,OAAO,MAGIsU,EAAsB,SAAC1D,GAAY,IAAA2D,EAC9C,MAAMC,EACJlc,KAAKC,IAAI,GAAyB,QAAtBgc,EAAA3D,EAAQ7D,MAAM,gBAAQ,IAAAwH,GAAwC,QAAxCA,EAAtBA,EAAwBE,MAAK,SAACC,EAAGC,GAAC,OAAKA,EAAE7c,OAAS4c,EAAE5c,UAAQ,UAAE,IAAAyc,OAAA,EAA9DA,EAAgEzc,SAAU,GAAK,EAC7F,MAAO,IAAI4a,OAAO8B,K,wtBC7+Bb,MAAMI,EAA2B,2BAaxC,SAASC,EAA4BC,GACnC,MAAMC,EAAkBD,EAAU5Z,QAAQ,YAAYtB,KAAK,UACrD9C,EAAM,IAAIke,IAWhB,OATAD,EAAgB9B,MAAK,WACnB,MAAMV,EAAclU,IAAElI,MAChB8e,EAAoB1C,EAAYnc,KAAK,gBAEvC6e,WAAmBnd,QACrBhB,EAAIoe,IAAI3C,EAAa0C,MAIlBne,EAGM,MAAMoc,EACnBjd,cAAc,IAAAkf,EAMZhf,KAAKif,WAAa,IAAIJ,IACtB7e,KAAKkf,aAAelf,KAAKkf,aAAa/Q,KAAKnO,MAC3CA,KAAKmf,iBAAmB,KACxBnf,KAAKof,qBAAuB,KAE5Bpf,KAAKqf,OAAO,CACV,CAACC,KAAkCtf,KAAKkf,cACxC,CAACK,KAA2BxC,EAAUyC,iBACtC,CAACC,KAAc1C,EAAU2C,aACzB,CAACC,IAAkB3f,KAAK4f,YAAYzR,KAAKnO,OACzC,CAAC6f,KAAwB9C,EAAU+C,iBACnC,CAACC,IAAwBhD,EAAUiD,sBACnC,CAACC,KAAelD,EAAUmD,gBAE1B,CAACC,IAAsB,kBAAMC,YAAkB,sBAC/C,CAACC,IAAqB,kBAAMD,YAAkB,mCAC9C,CAACE,IAAmB,kBAAMF,YAAkB,iCAC5C,CACEG,IACA,kBACEH,YACE,+EAGN,CACEI,IACA,kBACEJ,YACE,gFAGN,CAACK,IAAqB,kBAAML,YAAkB,mCAC9C,CAACM,IAAmB,kBAAMN,YAAkB,iCAC5C,CAACO,IAAsB,kBAAMP,YAAkB,qCAC/C,CAACQ,IAAqB,kBAAMR,YAAkB,mCAE9C,CAACS,KAAyB9D,EAAU+D,yBAGtCC,aAAgB,SAACxY,EAAGyY,EAASC,GAAK,OAChCC,aAAQL,MAAyBzf,SAAS6f,SAAiB9X,KAGtD,QAAP6V,EAAI5a,WAAG,IAAA4a,GAAU,QAAVA,EAAHA,EAAKzD,gBAAQ,IAAAyD,GAAbA,EAAemC,iBACjBnhB,KAAK+Q,IAAIqQ,IAAkBrE,EAAUsE,yBAErCN,aAAgB,SAACxY,EAAGyY,EAASC,GAAK,OAChCC,aAAQE,KAAkBhgB,SAAS6f,SAAiB9X,MAIxDjB,IAAE0G,UAAUxJ,GAAG,QAAS,8BAA+BpF,KAAKkf,cAExDoC,eACFC,cAGFvhB,KAAKwhB,gBAAkB,CACrB,qBACA,iCACA,oBAmCJC,aAAaC,EAAWC,EAAO,GAAIC,EAA6B,IAAIC,KAClED,EAA2B7Q,IAAI2Q,GAE/B,IAAII,EAAW9hB,KAAKif,WAAWvZ,IAAIgc,GACnC,IAAKI,EAAU,CACb,IAAK,MAAMC,KAA6B,QAA1BC,EAAIN,EAAUO,oBAAY,IAAAD,IAAI,GAAI,KAAAA,EAC1CJ,EAA2BM,IAAIH,IAAQA,IAAQhF,IAMnD6E,EAA2B7Q,IAAIgR,GAE/B/hB,KAAKyhB,aAAaM,EAAK,GAAIH,IAG7BE,EAAW,IAAIJ,EAAU1hB,QAAS2hB,GAClC3hB,KAAKif,WAAWF,IAAI2C,EAAWI,GAIjC,OADAF,EAA2BO,OAAOT,GAC3BI,EAaT/Q,IAAIqR,EAASC,GACXC,IAAUnU,KAAK+S,aAAQkB,GAAUC,GAWnChD,OAAOkD,GAAsB,IAAA/hB,EAAA,KAC3B+hB,EAAqBlgB,SAAQ,SAACmgB,GAAkB,OAAKhiB,EAAKuQ,OAAOyR,MAGnEtD,aAAa3W,GAAG,IAAAhG,EAAA,KACVgG,WAAGC,gBACLD,EAAEC,iBAGAxI,KAAKmf,kBAAoBnf,KAAKof,sBAChCpf,KAAKof,qBAAqBqD,WAC1BziB,KAAKmf,iBAAiBhX,SACtBnI,KAAKmf,iBAAmB,KACxBnf,KAAKof,qBAAuB,OAE5Bpf,KAAKmf,iBAAmBvQ,SAAS8T,cAAc,OAC/C9T,SAASqP,KAAK9Y,OAAOnF,KAAKmf,kBAE1Bnf,KAAKof,qBAAuB,IAAIuD,UAAI,CAClCC,GAAI5iB,KAAKmf,iBACT0D,WAAY,CACVC,cAAe,kBAAM,iFAEvBC,OAAQ,SAACL,GACP,OAAOA,EAAc,iBAAkB,CACrCtd,GAAI,CACF4d,OAAQzgB,EAAK2c,oBAQzB,uBAAuB3W,GACrBA,EAAEC,iBAEEya,YAAaC,YADgB,qBAE/BC,YAF+B,mBAEK,QAAS,CAAE3Y,KAAM,MAErD2Y,YAJ+B,mBAIK,OAAQ,CAAE3Y,KAAM,MAEtD4Y,cAGF,sBAAsB7a,GACpBA,EAAEC,iBACF,MACM6a,EAAeJ,YAAaC,YADT,kBAEzBC,YAFyB,kBAEKE,GAAczN,WAAY,CAAE0N,QAAS,IAAK9Y,KAAM,MAC9E4Y,cAGF,6BAA6B7a,GAC3BL,IAAE0G,UAAU2U,eAAe,0BAA2B,CAAChb,IAGzD,+BAA+BA,GAC7BL,IAAE0G,UAAU2U,eAAe,mCAAoC,CAAChb,IAGlEqX,YAAYrX,GACV,MAAMib,EAAW5U,SAASlN,iBAAiB1B,KAAKwhB,gBAAgBxgB,KAAK,MAC/DyiB,EAAiB3gB,MAAMqW,KAAKqK,GAAU/f,MAAK,SAACmf,GAAE,OAAKA,EAAGc,gBAE5DD,WAAgBld,QAChBgC,EAAEC,iBAGJ,mBAAmBD,GAAG,IAAAob,EAC2B,QAA/CA,EAAA/U,SAASjJ,cAAc,gCAAwB,IAAAge,GAA/CA,EAAiD/Y,QACjDgZ,IAAeC,WAAW,uDAEtBtb,EAAEC,gBACJD,EAAEC,iBAIN,6BAA6BD,GAAG,IAAAub,EAAAC,EAC1Bxb,WAAG8P,KACLuL,IAAeC,WAAWG,KAE5Bzb,WAAGC,iBAC4C,QAA/Csb,EAAAlV,SAASjJ,cAAc,gCAAwB,IAAAme,GAA/CA,EAAiDlZ,QAEjD,MAAMqZ,QAAoBC,YAAe,uCACzC,IAAKD,EAAa,OAElB,MAAME,EAA4D,QAAjDJ,EAAGnV,SAASjJ,cAAc,+BAAuB,IAAAoe,OAAA,EAA9CA,EAAgDK,QAAQD,YAE5EF,EAAYzY,MAAQ,KAAI2Y,EAAiBA,EAAH,IAAoB,IAC1DF,EAAY3Z,cAAc,IAAIC,MAAM,UAGtC,4BAA4BhC,GACTqG,SAASlN,iBAAiB,sBAElCW,SAAQ,SAAC2e,GAChBA,EAAQqD,MAAMC,QAAU,UAGtB/b,EAAEC,gBACJD,EAAEC,iBAYN,mCAAmCmW,EAAW4F,GAC5C,MAAMC,EAA2B9F,EAA4BC,GAEvD8F,EAAiB,IAAInC,IAAU3D,EAAU,IAI/CA,EAAU1e,KAAKwe,EAA0BgG,GAEzCD,EAAyBniB,SAAQ,SAACyc,EAAmB1C,GACnDqI,EAAetW,KAAK2Q,GAAmB,SAACvW,GACtCA,EAAEC,iBAEF+b,EAAQnI,SAKZ,MAAMsI,EAAeC,IAAQ,IAAIH,EAAyBI,WAEpDC,EAAuBvC,IAAUwC,UAAUC,aACjDN,EAAeM,aAAe,SAAyBxc,EAAGyY,EAASC,GACjE,OAAIyD,EAAatjB,SAAS6f,IAInB4D,EAAqBzJ,KAAKpb,KAAMuI,EAAGyY,EAASC,IAcvD,qCAAqCtC,GACnC,MAAM8F,EAAiB9F,EAAU1e,KAAKwe,GAElCgG,GACF/F,EAA4BC,GAAWtc,SAAQ,SAACyc,GAC9C2F,EAAeO,OAAOlG","file":"6.f041ad0f.chunk.js","sourcesContent":["const maxColumnWidth = (rows, columnIndex) =>\n Math.max(...rows.map((row) => row[columnIndex].length));\n\nexport default class PasteMarkdownTable {\n constructor(clipboardData) {\n this.data = clipboardData;\n this.columnWidths = [];\n this.rows = [];\n this.tableFound = this.parseTable();\n }\n\n isTable() {\n return this.tableFound;\n }\n\n convertToTableMarkdown() {\n this.calculateColumnWidths();\n\n const markdownRows = this.rows.map(\n (row) =>\n // | Name | Title | Email Address |\n // |--------------|-------|----------------|\n // | Jane Atler | CEO | jane@acme.com |\n // | John Doherty | CTO | john@acme.com |\n // | Sally Smith | CFO | sally@acme.com |\n `| ${row.map((column, index) => this.formatColumn(column, index)).join(' | ')} |`,\n );\n\n // Insert a header break (e.g. -----) to the second row\n markdownRows.splice(1, 0, this.generateHeaderBreak());\n\n return markdownRows.join('\\n');\n }\n\n // Private methods below\n\n // To determine whether the cut data is a table, the following criteria\n // must be satisfied with the clipboard data:\n //\n // 1. MIME types \"text/plain\" and \"text/html\" exist\n // 2. The \"text/html\" data must have a single element\n // 3. The number of rows in the \"text/plain\" data matches that of the \"text/html\" data\n // 4. The max number of columns in \"text/plain\" matches that of the \"text/html\" data\n parseTable() {\n if (!this.data.types.includes('text/html') || !this.data.types.includes('text/plain')) {\n return false;\n }\n\n const htmlData = this.data.getData('text/html');\n this.doc = new DOMParser().parseFromString(htmlData, 'text/html');\n // Avoid formatting lines that were copied from a diff\n const tables = this.doc.querySelectorAll('table:not(.diff-wrap-lines)');\n\n // We're only looking for exactly one table. If there happens to be\n // multiple tables, it's possible an application copied data into\n // the clipboard that is not related to a simple table. It may also be\n // complicated converting multiple tables into Markdown.\n if (tables.length !== 1) {\n return false;\n }\n\n const text = this.data.getData('text/plain').trim();\n const splitRows = text.split(/[\\n\\u0085\\u2028\\u2029]|\\r\\n?/g);\n\n // Now check that the number of rows matches between HTML and text\n if (this.doc.querySelectorAll('tr').length !== splitRows.length) {\n return false;\n }\n\n this.rows = splitRows.map((row) => row.split('\\t'));\n this.normalizeRows();\n\n // Check that the max number of columns in the HTML matches the number of\n // columns in the text. GitHub, for example, copies a line number and the\n // line itself into the HTML data.\n if (!this.columnCountsMatch()) {\n return false;\n }\n\n return true;\n }\n\n // Ensure each row has the same number of columns\n normalizeRows() {\n const rowLengths = this.rows.map((row) => row.length);\n const maxLength = Math.max(...rowLengths);\n\n this.rows.forEach((row) => {\n while (row.length < maxLength) {\n row.push('');\n }\n });\n }\n\n calculateColumnWidths() {\n this.columnWidths = this.rows[0].map((_column, columnIndex) =>\n maxColumnWidth(this.rows, columnIndex),\n );\n }\n\n columnCountsMatch() {\n const textColumnCount = this.rows[0].length;\n let htmlColumnCount = 0;\n\n this.doc.querySelectorAll('table tr').forEach((row) => {\n htmlColumnCount = Math.max(row.cells.length, htmlColumnCount);\n });\n\n return textColumnCount === htmlColumnCount;\n }\n\n formatColumn(column, index) {\n const spaces = Array(this.columnWidths[index] - column.length + 1).join(' ');\n return column + spaces;\n }\n\n generateHeaderBreak() {\n // Add 3 dashes to line things up: there is additional spacing for the pipe characters\n const dashes = this.columnWidths.map((width, index) =>\n Array(this.columnWidths[index] + 3).join('-'),\n );\n return `|${dashes.join('|')}|`;\n }\n}\n","import Dropzone from 'dropzone';\nimport $ from 'jquery';\nimport { escape } from 'lodash';\nimport './behaviors/preview_markdown';\nimport { spriteIcon, insertText } from '~/lib/utils/common_utils';\nimport { getFilename } from '~/lib/utils/file_upload';\nimport { truncate } from '~/lib/utils/text_utility';\nimport { n__, __ } from '~/locale';\nimport { getRetinaDimensions } from '~/lib/utils/image_utils';\nimport PasteMarkdownTable from './behaviors/markdown/paste_markdown_table';\nimport axios from './lib/utils/axios_utils';\nimport csrf from './lib/utils/csrf';\n\nDropzone.autoDiscover = false;\n\n/**\n * Return the error message string from the given response.\n *\n * @param {String|Object} res\n */\nfunction getErrorMessage(res) {\n if (!res || typeof res === 'string') {\n return res;\n }\n\n return res.message;\n}\n\nexport default function dropzoneInput(form, config = { parallelUploads: 2 }) {\n const divHover = '
';\n const iconPaperclip = spriteIcon('paperclip', 'div-dropzone-icon s24');\n const $attachingFileMessage = form.find('.attaching-file-message');\n const $cancelButton = form.find('.button-cancel-uploading-files');\n const $retryLink = form.find('.retry-uploading-link');\n const $uploadProgress = form.find('.uploading-progress');\n const $uploadingErrorContainer = form.find('.uploading-error-container');\n const $uploadingErrorMessage = form.find('.uploading-error-message');\n const $uploadingProgressContainer = form.find('.uploading-progress-container');\n const uploadsPath = form.data('uploads-path') || window.uploads_path || null;\n const maxFileSize = gon.max_file_size || 10;\n const formTextarea = form.find('.js-gfm-input');\n let handlePaste;\n let pasteText;\n let addFileToForm;\n let updateAttachingMessage;\n let uploadFile;\n let hasPlainText;\n\n formTextarea.wrap('
');\n\n // Add dropzone area to the form.\n const $mdArea = formTextarea.closest('.md-area');\n const $formDropzone = form.find('.div-dropzone');\n $formDropzone.parent().addClass('div-dropzone-wrapper');\n $formDropzone.append(divHover);\n $formDropzone.find('.div-dropzone-hover').append(iconPaperclip);\n\n if (!uploadsPath) {\n $formDropzone.addClass('js-invalid-dropzone');\n return null;\n }\n\n formTextarea.on('paste', (event) => handlePaste(event));\n\n const dropzone = $formDropzone.dropzone({\n url: uploadsPath,\n dictDefaultMessage: '',\n clickable: form.get(0).querySelector('[data-button-type=\"attach-file\"]') ?? true,\n paramName: 'file',\n maxFilesize: maxFileSize,\n uploadMultiple: false,\n headers: csrf.headers,\n previewContainer: false,\n ...config,\n dragover: () => {\n $mdArea.addClass('is-dropzone-hover');\n form.find('.div-dropzone-hover').css('opacity', 0.7);\n },\n dragleave: () => {\n $mdArea.removeClass('is-dropzone-hover');\n form.find('.div-dropzone-hover').css('opacity', 0);\n },\n drop: () => {\n $mdArea.removeClass('is-dropzone-hover');\n form.find('.div-dropzone-hover').css('opacity', 0);\n formTextarea.focus();\n },\n success(header, response) {\n const processingFileCount = this.getQueuedFiles().length + this.getUploadingFiles().length;\n const shouldPad = processingFileCount >= 1;\n\n addFileToForm(response.link.url, header.size);\n pasteText(response.link.markdown, shouldPad);\n },\n error: (file, errorMessage = __('Attaching the file failed.'), xhr) => {\n // If 'error' event is fired by dropzone, the second parameter is error message.\n // If the 'errorMessage' parameter is empty, the default error message is set.\n // If the 'error' event is fired by backend (xhr) error response, the third parameter is\n // xhr object (xhr.responseText is error message).\n // On error we hide the 'Attach' and 'Cancel' buttons\n // and show an error.\n const message = getErrorMessage(errorMessage || xhr.responseText);\n\n $uploadingErrorContainer.removeClass('hide');\n $uploadingErrorMessage.html(message);\n $cancelButton.addClass('hide');\n },\n totaluploadprogress(totalUploadProgress) {\n updateAttachingMessage(this.files, $attachingFileMessage);\n $uploadProgress.text(`${Math.round(totalUploadProgress)}%`);\n },\n sending: () => {\n // DOM elements already exist.\n // Instead of dynamically generating them,\n // we just either hide or show them.\n $uploadingErrorContainer.addClass('hide');\n $uploadingProgressContainer.removeClass('hide');\n $cancelButton.removeClass('hide');\n },\n removedfile: () => {\n $cancelButton.addClass('hide');\n $uploadingProgressContainer.addClass('hide');\n $uploadingErrorContainer.addClass('hide');\n },\n queuecomplete: () => {\n $('.dz-preview').remove();\n $('.markdown-area').trigger('input');\n\n $uploadingProgressContainer.addClass('hide');\n $cancelButton.addClass('hide');\n },\n });\n\n const child = $(dropzone[0]).children('textarea');\n\n // removeAllFiles(true) stops uploading files (if any)\n // and remove them from dropzone files queue.\n $cancelButton.on('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n Dropzone.forElement($formDropzone.get(0)).removeAllFiles(true);\n });\n\n // If 'error' event is fired, we store a failed files,\n // clear dropzone files queue, change status of failed files to undefined,\n // and add that files to the dropzone files queue again.\n // addFile() adds file to dropzone files queue and upload it.\n $retryLink.on('click', (e) => {\n const dropzoneInstance = Dropzone.forElement(\n e.target.closest('.js-main-target-form').querySelector('.div-dropzone'),\n );\n const failedFiles = dropzoneInstance.files;\n\n e.preventDefault();\n\n // 'true' parameter of removeAllFiles() cancels\n // uploading of files that are being uploaded at the moment.\n dropzoneInstance.removeAllFiles(true);\n\n failedFiles.map((failedFile) => {\n const file = failedFile;\n\n if (file.status === Dropzone.ERROR) {\n file.status = undefined;\n file.accepted = undefined;\n }\n\n return dropzoneInstance.addFile(file);\n });\n });\n\n handlePaste = (event) => {\n const pasteEvent = event.originalEvent;\n const { clipboardData } = pasteEvent;\n if (clipboardData && clipboardData.items) {\n const converter = new PasteMarkdownTable(clipboardData);\n // Apple Numbers copies a table as an image, HTML, and text, so\n // we need to check for the presence of a table first.\n if (converter.isTable()) {\n event.preventDefault();\n const text = converter.convertToTableMarkdown();\n pasteText(text);\n } else if (!hasPlainText(pasteEvent)) {\n const fileList = [...clipboardData.files];\n fileList.forEach((file) => {\n if (file.type.indexOf('image') !== -1) {\n event.preventDefault();\n const MAX_FILE_NAME_LENGTH = 246;\n\n const filename = getFilename(file) || 'image.png';\n const truncateFilename = truncate(filename, MAX_FILE_NAME_LENGTH);\n const text = `{{${truncateFilename}}}`;\n pasteText(text);\n\n uploadFile(file, truncateFilename);\n }\n });\n }\n }\n };\n\n hasPlainText = (data) => {\n const clipboardDataList = [...data.clipboardData.items];\n return clipboardDataList.some((item) => item.type === 'text/plain');\n };\n\n pasteText = (text, shouldPad) => {\n let formattedText = text;\n if (shouldPad) {\n formattedText += '\\n\\n';\n }\n const textarea = child.get(0);\n insertText(textarea, formattedText);\n formTextarea.get(0).dispatchEvent(new Event('input'));\n formTextarea.trigger('input');\n };\n\n addFileToForm = (path) => {\n $(form).append(``);\n };\n\n const showSpinner = () => $uploadingProgressContainer.removeClass('hide');\n\n const closeSpinner = () => $uploadingProgressContainer.addClass('hide');\n\n const showError = (message) => {\n $uploadingErrorContainer.removeClass('hide');\n $uploadingErrorMessage.html(message);\n };\n\n const insertToTextArea = (filename, url) => {\n const $child = $(child);\n const textarea = $child.get(0);\n const formattedText = `{{${filename}}}`;\n\n const replaceStart = textarea.value.indexOf(formattedText);\n if (replaceStart !== -1) {\n const replaceEnd = replaceStart + formattedText.length;\n textarea.setSelectionRange(replaceStart, replaceEnd);\n }\n insertText(textarea, url);\n $child.trigger('change');\n };\n\n uploadFile = (item, filename) => {\n const formData = new FormData();\n formData.append('file', item, filename);\n\n showSpinner();\n\n axios\n .post(uploadsPath, formData)\n .then(async ({ data }) => {\n let md = data.link.markdown;\n\n const { width, height } = (await getRetinaDimensions(item)) || {};\n if (width && height) {\n // eslint-disable-next-line @gitlab/require-i18n-strings\n md += `{width=${width} height=${height}}`;\n }\n insertToTextArea(filename, md);\n closeSpinner();\n })\n .catch((e) => {\n showError(e.response.data.message);\n closeSpinner();\n });\n };\n\n updateAttachingMessage = (files, messageContainer) => {\n const filesCount = files.filter(\n (file) => file.status === 'uploading' || file.status === 'queued',\n ).length;\n const attachingMessage = n__('Attaching a file', 'Attaching %d files', filesCount);\n\n messageContainer.text(`${attachingMessage} -`);\n };\n\n function handleAttachFile(e) {\n e.preventDefault();\n $(this).closest('.gfm-form').find('.div-dropzone').click();\n formTextarea.focus();\n }\n\n form.find('.markdown-selector').click(handleAttachFile);\n\n const $attachFileButton = form.find('.js-attach-file-button');\n if ($attachFileButton.length) {\n $attachFileButton.get(0).addEventListener('click', handleAttachFile);\n }\n\n return $formDropzone.get(0) ? Dropzone.forElement($formDropzone.get(0)) : null;\n}\n","import autosize from 'autosize';\nimport $ from 'jquery';\nimport { isEmpty } from 'lodash';\nimport GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';\nimport { disableButtonIfEmptyField } from '~/lib/utils/common_utils';\nimport dropzoneInput from './dropzone_input';\nimport { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';\n\nexport default class GLForm {\n /**\n * Create a GLForm\n *\n * @param {jQuery} form Root element of the GLForm\n * @param {Object} enableGFM Which autocomplete features should be enabled?\n * @param {Boolean} forceNew If true, treat the element as a **new** form even if `gfm-form` class already exists.\n * @param {Object} gfmDataSources The paths of the autocomplete data sources to use for GfmAutoComplete\n * By default, the backend embeds these in the global object gl.GfmAutocomplete.dataSources.\n * Use this param to override them.\n */\n // eslint-disable-next-line max-params\n constructor(form, enableGFM = {}, forceNew = false, gfmDataSources = {}) {\n this.form = form;\n this.textarea = this.form.find('textarea.js-gfm-input');\n this.enableGFM = { ...defaultAutocompleteConfig, ...enableGFM };\n\n // Disable autocomplete for keywords which do not have dataSources available\n let dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};\n\n if (!isEmpty(gfmDataSources)) {\n dataSources = gfmDataSources;\n }\n\n Object.keys(this.enableGFM).forEach((item) => {\n if (item !== 'emojis' && !dataSources[item]) {\n this.enableGFM[item] = false;\n }\n });\n\n // Before we start, we should clean up any previous data for this form\n this.destroy();\n // Set up the form\n this.setupForm(dataSources, forceNew);\n this.form.data('glForm', this);\n }\n\n destroy() {\n // Clean form listeners\n this.clearEventListeners();\n if (this.autoComplete) {\n this.autoComplete.destroy();\n }\n if (this.formDropzone) {\n this.formDropzone.destroy();\n }\n\n this.form.data('glForm', null);\n }\n\n setupForm(dataSources, forceNew = false) {\n const isNewForm = this.form.is(':not(.gfm-form)') || forceNew;\n this.form.removeClass('js-new-note-form');\n if (isNewForm) {\n this.form.find('.div-dropzone').remove();\n this.form.addClass('gfm-form');\n // remove notify commit author checkbox for non-commit notes\n disableButtonIfEmptyField(\n this.form.find('.js-note-text'),\n this.form.find('.js-comment-button, .js-note-new-discussion'),\n );\n this.autoComplete = new GfmAutoComplete(dataSources);\n this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM);\n this.formDropzone = dropzoneInput(this.form, { parallelUploads: 1 });\n\n if (this.form.is(':not(.js-no-autosize)')) {\n autosize(this.textarea);\n }\n }\n // form and textarea event listeners\n this.addEventListeners();\n addMarkdownListeners(this.form);\n this.form.show();\n if (this.isAutosizeable) this.setupAutosize();\n if (this.textarea.data('autofocus') === true) this.textarea.focus();\n }\n\n updateAutocompleteDataSources(dataSources) {\n if (this.autoComplete) {\n this.autoComplete.updateDataSources(dataSources);\n }\n }\n\n setupAutosize() {\n // eslint-disable-next-line @gitlab/no-global-event-off\n this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));\n\n // eslint-disable-next-line @gitlab/no-global-event-off\n this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this));\n\n setTimeout(() => {\n autosize(this.textarea);\n this.textarea.css('resize', 'vertical');\n }, 0);\n }\n\n setHeightData() {\n this.textarea.data('height', this.textarea.outerHeight());\n }\n\n destroyAutosize() {\n const outerHeight = this.textarea.outerHeight();\n\n if (this.textarea.data('height') === outerHeight) return;\n\n autosize.destroy(this.textarea);\n\n this.textarea.data('height', outerHeight);\n this.textarea.outerHeight(outerHeight);\n this.textarea.css('max-height', window.outerHeight);\n }\n\n clearEventListeners() {\n // eslint-disable-next-line @gitlab/no-global-event-off\n this.textarea.off('focus');\n // eslint-disable-next-line @gitlab/no-global-event-off\n this.textarea.off('blur');\n removeMarkdownListeners(this.form);\n }\n\n addEventListeners() {\n this.textarea.on('focus', function focusTextArea() {\n $(this).closest('.md-area').addClass('is-focused');\n });\n this.textarea.on('blur', function blurTextArea() {\n $(this).closest('.md-area').removeClass('is-focused');\n });\n }\n\n get supportsQuickActions() {\n return Boolean(this.textarea.data('supports-quick-actions'));\n }\n}\n","export default (buttonSelector, fileSelector) => {\n const btn = document.querySelector(buttonSelector);\n const fileInput = document.querySelector(fileSelector);\n\n if (!btn || !fileInput) return;\n\n const form = btn.closest('form');\n\n btn.addEventListener('click', () => {\n fileInput.click();\n });\n\n fileInput.addEventListener('change', () => {\n form.querySelector('.js-filename').textContent = fileInput.value.replace(/^.*[\\\\\\/]/, ''); // eslint-disable-line no-useless-escape\n });\n};\n\nexport const getFilename = (file) => {\n let fileName;\n if (file) {\n fileName = file.name;\n }\n\n return fileName;\n};\n\nexport const validateImageName = (file) => {\n const fileName = file.name ? file.name : 'image.png';\n const legalImageRegex = /^[\\w.\\-+]+\\.(png|jpg|jpeg|gif|bmp|tiff|ico|webp)$/;\n return legalImageRegex.test(fileName) ? fileName : 'image.png';\n};\n\nexport const validateFileFromAllowList = (fileName, allowList) => {\n const parts = fileName.split('.');\n const ext = `.${parts[parts.length - 1]}`;\n\n return allowList.includes(ext);\n};\n","/**\n * Takes a file object and returns a data uri of its contents.\n *\n * @param {File} file\n */\nexport function readFileAsDataURL(file) {\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.addEventListener('load', (e) => resolve(e.target.result), { once: true });\n reader.readAsDataURL(file);\n });\n}\n","class Vector {\n constructor(x, y) {\n this.x = x;\n this.y = y;\n }\n\n eq(x1, y1) {\n return this.x === x1 && this.y === y1;\n }\n\n neq(x1, y1) {\n return this.x !== x1 || this.y !== y1;\n }\n\n gte(x1, y1) {\n return this.x >= x1 && this.y >= y1;\n }\n\n gt(x1, y1) {\n return this.x > x1 && this.y > y1;\n }\n\n lte(x1, y1) {\n return this.x <= x1 && this.y <= y1;\n }\n\n lt(x1, y1) {\n return this.x < x1 && this.y < y1;\n }\n\n map(fn) {\n return new Vector(fn(this.x), fn(this.y));\n }\n\n mul(scalar) {\n return new Vector(this.x * scalar, this.y * scalar);\n }\n\n div(scalar) {\n return new Vector(this.x / scalar, this.y / scalar);\n }\n\n add(x1, y1) {\n return new Vector(this.x + x1, this.y + y1);\n }\n\n sub(x1, y1) {\n return new Vector(this.x - x1, this.y - y1);\n }\n\n round() {\n return new Vector(Math.round(this.x), Math.round(this.y));\n }\n\n floor() {\n return new Vector(Math.floor(this.x), Math.floor(this.y));\n }\n\n ceil() {\n return new Vector(Math.ceil(this.x), Math.ceil(this.y));\n }\n\n toSize() {\n return { width: this.x, height: this.y };\n }\n}\n\nconst vector = (x, y) => new Vector(x, y);\n\nexport default vector;\n","import { domToBlob } from 'modern-screenshot';\nimport vector from './vector';\nimport { readFileAsDataURL } from './file_utility';\n\n// 1 meter = 39.3701 inches\nconst METER_TO_INCHES = 39.3701;\nconst UNIT_METERS = 1;\nconst PNG_DEFAULT_PPI = 72;\n\nconst stringToUInt32 = (str) => {\n const buffer = str.split('').map((char) => char.charCodeAt(0));\n // eslint-disable-next-line no-bitwise\n return (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];\n};\n\nconst getPixelsPerInch = (pngImage) => {\n // pHYs is a chunk that specifies the intended pixel size or aspect ratio of the image\n // See https://www.w3.org/TR/PNG-Chunks.html#C.pHYs\n const physPosition = pngImage.indexOf('pHYs');\n if (physPosition === -1) return null;\n\n // p H Y s x x x x y y y y u\n // - - - - 0 1 2 3 4 5 6 7 8\n // ^ width ^ height ^ unit\n const phys = pngImage.substring(physPosition + 4, physPosition + 4 + 9);\n if (phys.charCodeAt(8) !== UNIT_METERS) return null;\n\n return vector(phys.substring(0, 4), phys.substring(4, 8))\n .map(stringToUInt32)\n .div(METER_TO_INCHES)\n .round();\n};\n\nconst fileToPngImage = async (file) => {\n if (file.type !== 'image/png') return null;\n\n const dataUrl = await readFileAsDataURL(file);\n return atob(dataUrl.split(',')[1]).split('IDAT')[0];\n};\n\nexport const getRetinaDimensions = async (pngFile) => {\n try {\n const pngImage = await fileToPngImage(pngFile);\n const pixelsPerInch = getPixelsPerInch(pngImage);\n if (pixelsPerInch.lte(PNG_DEFAULT_PPI, PNG_DEFAULT_PPI)) return null;\n\n // IHDR is the first chunk in a PNG file\n // It contains the image dimensions\n // See https://www.w3.org/TR/PNG-Chunks.html#C.IHDR\n const ihdrPosition = pngImage.substring(0, 30).indexOf('IHDR');\n if (ihdrPosition === -1) return null;\n\n // I H D R x x x x y y y y\n // - - - - 0 1 2 3 4 5 6 7\n // ^ width ^ height\n const ihdr = pngImage.substring(ihdrPosition + 4, ihdrPosition + 4 + 8);\n\n return vector(ihdr.substring(0, 4), ihdr.substring(4, 8))\n .map(stringToUInt32)\n .mul(PNG_DEFAULT_PPI)\n .div(Math.max(pixelsPerInch.x, pixelsPerInch.y))\n .ceil()\n .toSize();\n } catch (e) {\n return null;\n }\n};\n\nexport function domElementToBlob(domElement) {\n return domToBlob(domElement);\n}\n","/* eslint-disable func-names, no-param-reassign, operator-assignment, consistent-return */\nimport $ from 'jquery';\nimport Shortcuts from '~/behaviors/shortcuts/shortcuts';\nimport { insertText } from '~/lib/utils/common_utils';\nimport axios from '~/lib/utils/axios_utils';\nimport { isValidURL } from '~/lib/utils/url_utility';\n\nconst BOLD_TAG_PATTERN = '**';\nconst INLINE_CODE_TAG_PATTERN = '`';\nconst ITALIC_TAG_PATTERN = '_';\nconst LINK_TAG_PATTERN = '[{text}](url)';\nconst STRIKETHROUGH_TAG_PATTERN = '~~';\nconst QUOTE_TAG_PATTERN = '> ';\n\nconst ALLOWED_UNDO_TAGS = [\n BOLD_TAG_PATTERN,\n INLINE_CODE_TAG_PATTERN,\n ITALIC_TAG_PATTERN,\n STRIKETHROUGH_TAG_PATTERN,\n];\n\nconst INDENT_CHAR = ' ';\nconst INDENT_LENGTH = 2;\n\n// detect a horizontal rule that might be mistaken for a list item (not full pattern for an
)\nconst HR_PATTERN = /^((\\s{0,3}-+\\s*-+\\s*-+\\s*[\\s-]*)|(\\s{0,3}\\*+\\s*\\*+\\s*\\*+\\s*[\\s*]*))$/;\n\nconst INDENTED_LINE_PATTERN = /^(?\\s+)(?.)?/;\n\n// at the start of a line, find any amount of whitespace followed by\n// a bullet point character (*+-) and an optional checkbox ([ ] [x])\n// OR a number with a . after it and an optional checkbox ([ ] [x])\n// followed by one or more whitespace characters\nconst LIST_LINE_HEAD_PATTERN =\n /^(?\\s*)(?((?[*+-])|(?\\d+\\.))( \\[([xX~\\s])\\])?\\s)(?.)?/;\n\nconst QUOTE_LINE_PATTERN = /^\\s{0,3}>\\s?/;\n\nlet compositioningNoteText = false;\n\nfunction selectedText(text, textarea) {\n return text.substring(textarea.selectionStart, textarea.selectionEnd);\n}\n\nfunction addBlockTags(blockTag, selected) {\n return `${blockTag}\\n${selected}\\n${blockTag}`;\n}\n\n/**\n * Returns the line of text that is before the first line\n * of the current selection\n *\n * @param {String} text - the text of the targeted text area\n * @param {Object} textArea - the targeted text area\n * @returns {String}\n */\nfunction lineBeforeSelection(text, textArea) {\n let split = text.substring(0, textArea.selectionStart);\n\n split = split.split('\\n');\n\n // Last item, at -1, is the line where the start of selection is.\n // Line before selection is therefore at -2\n const lineBefore = split[split.length - 2];\n\n return lineBefore === undefined ? '' : lineBefore;\n}\n\n/**\n * Returns the line of text that is after the last line\n * of the current selection\n *\n * @param {String} text - the text of the targeted text area\n * @param {Object} textArea - the targeted text area\n * @returns {String}\n */\nfunction lineAfterSelection(text, textArea) {\n let split = text.substring(textArea.selectionEnd);\n\n // remove possible leading newline to get at the real line\n split = split.replace(/^\\n/, '');\n split = split.split('\\n');\n\n return split[0];\n}\n\n/**\n * Return subsequent lines after textArea.selectionEnd\n * that satisfy the provided condition.\n *\n * @param {Object} textArea - the targeted text area\n * @param {Function} condition - A function that takes a string as an\n * argument and returns a boolean indicating whether the line\n * meets the specified condition.\n * @returns {Object} An object containing:\n * - {Array} lines - An array of lines that satisfy the condition.\n * - {Number} startPos - The starting position of the first line\n * that satisfies the condition.\n * - {Number} endPos - The ending position of the last line that\n * satisfies the condition.\n */\nfunction linesAfterSelection(textArea, condition) {\n const { selectionEnd, value } = textArea;\n\n const selectionEndOfLine = value.indexOf('\\n', selectionEnd);\n if (selectionEndOfLine === -1) {\n return { lines: [], startPos: -1, endPos: -1 };\n }\n\n const remainingText = value.substring(selectionEndOfLine);\n\n const lines = [];\n const startPos = selectionEndOfLine + 1; // Move to new line\n let currentPos = startPos;\n\n for (const line of remainingText.replace(/^\\n/, '').split('\\n')) {\n if (!condition(line)) break;\n\n lines.push(line);\n currentPos += line.length + 1; // +1 for the newline character\n }\n\n return { lines, startPos, endPos: currentPos - 1 }; // -1 to exclude the last newline\n}\n\n/**\n * Returns the single character before the current selection,\n * or empty string if there is none.\n *\n * @param {String} text - the text of the targeted text area\n * @param {Object} textArea - the targeted text area\n * @returns {String}\n */\nfunction characterBeforeSelection(text, textArea) {\n return text.substring(Math.max(0, textArea.selectionStart - 1), textArea.selectionStart);\n}\n\n/**\n * Returns the single character after the current selection,\n * or empty string if there is none.\n *\n * @param {String} text - the text of the targeted text area\n * @param {Object} textArea - the targeted text area\n * @returns {String}\n */\nfunction characterAfterSelection(text, textArea) {\n return text.substring(textArea.selectionEnd, Math.min(textArea.selectionEnd + 1, text.length));\n}\n\n/**\n * Returns true if either brackets or parentheses surround the current\n * selection, false otherwise.\n *\n * @param {String} text - the text of the targeted text area\n * @param {Object} textArea - the targeted text area\n * @returns {Boolean}\n */\nfunction isSelectionBracketed(text, textArea) {\n const before = characterBeforeSelection(text, textArea);\n const after = characterAfterSelection(text, textArea);\n const pairedBrackets = {\n '[': ']',\n '(': ')',\n };\n return pairedBrackets[before] === after;\n}\n\n/**\n * Returns the text lines that encompass the current selection\n *\n * @param {Object} textArea - the targeted text area\n * @returns {Object}\n */\nfunction linesFromSelection(textArea) {\n const text = textArea.value;\n const { selectionStart, selectionEnd } = textArea;\n\n let startPos = text[selectionStart] === '\\n' ? selectionStart - 1 : selectionStart;\n startPos = text.lastIndexOf('\\n', startPos) + 1;\n\n let endPos = selectionEnd === selectionStart ? selectionEnd : selectionEnd - 1;\n endPos = text.indexOf('\\n', endPos);\n if (endPos < 0) endPos = text.length;\n\n const selectedRange = text.substring(startPos, endPos);\n const lines = selectedRange.split('\\n');\n\n return {\n lines,\n selectionStart,\n selectionEnd,\n startPos,\n endPos,\n };\n}\n\n/**\n * Set the selection of a textarea such that it maintains the\n * previous selection before the lines were indented/outdented\n *\n * @param {Object} textArea - the targeted text area\n * @param {Number} selectionStart - start position of original selection\n * @param {Number} selectionEnd - end position of original selection\n * @param {Number} lineStart - start pos of first line\n * @param {Number} firstLineChange - number of characters changed on first line\n * @param {Number} totalChanged - total number of characters changed\n */\n// eslint-disable-next-line max-params\nfunction setNewSelectionRange(\n textArea,\n selectionStart,\n selectionEnd,\n lineStart,\n firstLineChange,\n totalChanged,\n) {\n let newStart = Math.max(lineStart, selectionStart + firstLineChange);\n let newEnd = Math.max(lineStart, selectionEnd + totalChanged);\n\n if (selectionStart === selectionEnd) {\n newEnd = newStart;\n } else if (selectionStart === lineStart) {\n newStart = lineStart;\n }\n\n textArea.setSelectionRange(newStart, newEnd);\n}\n\nfunction convertMonacoSelectionToAceFormat(sel) {\n return {\n start: {\n row: sel.startLineNumber,\n column: sel.startColumn,\n },\n end: {\n row: sel.endLineNumber,\n column: sel.endColumn,\n },\n };\n}\n\nfunction getEditorSelectionRange(editor) {\n return convertMonacoSelectionToAceFormat(editor.getSelection());\n}\n\n// eslint-disable-next-line max-params\nfunction editorBlockTagText(text, blockTag, selected, editor) {\n const lines = text.split('\\n');\n const selectionRange = getEditorSelectionRange(editor);\n const shouldRemoveBlock =\n lines[selectionRange.start.row - 1] === blockTag &&\n lines[selectionRange.end.row + 1] === blockTag;\n\n if (shouldRemoveBlock) {\n if (blockTag !== null) {\n const lastLine = lines[selectionRange.end.row + 1];\n const rangeWithBlockTags = new Range(\n lines[selectionRange.start.row - 1],\n 0,\n selectionRange.end.row + 1,\n lastLine.length,\n );\n editor.getSelection().setSelectionRange(rangeWithBlockTags);\n }\n return selected;\n }\n return addBlockTags(blockTag, selected);\n}\n\n// eslint-disable-next-line max-params\nfunction blockTagText(text, textArea, blockTag, selected) {\n const shouldRemoveBlock =\n lineBeforeSelection(text, textArea) === blockTag &&\n lineAfterSelection(text, textArea) === blockTag;\n\n if (shouldRemoveBlock) {\n // To remove the block tag we have to select the line before & after\n if (blockTag != null) {\n textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);\n textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);\n }\n return selected;\n }\n return addBlockTags(blockTag, selected);\n}\n\nfunction moveCursor({\n textArea,\n tag,\n cursorOffset,\n positionBetweenTags,\n removedLastNewLine,\n select,\n editor,\n editorSelectionStart,\n editorSelectionEnd,\n}) {\n let pos;\n if (textArea && !textArea.setSelectionRange) {\n return;\n }\n if (select && select.length > 0) {\n if (textArea) {\n // calculate the part of the text to be selected\n const startPosition = textArea.selectionStart - (tag.length - tag.indexOf(select));\n const endPosition = startPosition + select.length;\n return textArea.setSelectionRange(startPosition, endPosition);\n }\n if (editor) {\n editor.selectWithinSelection(select, tag);\n return;\n }\n }\n if (textArea) {\n if (textArea.selectionStart === textArea.selectionEnd) {\n if (positionBetweenTags) {\n pos = textArea.selectionStart - tag.length;\n } else {\n pos = textArea.selectionStart;\n }\n\n if (removedLastNewLine) {\n pos -= 1;\n }\n\n if (cursorOffset) {\n pos -= cursorOffset;\n }\n\n return textArea.setSelectionRange(pos, pos);\n }\n } else if (editor && editorSelectionStart.row === editorSelectionEnd.row) {\n if (positionBetweenTags) {\n editor.moveCursor(tag.length * -1);\n }\n }\n}\n\n/**\n * This function returns some strategy values that inform the rest of the legacy code\n * on how to behave\n *\n * 1. `getSelectedWithoutTags` this is a function that returns `selected` without the given `tag`.\n * NOTE: For the most part, this is relevant in a non-multiline selection.\n * 2. `shouldUseSelectedWithoutTags` this is a function that returns whether we should use\n * the `getSelectedWithoutTags`. NOTE: For the most part, this is relevant only in a\n * non-multiline selection.\n * 3. `shouldRemoveTagFromLine` in a multiline selection, this is a function that returns whether\n * we want to remove the tag from the line.\n * 4. `removeTagFromLine` in a multiline selection, this is a function that removes\n * the tag from the line.\n *\n * @param {Object} options\n * @param {string} selected - the selected bit of the text area which is to be replaced\n * @param {string} tag - this is the Markdown tag we want to insert\n * @param {string} selectedSplit - this is selected that has been split by lines `.split('\\n')`\n */\nfunction prepareInsertMarkdownText({ selected, tag, selectedSplit }) {\n if (tag === QUOTE_TAG_PATTERN) {\n const shouldRemoveQuote = selectedSplit.every((val) => QUOTE_LINE_PATTERN.test(val));\n\n return {\n getSelectedWithoutTags: () =>\n selected.slice(selected.match(QUOTE_LINE_PATTERN)[0].length, selected.length),\n shouldUseSelectedWithoutTags: QUOTE_LINE_PATTERN.test(selected),\n removeTagFromLine: (line) => line.replace(QUOTE_LINE_PATTERN, ''),\n shouldRemoveTagFromLine: () => shouldRemoveQuote,\n };\n }\n\n return {\n getSelectedWithoutTags: () => selected.slice(tag.length, selected.length - tag.length),\n shouldUseSelectedWithoutTags:\n selected.length >= tag.length * 2 &&\n selected.startsWith(tag) &&\n selected.endsWith(tag) &&\n ALLOWED_UNDO_TAGS.includes(tag),\n removeTagFromLine: (line) => line.replace(tag, ''),\n shouldRemoveTagFromLine: (line) => line.indexOf(tag) === 0,\n };\n}\n\n/**\n * Inserts the given MarkdownText into the given textArea or editor\n *\n * WARNING: This is a bit of legacy code that has some complicated logic.\n * There are a lot of hidden contexts to consider here. Please proceed with caution.\n *\n * We've tried to document the parameter responsibilities as best as possible.\n * Please look for actual usage in the code to verify any assumptions.\n *\n * @param {Object} options - Named parameters\n * @param {HTMLTextAreaElement} options.textArea - The relevant text area\n * @param {String} options.text - The current text of the text area\n * @param {String} options.tag - The markdown tag we want to enter (Example: `- [ ] ` for lists)\n * @param {Number} options.cursorOffset - Applied to the position after we insert the text (moves backward)\n * @param {String} options.blockTag - The markdown tag to use if a block is detected (Example ` ``` ` vs. ` ` `)\n * @param {Boolean} options.wrap - Flag for whether the tag is a wrapping tag (Example `**text**` vs `* text`)\n * @param {String} options.select - The text to select after inserting (Example `url` of `({text})[url]`)\n * @param {Object} options.editor - The instance of the SourceEditor which we are inserting MarkdownText into. This should be mutually exclusive with textArea.\n */\nexport function insertMarkdownText({\n textArea,\n text,\n tag,\n cursorOffset,\n blockTag,\n selected = '',\n wrap,\n select,\n editor,\n}) {\n // If we aren't really inserting anything, let's just noop.\n // Let's check for `selected` too because there might be hidden logic that actually\n // is expected to run for this case.\n if (!tag && !blockTag && !selected) {\n return;\n }\n\n let removedLastNewLine = false;\n let removedFirstNewLine = false;\n let currentLineEmpty = false;\n let editorSelectionStart;\n let editorSelectionEnd;\n let lastNewLine;\n let textToUpdate;\n selected = selected.toString();\n\n if (editor) {\n const selectionRange = getEditorSelectionRange(editor);\n\n editorSelectionStart = selectionRange.start;\n editorSelectionEnd = selectionRange.end;\n }\n\n // check for link pattern and selected text is an URL\n // if so fill in the url part instead of the text part of the pattern.\n if (tag === LINK_TAG_PATTERN) {\n if (isValidURL(selected)) {\n tag = '[text]({text})';\n select = 'text';\n }\n }\n\n // Remove the first newline\n if (selected.indexOf('\\n') === 0) {\n removedFirstNewLine = true;\n selected = selected.replace(/\\n+/, '');\n }\n\n // Remove the last newline\n if (textArea) {\n if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\\n$/, '').length) {\n removedLastNewLine = true;\n selected = selected.replace(/\\n$/, '');\n }\n } else if (editor) {\n if (editorSelectionStart.row !== editorSelectionEnd.row) {\n removedLastNewLine = true;\n selected = selected.replace(/\\n$/, '');\n }\n }\n\n const selectedSplit = selected.split('\\n');\n\n if (editor && !wrap) {\n lastNewLine = editor.getValue().split('\\n')[editorSelectionStart.row];\n\n if (/^\\s*$/.test(lastNewLine)) {\n currentLineEmpty = true;\n }\n } else if (textArea && !wrap) {\n lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\\n');\n\n // Check whether the current line is empty or consists only of spaces(=handle as empty)\n if (/^\\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) {\n currentLineEmpty = true;\n }\n }\n\n const isBeginning =\n (textArea && textArea.selectionStart === 0) ||\n (editor && editorSelectionStart.column === 0 && editorSelectionStart.row === 0);\n\n const startChar = !wrap && !currentLineEmpty && !isBeginning ? '\\n' : '';\n const textPlaceholder = '{text}';\n\n const {\n shouldUseSelectedWithoutTags,\n removeTagFromLine,\n shouldRemoveTagFromLine,\n getSelectedWithoutTags,\n } = prepareInsertMarkdownText({ selected, tag, selectedSplit });\n\n const getSelectedWithTags = () => `${startChar}${tag}${selected}${wrap ? tag : ''}`;\n\n if (selectedSplit.length > 1 && (!wrap || (blockTag != null && blockTag !== ''))) {\n if (blockTag != null && blockTag !== '') {\n textToUpdate = editor\n ? editorBlockTagText(text, blockTag, selected, editor)\n : blockTagText(text, textArea, blockTag, selected);\n } else {\n textToUpdate = selectedSplit\n .map((line) => {\n if (tag.indexOf(textPlaceholder) > -1) {\n return tag.replace(textPlaceholder, line);\n }\n if (shouldRemoveTagFromLine(line)) {\n return removeTagFromLine(line);\n }\n return String(tag) + line;\n })\n .join('\\n');\n }\n } else if (tag.indexOf(textPlaceholder) > -1) {\n textToUpdate = tag.replace(textPlaceholder, () =>\n selected.replace(/\\\\n/g, '\\n').replace(/%br/g, '\\\\n'),\n );\n } else if (shouldUseSelectedWithoutTags) {\n textToUpdate = getSelectedWithoutTags();\n } else {\n textToUpdate = getSelectedWithTags();\n }\n\n if (removedFirstNewLine) {\n textToUpdate = `\\n${textToUpdate}`;\n }\n\n if (removedLastNewLine) {\n textToUpdate += '\\n';\n }\n\n if (editor) {\n editor.replaceSelectedText(textToUpdate, select);\n } else {\n insertText(textArea, textToUpdate);\n }\n\n moveCursor({\n textArea,\n tag: tag.replace(textPlaceholder, selected),\n cursorOffset,\n positionBetweenTags: wrap && selected.length === 0,\n removedLastNewLine,\n select,\n editor,\n editorSelectionStart,\n editorSelectionEnd,\n });\n}\n\nexport function updateText({ textArea, tag, cursorOffset, blockTag, wrap, select, tagContent }) {\n const $textArea = $(textArea);\n textArea = $textArea.get(0);\n const text = $textArea.val();\n const selected = selectedText(text, textArea) || tagContent;\n textArea.focus();\n insertMarkdownText({\n textArea,\n text,\n tag,\n cursorOffset,\n blockTag,\n selected,\n wrap,\n select,\n });\n textArea.click();\n}\n\n/**\n * Indents selected lines to the right by 2 spaces\n *\n * @param {Object} textArea - jQuery object with the targeted text area\n */\nfunction indentLines($textArea) {\n const textArea = $textArea.get(0);\n const { lines, selectionStart, selectionEnd, startPos, endPos } = linesFromSelection(textArea);\n const shiftedLines = [];\n let totalAdded = 0;\n\n textArea.focus();\n textArea.setSelectionRange(startPos, endPos);\n\n lines.forEach((line) => {\n line = INDENT_CHAR.repeat(INDENT_LENGTH) + line;\n totalAdded += INDENT_LENGTH;\n\n shiftedLines.push(line);\n });\n\n const textToInsert = shiftedLines.join('\\n');\n\n insertText(textArea, textToInsert);\n setNewSelectionRange(textArea, selectionStart, selectionEnd, startPos, INDENT_LENGTH, totalAdded);\n}\n\n/**\n * Outdents selected lines to the left by 2 spaces\n *\n * @param {Object} textArea - the targeted text area\n */\nfunction outdentLines($textArea) {\n const textArea = $textArea.get(0);\n const { lines, selectionStart, selectionEnd, startPos, endPos } = linesFromSelection(textArea);\n const shiftedLines = [];\n let totalRemoved = 0;\n let removedFromFirstline = -1;\n let removedFromLine = 0;\n\n textArea.focus();\n textArea.setSelectionRange(startPos, endPos);\n\n lines.forEach((line) => {\n removedFromLine = 0;\n\n if (line.length > 0) {\n // need to count how many spaces are actually removed, so can't use `replace`\n while (removedFromLine < INDENT_LENGTH && line[removedFromLine] === INDENT_CHAR) {\n removedFromLine += 1;\n }\n\n if (removedFromLine > 0) {\n line = line.slice(removedFromLine);\n totalRemoved += removedFromLine;\n }\n }\n\n if (removedFromFirstline === -1) removedFromFirstline = removedFromLine;\n shiftedLines.push(line);\n });\n\n const textToInsert = shiftedLines.join('\\n');\n\n if (totalRemoved > 0) insertText(textArea, textToInsert);\n\n setNewSelectionRange(\n textArea,\n selectionStart,\n selectionEnd,\n startPos,\n -removedFromFirstline,\n -totalRemoved,\n );\n}\n\n/* eslint-disable @gitlab/require-i18n-strings */\nfunction handleSurroundSelectedText(e, textArea) {\n if (!gon.markdown_surround_selection) return;\n if (e.metaKey || e.ctrlKey) return;\n if (textArea.selectionStart === textArea.selectionEnd) return;\n\n const keys = {\n '*': '**{text}**', // wraps with bold character\n _: '_{text}_', // wraps with italic character\n '`': '`{text}`', // wraps with inline character\n \"'\": \"'{text}'\", // single quotes\n '\"': '\"{text}\"', // double quotes\n '[': '[{text}]', // brackets\n '{': '{{text}}', // braces\n '(': '({text})', // parentheses\n '<': '<{text}>', // angle brackets\n };\n const tag = keys[e.key];\n\n if (tag) {\n e.preventDefault();\n\n updateText({\n tag,\n textArea,\n blockTag: '',\n wrap: true,\n select: '',\n tagContent: '',\n });\n }\n}\n\n/* eslint-enable @gitlab/require-i18n-strings */\n\nfunction isEnterPressedOnly(e) {\n return e.key === 'Enter' && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey;\n}\n\nfunction shouldHandleIndentation(e, textArea) {\n return (\n isEnterPressedOnly(e) &&\n textArea.selectionStart === textArea.selectionEnd &&\n !compositioningNoteText\n );\n}\n\n/**\n * Returns the content for a new line following a ordered list item.\n *\n * @param {Object} listLineMatch - regex match of the current line\n * @param {Number} num - number for the list item\n * @returns {String} with the new list item\n */\nfunction createOlText(listLineMatch, num) {\n const { indent, leader } = listLineMatch.groups;\n const [, postfix = ''] = leader.split('.');\n return `${indent}${num}.${postfix}`;\n}\n\n/**\n * Returns the ordering number for ordered list item\n *\n * @param {Object} regex match of the current line\n * @returns {Number} ordering nubmer for the regex match of the current line\n */\nfunction parseOlNum({ groups: { leader } }) {\n return parseInt(leader, 10);\n}\n\n/**\n * Update ordered list line numbers in textArea from startPos to endPos\n *\n * @param {Object} textArea - textArea object\n * @param {Array} lines - array of strings from startPos to endPos in textArea\n * @param {Number} startPos - start position of the lines array\n * @param {Number} endPos - end position of the lines array\n * @param {Number} from - starting number for the first line in lines\n */\nfunction updateOlLineNumbers({ textArea, lines, startPos, endPos, from }) {\n if (lines.length === 0) {\n return;\n }\n\n const incrementedLines = [];\n const { selectionStart, selectionEnd } = textArea;\n\n let orderingNumber = from;\n lines.forEach((line) => {\n const lineMatch = line.match(LIST_LINE_HEAD_PATTERN);\n const lineContent = line.slice(lineMatch.groups.leader.length + lineMatch.groups.indent.length);\n\n const incrementedLine = createOlText(lineMatch, orderingNumber) + lineContent;\n incrementedLines.push(incrementedLine);\n orderingNumber = orderingNumber + 1;\n });\n\n textArea.focus();\n textArea.setSelectionRange(startPos, endPos);\n const textToInsert = incrementedLines.join('\\n');\n insertText(textArea, textToInsert);\n textArea.setSelectionRange(selectionStart, selectionEnd);\n}\n\n/**\n * Updates all subsequent ordered list items starting from textArea.endPos\n *\n * @param {Object} textArea - the targeted text area\n * @param {Object} listLineMatch - regex match of the current line\n * @param {Number} from - starting number for the first subsequent ordered list line\n */\nfunction updateOlLineNumbersAfterSelection(textArea, listLineMatch, from) {\n const { indent } = listLineMatch.groups;\n const { lines, startPos, endPos } = linesAfterSelection(textArea, (text) => {\n const textMatch = text.match(LIST_LINE_HEAD_PATTERN);\n const { indent: nextIndent, isOl: nextIsOl } = textMatch?.groups ?? {};\n return nextIsOl && nextIndent === indent;\n });\n\n updateOlLineNumbers({ textArea, lines, startPos, endPos, from });\n}\n\nfunction handleContinueList(e, textArea) {\n if (!gon.markdown_automatic_lists) return;\n\n if (!shouldHandleIndentation(e, textArea)) {\n return;\n }\n\n const selectedLines = linesFromSelection(textArea);\n const firstSelectedLine = selectedLines.lines[0];\n const listLineMatch = firstSelectedLine.match(LIST_LINE_HEAD_PATTERN);\n\n if (listLineMatch) {\n const { leader, indent, content, isOl } = listLineMatch.groups;\n const emptyListItem = !content;\n const prefixLength = leader.length + indent.length;\n\n if (selectedLines.selectionStart - selectedLines.startPos < prefixLength) {\n // cursor in the indent/leader area, allow the natural line feed to be added\n return;\n }\n\n if (emptyListItem) {\n // erase empty list item - select the text and allow the\n // natural line feed to erase the text\n textArea.selectionStart = textArea.selectionStart - listLineMatch[0].length;\n updateOlLineNumbersAfterSelection(textArea, listLineMatch, 1);\n return;\n }\n\n let itemToInsert;\n\n // Behaviors specific to either `ol` or `ul`\n if (isOl) {\n const nextNum = parseOlNum(listLineMatch) + 1;\n itemToInsert = createOlText(listLineMatch, nextNum);\n updateOlLineNumbersAfterSelection(textArea, listLineMatch, nextNum + 1);\n } else {\n if (firstSelectedLine.match(HR_PATTERN)) return;\n\n itemToInsert = `${indent}${leader}`;\n }\n\n itemToInsert = itemToInsert.replace(/\\[[x~]\\]/i, '[ ]');\n\n e.preventDefault();\n\n updateText({\n tag: itemToInsert,\n textArea,\n blockTag: '',\n wrap: false,\n select: '',\n tagContent: '',\n });\n }\n}\n\nfunction handleContinueIndentedText(e, textArea) {\n if (!gon.features?.continueIndentedText) return;\n\n if (!shouldHandleIndentation(e, textArea)) {\n return;\n }\n\n const selectedLines = linesFromSelection(textArea);\n const firstSelectedLine = selectedLines.lines[0];\n const lineMatch = firstSelectedLine.match(INDENTED_LINE_PATTERN);\n\n if (!lineMatch) return;\n\n const { indent, content } = lineMatch.groups;\n const isInsideLeadingWhitespace =\n selectedLines.selectionStart - selectedLines.startPos < indent.length;\n if (isInsideLeadingWhitespace) {\n return;\n }\n\n if (!content) {\n textArea.selectionStart -= lineMatch[0].length;\n return;\n }\n\n e.preventDefault();\n\n updateText({\n tag: indent,\n textArea,\n blockTag: '',\n wrap: false,\n select: '',\n tagContent: '',\n });\n}\n\nexport function keypressNoteText(e) {\n const textArea = this;\n\n if ($(textArea).atwho?.('isSelecting')) return;\n\n handleContinueList(e, textArea);\n\n // If this was in fact a valid list item, indentation was handled already\n if (!e.isDefaultPrevented()) {\n handleContinueIndentedText(e, textArea);\n }\n handleSurroundSelectedText(e, textArea);\n}\n\nexport function compositionStartNoteText() {\n compositioningNoteText = true;\n}\n\nexport function compositionEndNoteText() {\n compositioningNoteText = false;\n}\n\nfunction handleMarkdownPasteUrl(e) {\n const textArea = e.target;\n if (textArea.selectionStart === textArea.selectionEnd) return;\n const text = textArea.value;\n // If the user has selected [text](_url_), or [_text_](url),\n // we want to do a simple paste not add additional markdown.\n if (isSelectionBracketed(text, textArea)) return;\n if (!e.clipboardData) return;\n\n let pastedText = e.clipboardData.getData('text');\n if (!pastedText) return;\n pastedText = pastedText.trim();\n if (isValidURL(pastedText)) {\n e.preventDefault();\n e.stopImmediatePropagation();\n const selected = selectedText(text, textArea);\n\n const textToInsert = `[${selected}](${pastedText})`;\n insertText(textArea, textToInsert);\n }\n // If it wasn't a URL, just let default paste happen\n}\n\nexport function handlePasteModifications(e) {\n // Transparently unwrap event in case we're bound through jQuery.\n // Need the original event to access the clipboard.\n const event = e?.originalEvent ? e.originalEvent : e;\n handleMarkdownPasteUrl(event);\n}\n\nexport function updateTextForToolbarBtn($toolbarBtn) {\n const $textArea = $toolbarBtn.closest('.md-area').find('textarea');\n if (!$textArea.length) return;\n\n switch ($toolbarBtn.data('mdCommand')) {\n case 'indentLines':\n indentLines($textArea);\n break;\n case 'outdentLines':\n outdentLines($textArea);\n break;\n default:\n return updateText({\n textArea: $textArea,\n tag: $toolbarBtn.data('mdTag'),\n cursorOffset: $toolbarBtn.data('mdCursorOffset'),\n blockTag: $toolbarBtn.data('mdBlock'),\n wrap: !$toolbarBtn.data('mdPrepend'),\n select: $toolbarBtn.data('mdSelect'),\n tagContent: $toolbarBtn.attr('data-md-tag-content'),\n });\n }\n}\n\nexport function addMarkdownListeners(form) {\n $('.markdown-area', form)\n .on('keydown', keypressNoteText)\n .on('compositionstart', compositionStartNoteText)\n .on('compositionend', compositionEndNoteText)\n .on('paste', handlePasteModifications)\n .each(function attachTextareaShortcutHandlers() {\n Shortcuts.initMarkdownEditorShortcuts($(this), updateTextForToolbarBtn);\n });\n\n const $allToolbarBtns = $(form)\n .off('click', '.js-md')\n .on('click', '.js-md', function () {\n const $toolbarBtn = $(this);\n return updateTextForToolbarBtn($toolbarBtn);\n });\n\n return $allToolbarBtns;\n}\n\nexport function addEditorMarkdownListeners(editor) {\n // eslint-disable-next-line @gitlab/no-global-event-off\n $('.js-md')\n .off('click')\n .on('click', (e) => {\n const { mdTag, mdBlock, mdPrepend, mdSelect } = $(e.currentTarget).data();\n\n insertMarkdownText({\n tag: mdTag,\n blockTag: mdBlock,\n wrap: !mdPrepend,\n select: mdSelect,\n selected: editor.getSelectedText(),\n text: editor.getValue(),\n editor,\n });\n editor.focus();\n });\n}\n\nexport function removeMarkdownListeners(form) {\n $('.markdown-area', form)\n .off('keydown', keypressNoteText)\n .off('compositionstart', compositionStartNoteText)\n .off('compositionend', compositionEndNoteText)\n .off('paste', handlePasteModifications)\n .each(function removeTextareaShortcutHandlers() {\n Shortcuts.removeMarkdownEditorShortcuts($(this));\n });\n\n // eslint-disable-next-line @gitlab/no-global-event-off\n return $('.js-md', form).off('click');\n}\n\n/**\n * If the textarea cursor is positioned in a Markdown image declaration,\n * it uses the Markdown API to resolve the image’s absolute URL.\n * @param {Object} textarea Textarea DOM element\n * @param {String} markdownPreviewPath Markdown API path\n * @returns {Object} an object containing the image’s absolute URL, filename,\n * and the markdown declaration. If the textarea cursor is not positioned\n * in an image, it returns null.\n */\nexport const resolveSelectedImage = async (textArea, markdownPreviewPath = '') => {\n const { lines, startPos } = linesFromSelection(textArea);\n\n // image declarations can’t span more than one line in Markdown\n if (lines > 0) {\n return null;\n }\n\n const selectedLine = lines[0];\n\n if (!/!\\[.+?\\]\\(.+?\\)/.test(selectedLine)) return null;\n\n const lineSelectionStart = textArea.selectionStart - startPos;\n const preExlm = selectedLine.substring(0, lineSelectionStart).lastIndexOf('!');\n const postClose = selectedLine.substring(lineSelectionStart).indexOf(')');\n\n if (preExlm >= 0 && postClose >= 0) {\n const imageMarkdown = selectedLine.substring(preExlm, lineSelectionStart + postClose + 1);\n const { data } = await axios.post(markdownPreviewPath, { text: imageMarkdown });\n const parser = new DOMParser();\n\n const dom = parser.parseFromString(data.body, 'text/html');\n const imageURL = dom.body.querySelector('a').getAttribute('href');\n\n if (imageURL) {\n const filename = imageURL.substring(imageURL.lastIndexOf('/') + 1);\n\n return {\n imageMarkdown,\n imageURL,\n filename,\n };\n }\n }\n\n return null;\n};\n\nexport const repeatCodeBackticks = (content) => {\n const numBackticks =\n Math.max(2, content.match(/```+/g)?.sort((a, b) => b.length - a.length)[0]?.length || 0) + 1;\n return '`'.repeat(numBackticks);\n};\n","import $ from 'jquery';\nimport { flatten } from 'lodash';\nimport Vue from 'vue';\nimport { InternalEvents } from '~/tracking';\nimport { FIND_FILE_SHORTCUT_CLICK } from '~/tracking/constants';\nimport { Mousetrap, addStopCallback } from '~/lib/mousetrap';\nimport { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils';\nimport { waitForElement } from '~/lib/utils/dom_utils';\nimport findAndFollowLink from '~/lib/utils/navigation_utility';\nimport { refreshCurrentPage } from '~/lib/utils/url_utility';\nimport {\n keysFor,\n TOGGLE_KEYBOARD_SHORTCUTS_DIALOG,\n START_SEARCH,\n START_SEARCH_PROJECT_FILE,\n FOCUS_FILTER_BAR,\n TOGGLE_PERFORMANCE_BAR,\n HIDE_APPEARING_CONTENT,\n TOGGLE_CANARY,\n TOGGLE_MARKDOWN_PREVIEW,\n FIND_AND_REPLACE,\n GO_TO_YOUR_TODO_LIST,\n GO_TO_ACTIVITY_FEED,\n GO_TO_YOUR_ISSUES,\n GO_TO_YOUR_MERGE_REQUESTS,\n GO_TO_YOUR_PROJECTS,\n GO_TO_YOUR_GROUPS,\n GO_TO_MILESTONE_LIST,\n GO_TO_YOUR_SNIPPETS,\n GO_TO_YOUR_REVIEW_REQUESTS,\n} from './keybindings';\nimport { disableShortcuts, shouldDisableShortcuts } from './shortcuts_toggle';\n\n/**\n * The key used to save and fetch the local Mousetrap instance\n * attached to a `