You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1044 lines
155 KiB

3 years ago
/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/
'use strict';
var obsidian = require('obsidian');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
/*
* Filename: multi-column-markdown/src/regionSettings.ts
* Created Date: Tuesday, February 1st 2022, 12:23:53 pm
* Author: Cameron Robinson
*
* Copyright (c) 2022 Cameron Robinson
*/
var BorderOption;
(function (BorderOption) {
BorderOption[BorderOption["enabled"] = 0] = "enabled";
BorderOption[BorderOption["on"] = 1] = "on";
BorderOption[BorderOption["true"] = 2] = "true";
BorderOption[BorderOption["disabled"] = 3] = "disabled";
BorderOption[BorderOption["off"] = 4] = "off";
BorderOption[BorderOption["false"] = 5] = "false";
})(BorderOption || (BorderOption = {}));
var ShadowOption;
(function (ShadowOption) {
ShadowOption[ShadowOption["enabled"] = 0] = "enabled";
ShadowOption[ShadowOption["on"] = 1] = "on";
ShadowOption[ShadowOption["true"] = 2] = "true";
ShadowOption[ShadowOption["disabled"] = 3] = "disabled";
ShadowOption[ShadowOption["off"] = 4] = "off";
ShadowOption[ShadowOption["false"] = 5] = "false";
})(ShadowOption || (ShadowOption = {}));
var ColumnLayout;
(function (ColumnLayout) {
ColumnLayout[ColumnLayout["standard"] = 0] = "standard";
ColumnLayout[ColumnLayout["left"] = 1] = "left";
ColumnLayout[ColumnLayout["first"] = 2] = "first";
ColumnLayout[ColumnLayout["center"] = 3] = "center";
ColumnLayout[ColumnLayout["middle"] = 4] = "middle";
ColumnLayout[ColumnLayout["second"] = 5] = "second";
ColumnLayout[ColumnLayout["right"] = 6] = "right";
ColumnLayout[ColumnLayout["third"] = 7] = "third";
ColumnLayout[ColumnLayout["last"] = 8] = "last";
})(ColumnLayout || (ColumnLayout = {}));
/*
* File: multi-column-markdown/src/MultiColumnParser.ts
* Created Date: Saturday, January 22nd 2022, 6:02:46 pm
* Author: Cameron Robinson
*
* Copyright (c) 2022 Cameron Robinson
*/
const START_REGEX_STRS = ["=== *start-multi-column",
"=== *multi-column-start"];
const START_REGEX_ARR = [];
for (let i = 0; i < START_REGEX_STRS.length; i++) {
START_REGEX_ARR.push(new RegExp(START_REGEX_STRS[i]));
}
function findStartTag(text) {
let found = false;
let startPosition = -1;
for (let i = 0; i < START_REGEX_ARR.length; i++) {
if (START_REGEX_ARR[i].test(text)) {
found = true;
startPosition = text.search(START_REGEX_STRS[i]);
break;
}
}
return { found, startPosition };
}
function containsStartTag(text) {
return findStartTag(text).found;
}
function isStartTagWithID(text) {
let startTagData = findStartTag(text);
if (startTagData.found === true) {
let key = getStartTagKey(text);
if (key === null || key === "") {
return { isStartTag: true, hasKey: false };
}
return { isStartTag: true, hasKey: true };
}
return { isStartTag: false, hasKey: false };
}
const END_REGEX_STRS = ["=== *end-multi-column",
"=== *multi-column-end"];
const END_REGEX_ARR = [];
for (let i = 0; i < END_REGEX_STRS.length; i++) {
END_REGEX_ARR.push(new RegExp(END_REGEX_STRS[i]));
}
function findEndTag(text) {
let found = false;
let startPosition = -1;
for (let i = 0; i < END_REGEX_ARR.length; i++) {
if (END_REGEX_ARR[i].test(text)) {
found = true;
startPosition = text.search(END_REGEX_STRS[i]);
break;
}
}
return { found, startPosition };
}
function containsEndTag(text) {
return findEndTag(text).found;
}
const COL_REGEX_STRS = ["=== *column-end *===",
"=== *end-column *===",
"=== *column-break *===",
"=== *break-column *==="];
const COL_REGEX_ARR = [];
for (let i = 0; i < COL_REGEX_STRS.length; i++) {
COL_REGEX_ARR.push(new RegExp(COL_REGEX_STRS[i]));
}
function containsColEndTag(text) {
let found = false;
for (let i = 0; i < COL_REGEX_ARR.length; i++) {
if (COL_REGEX_ARR[i].test(text)) {
found = true;
break;
}
}
return found;
}
const COL_SETTINGS_REGEX_STRS = ["```settings",
"```column-settings",
"```multi-column-settings"];
const COL_SETTINGS_REGEX_ARR = [];
for (let i = 0; i < COL_SETTINGS_REGEX_STRS.length; i++) {
COL_SETTINGS_REGEX_ARR.push(new RegExp(COL_SETTINGS_REGEX_STRS[i]));
}
function containsColSettingsTag(text) {
let found = false;
for (let i = 0; i < COL_SETTINGS_REGEX_ARR.length; i++) {
if (COL_SETTINGS_REGEX_ARR[i].test(text)) {
found = true;
break;
}
}
return found;
}
function parseColumnSettings(settingsStr) {
// Set the minimum number of columnds to 2.
let numberOfColumns = 2;
let columnLayout = ColumnLayout.standard;
let borderDrawn = true;
let shadowDrawn = true;
let settingsLines = settingsStr.split("\n");
for (let i = 0; i < settingsLines.length; i++) {
if (settingsLines[i].toLowerCase().replace(/\s/g, "").contains("numberofcolumns:")) {
let userDefNumberOfCols = parseInt(settingsLines[i].split(":")[1]);
if (Number.isNaN(userDefNumberOfCols) === false) {
if (userDefNumberOfCols === 3) {
numberOfColumns = 3;
}
else if (userDefNumberOfCols === 2) {
numberOfColumns = 2;
}
}
break;
}
}
for (let i = 0; i < settingsLines.length; i++) {
if (settingsLines[i].toLowerCase().replace(/\s/g, "").contains("largestcolumn:")) {
let setting = settingsLines[i].split(":")[1].trimStart().trimEnd().toLowerCase();
let userDefLayout = ColumnLayout[setting];
if (userDefLayout !== undefined) {
columnLayout = userDefLayout;
}
}
}
for (let i = 0; i < settingsLines.length; i++) {
if (settingsLines[i].toLowerCase().replace(/\s/g, "").contains("border:")) {
let setting = settingsLines[i].split(":")[1].trimStart().trimEnd().toLowerCase();
let isBorderDrawn = BorderOption[setting];
if (isBorderDrawn !== undefined) {
switch (isBorderDrawn) {
case (BorderOption.disabled):
case (BorderOption.off):
case (BorderOption.false):
borderDrawn = false;
break;
}
}
}
}
for (let i = 0; i < settingsLines.length; i++) {
if (settingsLines[i].toLowerCase().replace(/\s/g, "").contains("shadow:")) {
let setting = settingsLines[i].split(":")[1].trimStart().trimEnd().toLowerCase();
let isShadowDrawn = ShadowOption[setting];
if (isShadowDrawn !== undefined) {
switch (isShadowDrawn) {
case (ShadowOption.disabled):
case (ShadowOption.off):
case (ShadowOption.false):
shadowDrawn = false;
break;
}
}
}
}
let settings = { numberOfColumns, columnLayout, drawBorder: borderDrawn, drawShadow: shadowDrawn };
return settings;
}
function countStartTags(text) {
let keys = [];
let startTagData = findStartTag(text);
while (startTagData.found) {
// Slice off everything before the tag
text = text.slice(startTagData.startPosition);
/**
* Get just the start tag line and then set text to everything just
* after the start tag.
*/
let tag = text.split("\n")[0];
text = text.slice(1); // This moves the text 1 character so we dont match the same tag.
// Parse out the key and append to the list.
let key = getStartTagKey(tag);
if (key === null) {
key = "";
}
keys.push(key);
// Search again for another tag before looping.
startTagData = findStartTag(text);
}
return { numberOfTags: keys.length, keys };
}
/**
* This function will filter a set of strings, returning all items starting
* from the closest open start tag through the last item in the set.
*
* The function filters out all end tags to make sure that the start tag we
* find is the proper start tag for the list sent.
* @param linesAboveArray
* @returns
*/
function getStartBlockAboveLine(linesAboveArray) {
// Reduce the array down into a single string so that we can
// easily RegEx over the string and find the indicies we're looking for.
let linesAboveStr = linesAboveArray.reduce((prev, current) => {
return prev + "\n" + current;
}, "");
/*
* First thing we need to do is check if there are any end tags in the
* set of strings (which logically would close start tags and therefore
* the start tag it closes is not what we want). If there are we want to
* slowly narrow down our set of strings until the last end tag is
* removed. This makes it easier to find the closest open start tag
* in the data.
*/
let endTagSerachData = findEndTag(linesAboveStr);
while (endTagSerachData.found === true) {
// Get the index of where the first regex match in the
// string is. then we slice from 0 to index off of the string
// split it by newline, cut off the first line (which actually
// contains the regex) then reduce back down to a single string.
//
// TODO: This could be simplified if we just slice the text after
// the end tag instead of the begining.
let indexOfRegex = endTagSerachData.startPosition;
linesAboveArray = linesAboveStr.slice(indexOfRegex).split("\n").splice(1);
linesAboveStr = linesAboveArray.reduce((prev, current) => {
return prev + "\n" + current;
}, "");
endTagSerachData = findEndTag(linesAboveStr);
}
/**
* Now we have the set of lines after all other end tags. We now
* need to check if there is still a start tag left in the data. If
* there is no start tag then we want to return an empty array and empty
* key.
*/
let startBlockKey = "";
let startTagSearchData = findStartTag(linesAboveStr);
if (startTagSearchData.found === false) {
return null;
}
else {
/**
* Now we know there is at least 1 start key left, however there
* may be multiple start keys if the user is not closing their
* blocks. We currently dont allow recusive splitting so we
* want to get the last key in our remaining set. Same idea as
* above.
*/
while (startTagSearchData.found === true) {
// Get the index of where the first regex match in the
// string is. then we slice from 0 to index off of the string
// split it by newline, cut off the first line (which actually
// contains the regex) then reduce back down to a single string.
//
// TODO: This could be simplified if we just slice the text after
// the end tag instead of the begining.
let startIndex = startTagSearchData.startPosition;
linesAboveArray = linesAboveStr.slice(startIndex).split("\n");
let startTag = linesAboveArray[0];
let key = getStartTagKey(startTag);
if (key !== null) {
startBlockKey = key;
}
linesAboveArray = linesAboveArray.splice(1);
linesAboveStr = linesAboveArray.reduce((prev, current) => {
return prev + "\n" + current;
}, "");
startTagSearchData = findStartTag(linesAboveStr);
}
}
return { startBlockKey, linesAboveArray };
}
function getStartTagKey(startTag) {
let keySplit = startTag.split(":");
if (keySplit.length > 1) {
return keySplit[1].replace(" ", "");
}
return null;
}
/*
* Filename: multi-column-markdown/src/utilities/utils.ts
* Created Date: Tuesday, January 30th 2022, 4:02:19 pm
* Author: Cameron Robinson
*
* Copyright (c) 2022 Cameron Robinson
*/
function getUID(length = 10) {
if (length > 10) {
length = 10;
}
let UID = Math.random().toString(36).substring(2);
UID = UID.slice(0, length);
return UID;
}
/*
* Filename: multi-column-markdown/src/domObject.ts
* Created Date: Tuesday, February 1st 2022, 12:04:00 pm
* Author: Cameron Robinson
*
* Copyright (c) 2022 Cameron Robinson
*/
var DOMObjectTag;
(function (DOMObjectTag) {
DOMObjectTag[DOMObjectTag["none"] = 0] = "none";
DOMObjectTag[DOMObjectTag["startRegion"] = 1] = "startRegion";
DOMObjectTag[DOMObjectTag["regionSettings"] = 2] = "regionSettings";
DOMObjectTag[DOMObjectTag["columnBreak"] = 3] = "columnBreak";
DOMObjectTag[DOMObjectTag["endRegion"] = 4] = "endRegion";
})(DOMObjectTag || (DOMObjectTag = {}));
class DOMObject {
constructor(nodeKey, element, randomID = getUID(), tag = DOMObjectTag.none) {
this.nodeKey = nodeKey;
this.element = element;
this.UID = randomID;
this.tag = tag;
}
}
class DOMRegionSettingsObject extends DOMObject {
constructor(baseDOMObject, regionSettings) {
super(baseDOMObject.nodeKey, baseDOMObject.element, baseDOMObject.UID, DOMObjectTag.regionSettings);
this.regionSettings = regionSettings;
}
}
/*
* File: multi-column-markdown/src/domManager.ts
* Created Date: Saturday, January 30th 2022, 3:16:32 pm
* Author: Cameron Robinson
*
* Copyright (c) 2022 Cameron Robinson
*/
class GlobalDOMManager {
constructor() {
this.managers = new Map();
}
removeFileManagerCallback(key) {
if (this.managers.has(key) === true) {
this.managers.delete(key);
}
}
getFileManager(key) {
let fileManager = null;
if (this.managers.has(key) === true) {
fileManager = this.managers.get(key);
}
else {
fileManager = createFileDOMManager(this, key);
this.managers.set(key, fileManager);
}
return fileManager;
}
}
function createFileDOMManager(parentManager, fileKey) {
let regionMap = new Map();
let hasStartTag = false;
function removeRegion(regionKey) {
let regionManager = regionMap.get(regionKey);
if (regionManager) {
regionManager.displayOriginalElements();
}
regionMap.delete(regionKey);
if (regionMap.size === 0) {
parentManager.removeFileManagerCallback(fileKey);
}
}
function createRegionalManager(regionKey, errorElement, regionElement) {
//TODO: Use the error element whenever there is an error.
let regonalManager = createRegionalDomManager(this, regionKey, regionElement);
regionMap.set(regionKey, regonalManager);
return regonalManager;
}
function getRegionalManager(regionKey) {
let regonalManager = null;
if (regionMap.has(regionKey) === true) {
regonalManager = regionMap.get(regionKey);
}
return regonalManager;
}
function setHasStartTag() {
hasStartTag = true;
}
function getHasStartTag() {
return hasStartTag;
}
function getNumberOfRegions() {
return regionMap.size;
}
function checkKeyExists(checkKey) {
return regionMap.has(checkKey);
}
return { regionMap: regionMap,
hasStartTag: hasStartTag,
createRegionalManager: createRegionalManager,
getRegionalManager: getRegionalManager,
removeRegion: removeRegion,
setHasStartTag: setHasStartTag,
getHasStartTag: getHasStartTag,
getNumberOfRegions: getNumberOfRegions,
checkKeyExists: checkKeyExists
};
}
function createRegionalDomManager(fileManager, regionKey, startRegionElement) {
/**
* We use a list and a map to help keep track of the objects. Requires
* more memory but makes processing things a little cleaner and presumably
* faster.
*
* Use the map to look up object by key and the list is used to track objects
* in the order they are in the document.
*/
let domList = [];
let domObjectMap = new Map();
let regionParent = startRegionElement;
let regionSettings = [];
function addObject(siblingsAbove, obj) {
let addAtIndex = siblingsAbove.children.length;
// console.log("Attempting to add:", obj, `at index: ${addAtIndex}`);
domList.splice(addAtIndex, 0, obj);
domObjectMap.set(obj.UID, obj);
// /**
// * Make a copy of the list to log, only because
// * console log updates its references with updates in memory.
// */
// let x = domList.slice(0);
// console.log(x);
return addAtIndex;
}
function removeObject(objectUID) {
// /**
// * Make a copy of the list to log
// */
// let x = domList.slice(0);
// console.log(x);
// Get the object by key, remove it from the map and then
// from the list.
let obj = domObjectMap.get(objectUID);
domObjectMap.delete(objectUID);
if (domList.contains(obj)) {
domList.remove(obj);
}
// If the object is a settings object we need to remove from the
// settings list.
if (obj.tag === DOMObjectTag.regionSettings) {
let settingsObj = obj;
if (regionSettings.contains(settingsObj)) {
regionSettings.remove(settingsObj);
}
}
if (domList.length === 0) {
fileManager.removeRegion(regionKey);
}
// x = domList.slice(0);
// console.log(x);
}
function updateElementTag(objectUID, newTag) {
let obj = domObjectMap.get(objectUID);
let index = domList.indexOf(obj);
if (index !== -1) {
domList[index].tag = newTag;
}
}
function setElementToSettingsBlock(objectUID, settingsText) {
let obj = domObjectMap.get(objectUID);
let index = domList.indexOf(obj);
if (index !== -1) {
let settings = parseColumnSettings(settingsText);
let regionSettingsObj = new DOMRegionSettingsObject(domList[index], settings);
domObjectMap.set(regionSettingsObj.UID, regionSettingsObj);
domList[index] = regionSettingsObj;
regionSettings.push(regionSettingsObj);
}
}
/**
* Creates an object containing all necessary information for the region
* to be rendered to the preview pane.
*
* @returns a MultiColumnRenderData object with the root DOM element, settings object, and
* all child objects in the order they should be rendered.
*/
function getRegionRenderData() {
// Set defaults before attempting to get settings.
let settings = { numberOfColumns: 2, columnLayout: ColumnLayout.standard, drawBorder: true, drawShadow: true };
if (regionSettings.length > 0) {
/**
* Since we append settings onto the end of the array we want the last
* item in the array as that would be the most recent settings we parsed.
*/
settings = regionSettings[regionSettings.length - 1].regionSettings;
}
return {
parentRenderElement: regionParent,
parentRenderSettings: settings,
domObjects: domList
};
}
/**
* This fuction is called when a start tag is removed from view meaning
* our parent element storing the multi-column region is removed. It
* removes the CSS class from all of the elements so they will be
* re-rendered in the preview window.
*/
function displayOriginalElements() {
for (let i = 0; i < domList.length; i++) {
domList[i].element.removeClass("multiColumnDataHidden");
}
for (let i = 0; i < regionSettings.length; i++) {
regionSettings[i].element.removeClass("multiColumnDataHidden");
}
}
return { addObject: addObject,
removeObject: removeObject,
updateElementTag: updateElementTag,
setElementToSettingsBlock: setElementToSettingsBlock,
getRegionRenderData: getRegionRenderData,
displayOriginalElements: displayOriginalElements
};
}
/*
* File: multi-column-markdown/src/main.ts
* Created Date: Tuesday, October 5th 2021, 1:09 pm
* Author: Cameron Robinson
*
* Copyright (c) 2022 Cameron Robinson
*/
class MultiColumnMarkdown extends obsidian.Plugin {
constructor() {
// settings: SplitColumnMarkdownSettings;
super(...arguments);
this.globalManager = new GlobalDOMManager();
}
onload() {
return __awaiter(this, void 0, void 0, function* () {
console.log("Loading multi-column markdown");
this.setupMarkdownPostProcessor();
//TODO: Set up this as a modal to set settings automatically
this.addCommand({
id: `insert-multi-column-region`,
name: `Insert Multi-Column Region`,
editorCallback: (editor, view) => {
try {
editor.getDoc().replaceSelection(`
=== multi-column-start: ID_${getUID(4)}
\`\`\`column-settings
Number of Columns: 2
Largest Column: standard
\`\`\`
=== end-column ===
=== multi-column-end
${editor.getDoc().getSelection()}`);
}
catch (e) {
new obsidian.Notice("Encountered an error inserting a multi-column region. Please try again later.");
}
}
});
this.addCommand({
id: `add-IDs-To-multi-column-region`,
name: `Fix Missing IDs for Multi-Column Regions`,
editorCallback: (editor, view) => {
try {
/**
* Not sure if there is an easier way to do this.
*
* Get all of the lines of the document split by newlines.
*/
let lines = editor.getRange({ line: 0, ch: 0 }, { line: editor.getDoc().lineCount(), ch: 0 }).split("\n");
/**
* Loop through all of the lines checking if the line is a
* start tag and if so is it missing an ID.
*/
let linesWithoutIDs = [];
let textWithoutIDs = [];
for (let i = 0; i < lines.length; i++) {
let data = isStartTagWithID(lines[i]);
if (data.isStartTag === true && data.hasKey === false) {
linesWithoutIDs.push(i);
textWithoutIDs.push(lines[i]);
}
}
if (linesWithoutIDs.length === 0) {
new obsidian.Notice("Found 0 missing IDs in the current document.");
return;
}
/**
* Now loop through each line that is missing an ID and
* generate a random ID and replace the original text.
*/
for (let i = 0; i < linesWithoutIDs.length; i++) {
let originalText = textWithoutIDs[i];
let text = originalText;
text = text.trimEnd();
if (text.charAt(text.length - 1) === ":") {
text = text.slice(0, text.length - 1);
}
text = `${text}: ID_${getUID(4)}`;
editor.replaceRange(text, { line: linesWithoutIDs[i], ch: 0 }, { line: linesWithoutIDs[i], ch: originalText.length });
}
new obsidian.Notice(`Replaced ${linesWithoutIDs.length} missing ID(s) in the current document.`);
}
catch (e) {
new obsidian.Notice("Encountered an error addign IDs to multi-column regions. Please try again later.");
}
}
});
});
}
setupMarkdownPostProcessor() {
this.registerMarkdownPostProcessor((el, ctx) => __awaiter(this, void 0, void 0, function* () {
// Get the info for our current context and then check
// if the entire text contains a start tag. If there is
// no start tag in the document we can just return and
// ignore the rest of the parsing.
let info = ctx.getSectionInfo(el);
/**
* We need the context info to properly parse so returning here
* info is null. TODO: Set error in view if this occurs.
*/
if (!info) {
return;
}
const sourcePath = ctx.sourcePath;
let fileDOMManager = this.globalManager.getFileManager(sourcePath);
if (fileDOMManager === null) {
console.log("Found null DOM manager. Could not process multi-column markdown.");
return;
}
/**
* If we encounter a start tag on the document we set the flag to start
* parsing the rest of the document.
*/
if (containsStartTag(el.textContent)) {
fileDOMManager.setHasStartTag();
}
/**
* If the document does not contain any start tags we ignore the
* rest of the parsing. This is only set to true once the first
* start tag element is parsed above.
*/
if (fileDOMManager.getHasStartTag() === false) {
return;
}
/**
* Take the info provided and generate the required variables from
* the line start and end values.
*/
let linesOfElement = info.text.split("\n").splice(info.lineStart, (info.lineEnd + 1 - info.lineStart));
let elementTextSpaced = linesOfElement.reduce((prev, curr) => {
return prev + "\n" + curr;
});
let elementText = linesOfElement.reduce((prev, curr) => {
// TODO: This can probably be removed as it is only used to identify DOMObjects.
return prev + curr;
});
/**
* If the current line is a start tag we want to set up the
* region manager. The regional manager takes care
* of all items between it's start and end tags while the
* file manager we got above above takes care of all regional
* managers in each file.
*/
if (containsStartTag(el.textContent)) {
/**
* Set up the current element to act as the parent for the
* multi-column region.
*/
el.id = `TwoColumnContainer-${getUID()}`;
el.children[0].detach();
el.classList.add("multiColumnContainer");
let renderErrorRegion = el.createDiv({
cls: `multiColumnErrorMessage`,
});
let renderColumnRegion = el.createDiv({
cls: `RenderColRegion`
});
let startBlockData = getStartBlockAboveLine(linesOfElement);
let regionKey = startBlockData.startBlockKey;
if (fileDOMManager.checkKeyExists(regionKey) === true) {
let { numberOfTags, keys } = countStartTags(info.text);
let index = 0;
for (; index < numberOfTags; index++) {
// Because we checked if key exists one of these has to match.
if (keys[index] === regionKey) {
break;
}
}
if (keys[index] === "") {
renderErrorRegion.innerText = "Found multiple regions with empty IDs. Please set a unique ID after each start tag.\nEG: '=== multi-column-start: randomID'\nOr use 'Fix Missing IDs' in the command palette and reload the document.";
}
else {
renderErrorRegion.innerText = "Region ID already exists in document, please set a unique ID.\nEG: '=== multi-column-start: randomID'";
}
return;
}
let elementMarkdownRenderer = new obsidian.MarkdownRenderChild(el);
fileDOMManager.createRegionalManager(regionKey, renderErrorRegion, renderColumnRegion);
elementMarkdownRenderer.onunload = () => {
if (fileDOMManager) {
fileDOMManager.removeRegion(startBlockData.startBlockKey);
}
};
ctx.addChild(elementMarkdownRenderer);
/**
* Now we have created our regional manager and defined what elements
* need to be rendered into. So we can return without any more processing.
*/
return;
}
/**
* Get a list of all of the lines above our current element.
* We will use this to determine if the element is within a block so
* we can otherwise ignore the element and keep compute time down.
*/
let linesAboveArray = info.text.split("\n").slice(0, info.lineStart);
linesAboveArray.reduce((prev, curr) => {
return prev + "\n" + curr;
}, "");
/**
* A line above us contains a start tag now see if we're within that
* block.
*/
let startBockAbove = getStartBlockAboveLine(linesAboveArray);
if (startBockAbove == null) {
return;
}
/**
* Here we now know we're within a regional block.
*/
// Now we only want to work with the lines within the current region.
linesAboveArray = startBockAbove.linesAboveArray;
let regionalManager = fileDOMManager.getRegionalManager(startBockAbove.startBlockKey);
/**
* If we can not get the start block and this region's dom manager
* we can not continue something has gone wrong.
*/
if (regionalManager === null) {
return;
}
/**
* Now we take the lines above our current element up until the
* start region tag and render that into an HTML element. We will
* use this element to determine where to place our current element.
*/
let siblingsAbove = findSiblingsAboveEl(linesAboveArray, sourcePath);
/**
* Set up our dom object to be added to the manager.
*/
let currentObject = new DOMObject(elementText, el);
/**
* Now we add the object to the manager and then setup the
* callback for when the object is removed from view that will remove
* the item from the manager.
*/
regionalManager.addObject(siblingsAbove, currentObject);
let elementMarkdownRenderer = new obsidian.MarkdownRenderChild(el);
elementMarkdownRenderer.onunload = () => {
if (regionalManager) {
// We can attempt to update the view here after the item is removed
// but need to get the item's parent element before removing object from manager.
let regionRenderData = regionalManager.getRegionRenderData();
regionalManager.removeObject(currentObject.UID);
/**
* Need to check here if element is null as this closure will be called
* repeatedly on file change.
*/
if (regionRenderData.parentRenderElement === null) {
return;
}
this.renderColumnMarkdown(regionRenderData.parentRenderElement, regionRenderData.domObjects, regionRenderData.parentRenderSettings);
}
};
ctx.addChild(elementMarkdownRenderer);
/**
* Now we check if our current element is a special flag so we can
* properly set the element tag within the regional manager.
*/
if (containsEndTag(el.textContent) === true) {
regionalManager.updateElementTag(currentObject.UID, DOMObjectTag.endRegion);
}
else if (containsColEndTag(elementTextSpaced) === true) {
regionalManager.updateElementTag(currentObject.UID, DOMObjectTag.columnBreak);
}
else if (containsColSettingsTag(elementTextSpaced) === true) {
regionalManager.setElementToSettingsBlock(currentObject.UID, elementTextSpaced);
}
/**
* Use our regional manager to get everything needed to render the region.
*/
let parentElementData = regionalManager.getRegionRenderData();
/**
* We want to hide all of the original elements that are now going to be
* rendered within our mutli-column region
*/
el.addClass("multiColumnDataHidden");
this.renderColumnMarkdown(parentElementData.parentRenderElement, parentElementData.domObjects, parentElementData.parentRenderSettings);
return;
}));
}
/**
* This function takes in the data for the multi-column region and sets up the
* user defined number of children with the proper css classes to be rendered properly.
*
* @param parentElement The element that the multi-column region will be rendered under.
* @param regionElements The list of DOM objects that will be coppied under the parent object
* @param settings The settings the user has defined for the region.
*/
renderColumnMarkdown(parentElement, regionElements, settings) {
let multiColumnParent = createDiv({
cls: `multiColumnParent rowC`,
});
if (settings.drawShadow === true) {
multiColumnParent.addClass("multiColumnParentShadow");
}
/**
* Pass our parent div and settings to parser to create the required
* column divs as children of the parent.
*/
let columnContentDivs = getColumnContentDivs(settings, multiColumnParent);
for (let i = 0; i < columnContentDivs.length; i++) {
if (settings.drawBorder === true) {
columnContentDivs[i].addClass("columnBorder");
}
if (settings.drawShadow === true) {
columnContentDivs[i].addClass("columnShadow");
}
}
// Create markdown renderer to parse the passed markdown
// between the tags.
let markdownRenderChild = new obsidian.MarkdownRenderChild(multiColumnParent);
// Remove every other child from the parent so
// we dont end up with multiple sets of data. This should
// really only need to loop once for i = 0 but loop just
// in case.
for (let i = parentElement.children.length - 1; i >= 0; i--) {
parentElement.children[i].detach();
}
parentElement.appendChild(markdownRenderChild.containerEl);
let columnIndex = 0;
for (let i = 0; i < regionElements.length; i++) {
// We want to skip column break tags but only if we have columns
// left to enter data for.
if (regionElements[i].tag === DOMObjectTag.columnBreak &&
(columnIndex + 1) < settings.numberOfColumns) {
columnIndex++;
}
else if (regionElements[i].tag !== DOMObjectTag.startRegion &&
regionElements[i].tag !== DOMObjectTag.endRegion &&
regionElements[i].tag !== DOMObjectTag.regionSettings) {
/**
* Make a deep copy of the element so we can remove the hidden class before
* appending to our column div.
*/
let clonedElement = regionElements[i].element.cloneNode(true);
clonedElement.removeClass("multiColumnDataHidden");
clonedElement.style.display = "block";
columnContentDivs[columnIndex].appendChild(clonedElement);
}
}
}
}
/**
* Sets up the CSS classes and the number of columns based on the passed settings.
* @param settings The user defined settings that determine what CSS is set here.
* @param multiColumnParent The parent object that the column divs will be created under.
* @returns The list of column divs created under the passed parent element.
*/
function getColumnContentDivs(settings, multiColumnParent) {
let columnContentDivs = [];
if (settings.numberOfColumns === 2) {
switch (settings.columnLayout) {
case (ColumnLayout.standard):
case (ColumnLayout.middle):
case (ColumnLayout.center):
case (ColumnLayout.third):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent twoEqualColumns_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent twoEqualColumns_Right`
}));
break;
case (ColumnLayout.left):
case (ColumnLayout.first):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent twoColumnsHeavyLeft_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent twoColumnsHeavyLeft_Right`
}));
break;
case (ColumnLayout.right):
case (ColumnLayout.second):
case (ColumnLayout.last):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent twoColumnsHeavyRight_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent twoColumnsHeavyRight_Right`
}));
break;
}
}
else if (settings.numberOfColumns === 3) {
switch (settings.columnLayout) {
case (ColumnLayout.standard):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threeEqualColumns_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threeEqualColumns_Middle`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threeEqualColumns_Right`
}));
break;
case (ColumnLayout.left):
case (ColumnLayout.first):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyLeft_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyLeft_Middle`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyLeft_Right`
}));
break;
case (ColumnLayout.middle):
case (ColumnLayout.center):
case (ColumnLayout.second):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyMiddle_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyMiddle_Middle`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyMiddle_Right`
}));
break;
case (ColumnLayout.right):
case (ColumnLayout.third):
case (ColumnLayout.last):
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyRight_Left`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyRight_Middle`
}));
columnContentDivs.push(multiColumnParent.createDiv({
cls: `columnContent threColumnsHeavyRight_Right`
}));
break;
}
}
return columnContentDivs;
}
function findSiblingsAboveEl(linesAbove, sourcePath) {
/**
* We re-render all of the items above our element, until the start tag,
* so we can determine where to place the new item in the manager.
*
* TODO: Can reduce the amount needing to be rendered by only rendering to
* the start tag or a column-break whichever is closer.
*/
let siblingsAbove = createDiv();
let markdownRenderChild = new obsidian.MarkdownRenderChild(siblingsAbove);
obsidian.MarkdownRenderer.renderMarkdown(linesAbove.reduce((prev, current) => {
return prev + "\n" + current;
}, ""), siblingsAbove, sourcePath, markdownRenderChild);
return siblingsAbove;
}
module.exports = MultiColumnMarkdown;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInNyYy9yZWdpb25TZXR0aW5ncy50cyIsInNyYy91dGlsaXRpZXMvdGV4dFBhcnNlci50cyIsInNyYy91dGlsaXRpZXMvdXRpbHMudHMiLCJzcmMvZG9tX21hbmFnZXIvZG9tT2JqZWN0LnRzIiwic3JjL2RvbV9tYW5hZ2VyL2RvbU1hbmFnZXIudHMiLCJzcmMvbWFpbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiEgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uXHJcblxyXG5QZXJtaXNzaW9uIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBhbmQvb3IgZGlzdHJpYnV0ZSB0aGlzIHNvZnR3YXJlIGZvciBhbnlcclxucHVycG9zZSB3aXRoIG9yIHdpdGhvdXQgZmVlIGlzIGhlcmVieSBncmFudGVkLlxyXG5cclxuVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiBBTkQgVEhFIEFVVEhPUiBESVNDTEFJTVMgQUxMIFdBUlJBTlRJRVMgV0lUSFxyXG5SRUdBUkQgVE8gVEhJUyBTT0ZUV0FSRSBJTkNMVURJTkcgQUxMIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFlcclxuQU5EIEZJVE5FU1MuIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1IgQkUgTElBQkxFIEZPUiBBTlkgU1BFQ0lBTCwgRElSRUNULFxyXG5JTkRJUkVDVCwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIE9SIEFOWSBEQU1BR0VTIFdIQVRTT0VWRVIgUkVTVUxUSU5HIEZST01cclxuTE9TUyBPRiBVU0UsIERBVEEgT1IgUFJPRklUUywgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIE5FR0xJR0VOQ0UgT1JcclxuT1RIRVIgVE9SVElPVVMgQUNUSU9OLCBBUklTSU5HIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFVTRSBPUlxyXG5QRVJGT1JNQU5DRSBPRiBUSElTIFNPRlRXQVJFLlxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAqL1xyXG4vKiBnbG9iYWwgUmVmbGVjdCwgUHJvbWlzZSAqL1xyXG5cclxudmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbihkLCBiKSB7XHJcbiAgICBleHRlbmRTdGF0aWNzID0gT2JqZWN0LnNldFByb3RvdHlwZU9mIHx8XHJcbiAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fFxyXG4gICAgICAgIGZ1bmN0aW9uIChkLCBiKSB7IGZvciAodmFyIHAgaW4gYikgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChiLCBwKSkgZFtwXSA9IGJbcF07IH07XHJcbiAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4dGVuZHMoZCwgYikge1xyXG4gICAgaWYgKHR5cGVvZiBiICE9PSBcImZ1bmN0aW9uXCIgJiYgYiAhPT0gbnVsbClcclxuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSBcIiArIFN0cmluZyhiKSArIFwiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGxcIik7XHJcbiAgICBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG4gICAgZnVuY3Rpb24gX18oKSB7IHRoaXMuY29uc3RydWN0b3IgPSBkOyB9XHJcbiAgICBkLnByb3RvdHlwZSA9IGIgPT09IG51bGwgPyBPYmplY3QuY3JlYXRlKGIpIDogKF9fLnByb3RvdHlwZSA9IGIucHJvdG90eXBlLCBuZXcgX18oKSk7XHJcbn1cclxuXHJcbmV4cG9ydCB2YXIgX19hc3NpZ24gPSBmdW5jdGlvbigpIHtcclxuICAgIF9fYXNzaWduID0gT2JqZWN0LmFzc2lnbiB8fCBmdW5jdGlvbiBfX2Fzc2lnbih0KSB7XHJcbiAgICAgICAgZm9yICh2YXIgcywgaSA9IDEsIG4gPSBhcmd1bWVudHMubGVuZ3RoOyBpIDwgbjsgaSsrKSB7XHJcbiAgICAgICAgICAgIHMgPSBhcmd1bWVudHNbaV07XHJcbiAgICAgICAgICAgIGZvciAodmFyIHAgaW4gcykgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzLCBwKSkgdFtwXSA9IHNbcF07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0O1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIF9fYXNzaWduLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3Jlc3QocywgZSkge1xyXG4gICAgdmFyIHQgPSB7fTtcclxuICAgIGZvciAodmFyIHAgaW4gcykgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzLCBwKSAmJiBlLmluZGV4T2YocCkgPCAwKVxyXG4gICAgICAgIHRbcF0gPSBzW3BdO1xyXG4gICAgaWYgKHMgIT0gbnVsbCAmJiB0eXBlb2YgT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyA9PT0gXCJmdW5jdGlvblwiKVxyXG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBwID0gT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhzKTsgaSA8IHAubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgICAgaWYgKGUuaW5kZXhPZihwW2ldKSA8IDAgJiYgT2JqZWN0LnByb3RvdHlwZS5wcm9wZXJ0eUlzRW51bWVyYWJsZS5jYWxsKHMsIHBbaV0pKVxyXG4gICAgICAgICAgICAgICAgdFtwW2ldXSA9IHNbcFtpXV07XHJcbiAgICAgICAgfVxyXG4gICAgcmV0dXJuIHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2RlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKSB7XHJcbiAgICB2YXIgYyA9IGFyZ3VtZW50cy5sZW5ndGgsIHIgPSBjIDwgMyA/IHRhcmdldCA6IGRlc2MgPT09IG51bGwgPyBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIGtleSkgOiBkZXNjLCBkO1xyXG4gICAgaWYgKHR5cGVvZiBSZWZsZWN0ID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiBSZWZsZWN0LmRlY29yYXRlID09PSBcImZ1bmN0aW9uXCIpIHIgPSBSZWZ