705 lines
22 KiB
Vue
705 lines
22 KiB
Vue
<template>
|
|
<div>
|
|
<template v-if="readOnly">
|
|
<div>
|
|
<v-btn depressed tile @click="switchView()">
|
|
Wiki<v-icon right>{{ switchViewIcon() }}</v-icon></v-btn
|
|
>
|
|
</div>
|
|
</template>
|
|
<template v-else>
|
|
<span class="v-label v-label--active theme--light">
|
|
Wiki
|
|
</span>
|
|
<div class="mt-2">
|
|
<v-btn-toggle v-model="currentView">
|
|
<v-btn color="white" :value="view.HIDDEN_VIEW">
|
|
<v-icon>fa-eye-slash</v-icon>
|
|
</v-btn>
|
|
<v-btn color="white" :value="view.WIKI_VIEW">
|
|
<v-icon>fa-eye</v-icon>
|
|
</v-btn>
|
|
<v-btn color="white" :value="view.DESIGN_VIEW">
|
|
<v-icon>fa-edit</v-icon>
|
|
</v-btn>
|
|
<v-btn color="white" :value="view.SPLIT_VIEW">
|
|
<v-icon>fa-columns</v-icon>
|
|
</v-btn>
|
|
</v-btn-toggle>
|
|
</div>
|
|
</template>
|
|
<v-sheet
|
|
v-if="currentView != this.view.HIDDEN_VIEW"
|
|
elevation="2"
|
|
class="aywiki pa-2 pa-sm-6 mt-2"
|
|
>
|
|
<v-row v-resize="onResize">
|
|
<!-- BUTTONS -->
|
|
<v-col v-if="showDesigner()" :cols="12">
|
|
<div>
|
|
<v-btn depressed tile @click="clickBold">
|
|
<v-icon>fa-bold</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickItalic">
|
|
<v-icon>fa-italic</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickStrike">
|
|
<v-icon>fa-strikethrough</v-icon></v-btn
|
|
>
|
|
|
|
<v-menu offset-y>
|
|
<template v-slot:activator="{ on }">
|
|
<v-btn depressed tile v-on="on">
|
|
<v-icon>fa-heading</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
<v-list>
|
|
<v-list-item @click="clickHeading(1)">
|
|
<v-list-item-title
|
|
><h1>{{ $ay.t("Heading") }} 1</h1></v-list-item-title
|
|
>
|
|
</v-list-item>
|
|
<v-list-item @click="clickHeading(2)">
|
|
<v-list-item-title
|
|
><h2>{{ $ay.t("Heading") }} 2</h2></v-list-item-title
|
|
>
|
|
</v-list-item>
|
|
<v-list-item @click="clickHeading(3)">
|
|
<v-list-item-title
|
|
><h3>{{ $ay.t("Heading") }} 3</h3></v-list-item-title
|
|
>
|
|
</v-list-item>
|
|
<v-list-item @click="clickHeading(4)">
|
|
<v-list-item-title
|
|
><h4>{{ $ay.t("Heading") }} 4</h4></v-list-item-title
|
|
>
|
|
</v-list-item>
|
|
<v-list-item @click="clickHeading(5)">
|
|
<v-list-item-title
|
|
><h5>{{ $ay.t("Heading") }} 5</h5></v-list-item-title
|
|
>
|
|
</v-list-item>
|
|
<v-list-item @click="clickHeading(6)">
|
|
<v-list-item-title
|
|
><h6>{{ $ay.t("Heading") }} 6</h6></v-list-item-title
|
|
>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-menu>
|
|
|
|
<v-btn depressed tile class="ml-1" @click="clickLine">
|
|
<v-icon>fa-minus</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickQuote">
|
|
<v-icon>fa-quote-left</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickUl">
|
|
<v-icon>fa-list-ul</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickOl">
|
|
<v-icon>fa-list-ol</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickTask">
|
|
<v-icon>fa-check-square</v-icon></v-btn
|
|
>
|
|
|
|
<v-menu
|
|
v-model="tableMenu"
|
|
:close-on-content-click="false"
|
|
offset-y
|
|
>
|
|
<template v-slot:activator="{ on }">
|
|
<v-btn depressed tile class="ml-1" v-on="on">
|
|
<v-icon>fa-table</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
<v-card width="300">
|
|
<v-card-title>{{ $ay.t("Table") }}</v-card-title>
|
|
|
|
<div class="ma-8">
|
|
<v-slider
|
|
thumb-size="24"
|
|
thumb-label="always"
|
|
v-model="tableMenuColumns"
|
|
min="1"
|
|
max="10"
|
|
prepend-icon="fa-arrows-alt-h"
|
|
></v-slider>
|
|
|
|
<v-slider
|
|
prepend-icon="fa-arrows-alt-v"
|
|
class="mt-8"
|
|
thumb-size="24"
|
|
thumb-label="always"
|
|
v-model="tableMenuRows"
|
|
min="1"
|
|
max="15"
|
|
></v-slider>
|
|
</div>
|
|
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
|
|
<v-btn text @click="tableMenu = false">{{
|
|
$ay.t("Cancel")
|
|
}}</v-btn>
|
|
<v-btn color="primary" text @click="clickTable">{{
|
|
$ay.t("OK")
|
|
}}</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-menu>
|
|
<v-btn depressed tile @click="openLinkMenu">
|
|
<v-icon>fa-link</v-icon>
|
|
</v-btn>
|
|
<v-menu
|
|
min-width="300"
|
|
v-model="linkMenu"
|
|
:close-on-content-click="false"
|
|
offset-y
|
|
:position-x="linkMenuX"
|
|
:position-y="linkMenuY"
|
|
absolute
|
|
>
|
|
<v-card width="300">
|
|
<v-card-title>{{ $ay.t("InsertLink") }}</v-card-title>
|
|
|
|
<div class="ma-8">
|
|
<v-text-field
|
|
v-model="linkUrl"
|
|
:label="$ay.t('LinkUrl')"
|
|
></v-text-field>
|
|
<v-text-field
|
|
v-model="linkText"
|
|
:label="$ay.t('LinkText')"
|
|
></v-text-field>
|
|
</div>
|
|
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
|
|
<v-btn text @click="linkMenu = false">{{
|
|
$ay.t("Cancel")
|
|
}}</v-btn>
|
|
<v-btn color="primary" text @click="clickLink">{{
|
|
$ay.t("OK")
|
|
}}</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-menu>
|
|
|
|
<v-btn depressed tile> <v-icon>fa-image</v-icon></v-btn>
|
|
<v-btn depressed tile class="ml-1" @click="clickCode">
|
|
<v-icon>fa-code</v-icon></v-btn
|
|
>
|
|
<v-btn depressed tile @click="clickCodeBlock">
|
|
<v-icon>fa-square-full</v-icon></v-btn
|
|
>
|
|
</div>
|
|
</v-col>
|
|
<!-- DESIGNER -->
|
|
<v-col
|
|
v-if="showDesigner()"
|
|
:cols="currentView == view.SPLIT_VIEW ? 6 : 12"
|
|
>
|
|
<div :style="editStyle()">
|
|
<v-textarea
|
|
solo
|
|
no-resize
|
|
:height="editAreaHeight"
|
|
ref="editArea"
|
|
@input="handleInput"
|
|
@dblclick="handleDoubleClick"
|
|
v-model="localVal"
|
|
></v-textarea>
|
|
</div>
|
|
</v-col>
|
|
<!-- WIKI auto-grow-->
|
|
<v-col
|
|
v-if="showWiki()"
|
|
:cols="currentView == view.SPLIT_VIEW ? 6 : 12"
|
|
>
|
|
<div
|
|
:style="wikiStyle()"
|
|
class="aywiki"
|
|
v-html="compiledOutput()"
|
|
></div>
|
|
</v-col>
|
|
</v-row>
|
|
</v-sheet>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
/*
|
|
This code written in Mid April 2020 about 4 weeks into Covid-19 self isolation
|
|
It's just starting to look like BC has "flattened the curve", but other places are still a shit-show
|
|
*/
|
|
import marked from "marked";
|
|
import DOMPurify from "dompurify";
|
|
|
|
export default {
|
|
created() {
|
|
|
|
// Add a hook to make all links open a new window
|
|
DOMPurify.addHook("afterSanitizeAttributes", function(node) {
|
|
// set all elements owning target to target=_blank
|
|
if ("target" in node) {
|
|
node.setAttribute("target", "_blank");
|
|
// prevent https://www.owasp.org/index.php/Reverse_Tabnabbing
|
|
node.setAttribute("rel", "noopener noreferrer");
|
|
}
|
|
// set non-HTML/MathML links to xlink:show=new
|
|
if (
|
|
!node.hasAttribute("target") &&
|
|
(node.hasAttribute("xlink:href") || node.hasAttribute("href"))
|
|
) {
|
|
node.setAttribute("xlink:show", "new");
|
|
}
|
|
});
|
|
},
|
|
data() {
|
|
return {
|
|
localVal: this.value,
|
|
currentView: 0,
|
|
selection: {
|
|
start: 0,
|
|
end: 0,
|
|
startOfBlock: 0, //block meaning first character after last linefeed before or at start of selection
|
|
endOfBlock: 0, //end of block meaning selection expanded to end of line (unless there isn't one)
|
|
hasSelection: false
|
|
},
|
|
editAreaHeight: 300,
|
|
view: {
|
|
HIDDEN_VIEW: 0,
|
|
SPLIT_VIEW: 1,
|
|
WIKI_VIEW: 2,
|
|
DESIGN_VIEW: 3
|
|
},
|
|
tableMenu: false,
|
|
tableMenuColumns: 2,
|
|
tableMenuRows: 2,
|
|
linkMenu: false,
|
|
linkMenuX: 10,
|
|
linkMenuY: 10,
|
|
linkUrl: "",
|
|
linkText: ""
|
|
};
|
|
},
|
|
props: {
|
|
value: String,
|
|
readOnly: Boolean
|
|
},
|
|
watch: {
|
|
value(value) {
|
|
this.localVal = value;
|
|
}
|
|
},
|
|
methods: {
|
|
compiledOutput() {
|
|
if (this.localVal.length == 0) {
|
|
return "";
|
|
}
|
|
return DOMPurify.sanitize(marked(this.localVal, { breaks: true }));
|
|
},
|
|
onResize() {
|
|
// this.editAreaHeight = window.innerHeight / 2;
|
|
this.editAreaHeight = window.innerHeight * 0.8;
|
|
},
|
|
editStyle() {
|
|
if (this.currentView == this.view.SPLIT_VIEW) {
|
|
return "height: " + this.editAreaHeight + "px;";
|
|
} else {
|
|
return false; //false attributes don't get rendered
|
|
}
|
|
},
|
|
wikiStyle() {
|
|
if (this.currentView == this.view.SPLIT_VIEW) {
|
|
return "height: " + this.editAreaHeight + "px;overflow-y:auto;";
|
|
} else {
|
|
return false; //false attributes don't get rendered
|
|
}
|
|
},
|
|
getSelectedRange(forceBlock) {
|
|
let bodyTextArea = this.$refs.editArea.$el.querySelector("textarea");
|
|
this.selection.start = bodyTextArea.selectionStart;
|
|
this.selection.end = bodyTextArea.selectionEnd;
|
|
|
|
//some edits only work on a block so if user is just clicked on a line then add make a selection
|
|
if (forceBlock) {
|
|
//add a character to selection forward if possible but if not then go backward one
|
|
if (this.selection.end < this.localVal.length) {
|
|
this.selection.end++;
|
|
} else {
|
|
if (this.selection.start > 0) {
|
|
this.selection.start--;
|
|
}
|
|
}
|
|
}
|
|
this.selection.hasSelection = this.selection.start != this.selection.end;
|
|
|
|
//block selection
|
|
//start of block...
|
|
//default
|
|
this.selection.startOfBlock = this.selection.start;
|
|
if (this.selection.start > 0) {
|
|
//find linefeed prior to current start
|
|
let indexOfLineFeed = this.localVal.lastIndexOf(
|
|
"\n",
|
|
this.selection.start
|
|
);
|
|
if (indexOfLineFeed != -1) {
|
|
this.selection.startOfBlock = indexOfLineFeed + 1;
|
|
} else {
|
|
//this may be wrong but if it's the first line then I think the block should be the first character
|
|
this.selection.startOfBlock = 0;
|
|
}
|
|
}
|
|
|
|
//end of block
|
|
//default
|
|
this.selection.endOfBlock = this.selection.end;
|
|
if (this.selection.end > 0) {
|
|
//find linefeed prior to current start
|
|
let indexOfLineFeed = this.localVal.indexOf("\n", this.selection.end);
|
|
if (indexOfLineFeed != -1) {
|
|
this.selection.endOfBlock = indexOfLineFeed;
|
|
}
|
|
}
|
|
},
|
|
setSelectedRange(start, end) {
|
|
let bodyTextArea = this.$refs.editArea.$el.querySelector("textarea");
|
|
bodyTextArea.setSelectionRange(start, end);
|
|
},
|
|
getSelectedText() {
|
|
let selectedText = "";
|
|
if (this.selection.hasSelection) {
|
|
selectedText = this.localVal.substring(
|
|
this.selection.start,
|
|
this.selection.end
|
|
);
|
|
}
|
|
return selectedText;
|
|
},
|
|
replaceSelectedText(newString) {
|
|
this.localVal = window.$gz.util.stringSplice(
|
|
this.localVal,
|
|
this.selection.start,
|
|
this.selection.end - this.selection.start,
|
|
newString
|
|
);
|
|
},
|
|
getSelectedBlock() {
|
|
let selectedText = "";
|
|
if (this.selection.hasSelection) {
|
|
selectedText = this.localVal.substring(
|
|
this.selection.startOfBlock,
|
|
this.selection.endOfBlock
|
|
);
|
|
}
|
|
return selectedText; //.trim();
|
|
},
|
|
replaceSelectedBlock(newString) {
|
|
this.localVal = window.$gz.util.stringSplice(
|
|
this.localVal,
|
|
this.selection.startOfBlock,
|
|
this.selection.endOfBlock - this.selection.startOfBlock,
|
|
newString
|
|
);
|
|
},
|
|
handleDoubleClick(i) {
|
|
//the purpose of this is only to change the selection if it's got an extra space to the right
|
|
//because double clicking on a word with another word after it causes the space to be included
|
|
this.getSelectedRange();
|
|
let temp = this.getSelectedText();
|
|
let tempTrimmed = temp.trimEnd();
|
|
let diff = temp.length - tempTrimmed.length;
|
|
if (diff != 0) {
|
|
//there were some spaces so update the selection range
|
|
//force selection to be shorter by diff
|
|
this.setSelectedRange(this.selection.start, this.selection.end - diff);
|
|
}
|
|
},
|
|
handleInput(val) {
|
|
debugger;
|
|
this.$emit("input", val);
|
|
this.localVal = val;
|
|
},
|
|
switchViewIcon() {
|
|
//return the icon that indicates what it will change to if you click it
|
|
//mirror of switchview below
|
|
|
|
if (this.readOnly) {
|
|
if (this.currentView == this.view.HIDDEN_VIEW) {
|
|
return "fa-eye";
|
|
} else {
|
|
return "fa-eye-slash";
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (this.currentView) {
|
|
case this.view.HIDDEN_VIEW:
|
|
return "fa-eye";
|
|
case this.view.WIKI_VIEW:
|
|
return "fa-columns";
|
|
case this.view.SPLIT_VIEW:
|
|
return "fa-feather";
|
|
case this.view.DESIGN_VIEW:
|
|
return "fa-eye";
|
|
}
|
|
},
|
|
showWiki() {
|
|
return (
|
|
this.currentView == this.view.WIKI_VIEW ||
|
|
this.currentView == this.view.SPLIT_VIEW
|
|
);
|
|
},
|
|
showDesigner() {
|
|
return (
|
|
this.currentView == this.view.DESIGN_VIEW ||
|
|
this.currentView == this.view.SPLIT_VIEW
|
|
);
|
|
},
|
|
switchView() {
|
|
//if user can't edit then cycle between hidden and wiki view
|
|
if (this.readOnly) {
|
|
if (this.currentView == this.view.HIDDEN_VIEW) {
|
|
this.currentView = this.view.WIKI_VIEW;
|
|
} else {
|
|
this.currentView = this.view.HIDDEN_VIEW;
|
|
}
|
|
return;
|
|
}
|
|
//user can edit so switch OUT of hidden view but never into it
|
|
//and cycle between design, split and wiki views only
|
|
//never goes into hidden
|
|
switch (this.currentView) {
|
|
case this.view.HIDDEN_VIEW:
|
|
this.currentView = this.view.WIKI_VIEW;
|
|
break;
|
|
case this.view.WIKI_VIEW:
|
|
this.currentView = this.view.SPLIT_VIEW;
|
|
break;
|
|
case this.view.SPLIT_VIEW:
|
|
this.currentView = this.view.DESIGN_VIEW;
|
|
break;
|
|
case this.view.DESIGN_VIEW:
|
|
this.currentView = this.view.WIKI_VIEW;
|
|
break;
|
|
}
|
|
},
|
|
visibleIcon() {
|
|
return this.wikiVisible ? "fa-eye-slash" : "fa-eye";
|
|
},
|
|
|
|
clickBold() {
|
|
this.getSelectedRange();
|
|
this.replaceSelectedText("**" + this.getSelectedText() + "**");
|
|
},
|
|
clickItalic() {
|
|
this.getSelectedRange();
|
|
this.replaceSelectedText("*" + this.getSelectedText() + "*");
|
|
},
|
|
clickStrike() {
|
|
this.getSelectedRange();
|
|
this.replaceSelectedText("~~" + this.getSelectedText() + "~~");
|
|
},
|
|
clickHeading(h) {
|
|
this.getSelectedRange(true);
|
|
let prepend = "#".repeat(h) + " ";
|
|
// if (this.selection.hasSelection) {
|
|
let s = this.getSelectedBlock();
|
|
|
|
s = s.replace(/\n/gi, "\n" + prepend);
|
|
if (s.length > 0 && s[0] != "\n") {
|
|
s = prepend + s;
|
|
}
|
|
|
|
this.replaceSelectedBlock(s);
|
|
// } else {
|
|
// this.replaceSelectedText("\n" + prepend);
|
|
// }
|
|
},
|
|
clickLine() {
|
|
this.getSelectedRange();
|
|
this.replaceSelectedText("***");
|
|
},
|
|
clickCode() {
|
|
this.getSelectedRange();
|
|
this.replaceSelectedText("`" + this.getSelectedText() + "`");
|
|
},
|
|
clickCodeBlock() {
|
|
this.getSelectedRange();
|
|
this.replaceSelectedBlock(
|
|
"\n```\n" + this.getSelectedBlock() + "\n```\n"
|
|
);
|
|
},
|
|
|
|
clickQuote() {
|
|
this.getSelectedRange();
|
|
if (this.selection.hasSelection) {
|
|
this.replaceSelectedBlock("\n>" + this.getSelectedBlock() + "\n");
|
|
} else {
|
|
this.replaceSelectedText("\n>");
|
|
}
|
|
},
|
|
clickUl() {
|
|
//if a selected block with several lines then prepend each line
|
|
//if a selected block with one line only then prepend start of first line after last linefeed
|
|
this.getSelectedRange();
|
|
if (this.selection.hasSelection) {
|
|
let s = this.getSelectedBlock();
|
|
s = s.replace(/\n/gi, "\n* ");
|
|
if (s.length > 0 && s[0] != "\n") {
|
|
s = "* " + s;
|
|
}
|
|
s = s + "\n";
|
|
this.replaceSelectedBlock(s);
|
|
} else {
|
|
this.replaceSelectedText("\n* ");
|
|
}
|
|
},
|
|
clickOl() {
|
|
//if a selected block with several lines then prepend each line
|
|
//if a selected block with one line only then prepend start of first line after last linefeed
|
|
this.getSelectedRange();
|
|
if (this.selection.hasSelection) {
|
|
let s = this.getSelectedBlock();
|
|
// console.log("Selected block:[" + s + "]");
|
|
let ret = "\n1. ";
|
|
let listItem = 1;
|
|
for (let i = 0; i < s.length; i++) {
|
|
if (s[i] == "\n") {
|
|
++listItem;
|
|
ret += "\n" + listItem + ". ";
|
|
} else {
|
|
ret += s[i];
|
|
}
|
|
}
|
|
ret += "\n\n";
|
|
// console.log("RET:[" + ret + "]");
|
|
this.replaceSelectedBlock(ret);
|
|
} else {
|
|
this.replaceSelectedText("\n1. ");
|
|
}
|
|
},
|
|
clickTask() {
|
|
this.getSelectedRange();
|
|
if (this.selection.hasSelection) {
|
|
let s = this.getSelectedBlock();
|
|
s = s.replace(/\n/gi, "\n* [ ] ");
|
|
if (s.length > 0 && s[0] != "\n") {
|
|
s = "* [ ] " + s;
|
|
}
|
|
s = s + "\n";
|
|
this.replaceSelectedBlock(s);
|
|
} else {
|
|
this.replaceSelectedText("\n* [ ] ");
|
|
}
|
|
},
|
|
clickTable() {
|
|
this.tableMenu = false;
|
|
//| Column 1 | Column 2 | Column 3 |
|
|
//| -------- | -------- | -------- |
|
|
//| John | Doe | Male |
|
|
//| Mary | Smith | Female |
|
|
this.getSelectedRange();
|
|
|
|
let t = "\n";
|
|
|
|
//Header
|
|
//| CCC | CCC | CCC |
|
|
for (let c = 0; c < this.tableMenuColumns; c++) {
|
|
t += "| CCC ";
|
|
}
|
|
t += "|\n";
|
|
|
|
//Divider
|
|
//| --- | --- | --- |
|
|
for (let c = 0; c < this.tableMenuColumns; c++) {
|
|
t += "| --- ";
|
|
}
|
|
t += "|\n";
|
|
|
|
//Rows
|
|
//| XXX | XXX | XXX |
|
|
for (let r = 0; r < this.tableMenuRows; r++) {
|
|
for (let c = 0; c < this.tableMenuColumns; c++) {
|
|
t += "| XXX ";
|
|
}
|
|
t += "|\n";
|
|
}
|
|
|
|
this.replaceSelectedText(t);
|
|
},
|
|
openLinkMenu(e) {
|
|
e.preventDefault();
|
|
this.linkMenu = false;
|
|
this.getSelectedRange();
|
|
this.linkText = this.getSelectedText();
|
|
this.linkMenuX = e.clientX;
|
|
this.linkMenuY = e.clientY;
|
|
this.$nextTick(() => {
|
|
this.linkMenu = true;
|
|
});
|
|
},
|
|
clickLink() {
|
|
this.linkMenu = false;
|
|
this.getSelectedRange();
|
|
|
|
//this.linkUrl
|
|
//this.linkText
|
|
let url = this.linkUrl;
|
|
//force it to an external url
|
|
if (!url.includes(":")) {
|
|
url = "https://" + url;
|
|
}
|
|
let t = "[" + this.linkText + "](" + url + ")";
|
|
// [MY Awesome LINK](www.ayanova.com)
|
|
|
|
this.replaceSelectedText(t);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
|
|
|
|
|
|
todo: all editing controls enabled
|
|
- add image and link
|
|
- determine how local images will be handled (attachments)
|
|
baseurl setting for local images: https://marked.js.org/#/USING_ADVANCED.md#options
|
|
|
|
|
|
todo: replace all redundant clickXXX functions with a single function and a flag indicating what to do
|
|
todo: Undo / redo (ctrl-z / ctrl-y) ability?
|
|
- may need a library for this one if it's tricky to support levels of undo
|
|
|
|
|
|
|
|
todo: Add wikiContent field to form defintions at server so can hide or show in customization
|
|
- Also for dataLists? (for reporting not grid I mean)
|
|
|
|
todo: event log type just for edit wiki?
|
|
- this is because a wiki is not a discrete object in v8 so rights follow object itself and maybe it's necessary to know when wiki was edited?
|
|
|
|
Clean up the example markdown, go through and use mine and sprinkle in the marked sample stuff where it differs
|
|
- Make sure images ONLY come from our own server, not any other
|
|
- Maybe make a key image and put on our server for wiki example so we can if we feel like it track usage of demo data
|
|
- Put a block of emojis in it with a link to the help docs regarding emojis for extra coolness
|
|
|
|
todo: STYLE / OUTPUT CSS
|
|
- Check with MARKED to see what css they use or require or something, maybe I'm missing something they have on their site before I roll my own
|
|
- CODE BLOCK: why is it indenting the start of a code block?
|
|
- TASK Style the task markdown output, it looks pretty bleak right now
|
|
I stole the css from toast it's in the notes, search for task-list-item
|
|
also maybe there's a cleaner way try a google search once you see how they did it
|
|
- TABLES currently look shitty, find a proper style for them with boxes and shit, maybe alternating background on rows etc
|
|
- STRIKETHROUGH hard to see
|
|
|
|
|
|
|
|
*/
|
|
</script>
|