@excellens/markdown-notepad
Version:
A simple but extensible markdown editor based on @excellens/elementary.
1,355 lines (1,354 loc) • 43.1 kB
JavaScript
/*! @excellens/markdown-notepad 1.0.0 https://github.com/excellens/markdown-notepad#readme @license MIT */
var MarkdownNotepad = function(exports, Elementary, MarkdownNotepadPack) {
"use strict";
/**
* @param {ParentNode} node
* @param {string} selector
*/ function FindOne(node, selector) {
let element = node.querySelector(selector);
if (null === element) {
throw "ERR_FIND_ONE";
}
return element;
}
/**
* @param {ParentNode} node
* @param {string} selector
*/ function FindAll(node, selector) {
let elementList = node.querySelectorAll(selector);
if (false === !!elementList.length) {
throw "ERR_FIND_ALL";
}
return elementList;
}
var Base = Object.freeze({
__proto__: null,
FindOne: FindOne,
FindAll: FindAll
});
function SelectionState() {
const self = new Elementary.Component.ComponentState;
this.start = 0;
this.getStart = function() {
return this.start;
};
this.setStart = function(start) {
this.start = start;
return this;
};
this.end = 0;
this.getEnd = function() {
return this.end;
};
this.setEnd = function(end) {
this.end = end;
return this;
};
this.direction = "none";
this.getDirection = function() {
return this.direction;
};
this.setDirection = function(direction) {
this.direction = direction;
return this;
};
return Elementary.Base.Merge(this, self);
}
var State = Object.freeze({
__proto__: null,
SelectionState: SelectionState
});
function Textarea(document, element, state) {
const self = function(document, element, state) {
const presenter = new TextareaPresenter(state);
return new Elementary.Component.Component(presenter, document, "textarea", element);
}(document, element, state || new TextareaState);
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
let eventChange = null;
let eventKeyUp = null;
let eventSelectionChange = null;
const changeSelection = function() {
const start = self.selectionStart;
const end = self.selectionEnd;
const direction = self.selectionDirection;
self.presenter.changeSelection(start, end, direction);
};
this.initialize = function() {
parent.initialize();
self.addEventListener("change", eventChange = function(event) {
changeSelection();
self.presenter.changeValue(self.value);
});
self.addEventListener("keyup", eventKeyUp = function(event) {
changeSelection();
self.presenter.changeValue(self.value);
});
document.addEventListener("selectionchange", eventSelectionChange = function(event) {
if (document.activeElement === self) {
changeSelection();
}
});
return this;
};
this.setValue = function(value) {
self.value = value;
return this;
};
this.setSelection = function(start, end, direction) {
self.selectionStart = start;
self.selectionEnd = end;
self.selectionDirection = direction;
return this;
};
this.destroy = function() {
parent.destroy();
self.removeEventListener("change", eventChange);
eventChange = null;
self.removeEventListener("keyup", eventKeyUp);
eventKeyUp = null;
document.removeEventListener("selectionchange", eventSelectionChange);
eventSelectionChange = null;
return this;
};
return Elementary.Base.Merge(this, self);
}
function TextareaPresenter(state) {
const self = new Elementary.Component.ComponentPresenter(state);
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
this.update = function(state, id) {
self.component.setValue(state.getValue());
if (state.hasSelection()) {
const selection = state.getSelection();
const start = selection.getStart();
const end = selection.getEnd();
const direction = selection.getDirection();
self.component.setSelection(start, end, direction);
}
};
this.initialize = function(component) {
this.initializeState();
parent.initialize(component);
return this;
};
this.initializeState = function() {
if (!self.state.hasSelection()) {
const selection = new SelectionState;
self.state.setSelection(selection);
}
return this;
};
this.changeValue = function(value) {
if (self.state.getValue() !== value) {
self.state.setValue(value);
self.state.notify();
}
return this;
};
this.changeSelection = function(start, end, direction) {
if (self.state.hasSelection()) {
const selection = self.state.getSelection();
selection.setStart(start);
selection.setEnd(end);
selection.setDirection(direction);
selection.notify();
}
return this;
};
return Elementary.Base.Merge(this, self);
}
function TextareaState() {
const self = new Elementary.Component.ComponentState;
this.value = "";
this.getValue = function() {
return this.value;
};
this.setValue = function(value) {
this.value = value;
return this;
};
this.selection = null;
this.getSelection = function() {
return this.selection;
};
this.setSelection = function(selection) {
this.selection = selection;
return this;
};
this.hasSelection = function() {
return !!this.selection;
};
return Elementary.Base.Merge(this, self);
}
function Tab(document, element, state) {
const self = function(document, element, state) {
const presenter = new TabPresenter(state);
return new Elementary.Component.Component(presenter, document, "div", element);
}(document, element, state || new TabState);
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
let eventClick = null;
this.initialize = function() {
parent.initialize();
self.addEventListener("click", eventClick = function(event) {
self.presenter.changeActive(true);
});
return this;
};
this.setActive = function(active) {
self.classList.toggle("active", active);
return this;
};
this.destroy = function() {
parent.destroy();
self.removeEventListener("click", eventClick);
eventClick = null;
return this;
};
return Elementary.Base.Merge(this, self);
}
function TabPresenter(state) {
const self = new Elementary.Component.ComponentPresenter(state);
this.update = function(state, id) {
self.component.setActive(state.isActive());
if (state.hasPane()) {
let paneState = state.getPane();
paneState.setActive(state.isActive());
paneState.notify();
}
};
this.changeActive = function(active) {
self.state.setActive(active);
self.state.notify();
return this;
};
return Elementary.Base.Merge(this, self);
}
function TabState() {
const self = new Elementary.Component.ComponentState;
this.name = "tab";
this.getName = function() {
return this.name;
};
this.setName = function(name) {
this.name = name;
return this;
};
this.active = false;
this.isActive = function() {
return this.active;
};
this.setActive = function(active) {
this.active = active;
return this;
};
this.pane = null;
this.getPane = function() {
return this.pane;
};
this.setPane = function(pane) {
this.pane = pane;
return this;
};
this.hasPane = function() {
return !!this.pane;
};
return Elementary.Base.Merge(this, self);
}
var Component = Object.freeze({
__proto__: null,
Textarea: Textarea,
TextareaPresenter: TextareaPresenter,
TextareaState: TextareaState,
Tab: Tab,
TabPresenter: TabPresenter,
TabState: TabState
});
function Theme(document, id) {
const self = {};
this.id = id;
this.template = null;
this.initialize = function(control) {
this.initializeTemplate();
control.innerHTML = this.template;
const id = this.id;
control.classList.add(`theme-${id}`);
return this;
};
this.initializeTemplate = function() {
const id = this.id;
const templateElement = FindOne(document, `script[type="theme/markdown-notepad"]#${id}`);
this.template = templateElement.innerHTML;
return this;
};
this.destroy = function() {
this.template = null;
return this;
};
return Elementary.Base.Merge(this, self);
}
var Theme$1 = Object.freeze({
__proto__: null,
Theme: Theme
});
function TabPane(document, element, state) {
const self = function(document, element, state) {
const presenter = new TabPanePresenter(state);
return new Elementary.Component.Component(presenter, document, "div", element);
}(document, element, state || new TabPaneState);
this.setActive = function(active) {
self.classList.toggle("active", active);
return this;
};
return Elementary.Base.Merge(this, self);
}
function TabPanePresenter(state) {
const self = new Elementary.Component.ComponentPresenter(state);
this.update = function(state, id) {
self.component.setActive(state.isActive());
};
return Elementary.Base.Merge(this, self);
}
function TabPaneState() {
const self = new Elementary.Component.ComponentState;
this.name = "tab-pane";
this.getName = function() {
return this.name;
};
this.setName = function(name) {
this.name = name;
return this;
};
this.active = false;
this.isActive = function() {
return this.active;
};
this.setActive = function(active) {
this.active = active;
return this;
};
return Elementary.Base.Merge(this, self);
}
function Button(document, element, state) {
const self = function(document, element, state) {
const presenter = new ButtonPresenter(state);
return new Elementary.Component.Component(presenter, document, "button", element);
}(document, element, state || new ButtonState);
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
let eventMouseDown = null;
let eventMouseUp = null;
this.initialize = function() {
parent.initialize();
self.addEventListener("mousedown", eventMouseDown = function(event) {
self.presenter.changeClick(true);
});
self.addEventListener("mouseup", eventMouseUp = function(event) {
self.presenter.changeClick(false);
});
return this;
};
this.setActive = function(active) {
self.classList.toggle("active", active);
return this;
};
this.destroy = function() {
parent.destroy();
self.removeEventListener("mousedown", eventMouseDown);
eventMouseDown = null;
self.removeEventListener("mouseup", eventMouseUp);
eventMouseUp = null;
return this;
};
return Elementary.Base.Merge(this, self);
}
function ButtonPresenter(state) {
const self = new Elementary.Component.ComponentPresenter(state);
this.update = function(state, id) {
self.component.setActive(state.isActive());
};
this.changeClick = function(click) {
return this;
};
return Elementary.Base.Merge(this, self);
}
function ButtonState() {
const self = new Elementary.Component.ComponentState;
this.name = "button";
this.getName = function() {
return this.name;
};
this.setName = function(name) {
this.name = name;
return this;
};
this.active = false;
this.isActive = function() {
return this.active;
};
this.setActive = function(active) {
this.active = active;
return this;
};
return Elementary.Base.Merge(this, self);
}
function Preview(document, element, state) {
const self = function(document, element, state) {
const presenter = new PreviewPresenter(state);
return new Elementary.Component.Component(presenter, document, "div", element);
}(document, element, state || new PreviewState);
this.setValue = function(value) {
self.innerHTML = value;
return this;
};
return Elementary.Base.Merge(this, self);
}
function PreviewPresenter(state) {
const self = new Elementary.Component.ComponentPresenter(state);
this.update = function(state, id) {
self.component.setValue(state.getValue());
};
return Elementary.Base.Merge(this, self);
}
function PreviewState() {
const self = new Elementary.Component.ComponentState;
this.value = "";
this.getValue = function() {
return this.value;
};
this.setValue = function(value) {
this.value = value;
return this;
};
return Elementary.Base.Merge(this, self);
}
function Engine() {
const self = {};
this.process = function(value) {
throw "ERR_ENGINE_PROCESS";
};
return Elementary.Base.Merge(this, self);
}
function EngineMd() {
const self = new Engine;
this.md = new MarkdownNotepadPack.Instance;
this.process = function(value) {
return this.md.render(value);
};
return Elementary.Base.Merge(this, self);
}
var Engine$1 = Object.freeze({
__proto__: null,
Engine: Engine,
EngineMd: EngineMd
});
function Transaction() {
const self = {};
this.commit = function(snapshot, state) {
throw "ERR_TRANSACTION_COMMIT";
};
this.rollback = function(snapshot, state) {
throw "ERR_TRANSACTION_ROLLBACK";
};
return Elementary.Base.Merge(this, self);
}
function TextareaTransaction() {
const self = new Transaction;
this.commit = function(snapshot, state) {
state.setValue(snapshot.getValue());
if (state.hasSelection()) {
this.commitSelection(snapshot, state.getSelection());
}
state.notify();
return this;
};
this.commitSelection = function(snapshot, state) {
state.setStart(snapshot.start);
state.setEnd(snapshot.end);
state.notify();
return this;
};
this.rollback = function(snapshot, state) {
const value = state.getValue();
if (state.hasSelection()) {
this.rollbackSelection(snapshot, state.getSelection());
} else {
snapshot.start = value.length;
snapshot.end /****/ = value.length;
}
snapshot.before /*****/ = value.substring(undefined, snapshot.start);
snapshot.inside /*****/ = value.substring(snapshot.start, snapshot.end);
snapshot.after /******/ = value.substring(snapshot.end, undefined);
return this;
};
this.rollbackSelection = function(snapshot, state) {
snapshot.start = state.getStart();
snapshot.end /****/ = state.getEnd();
return this;
};
return Elementary.Base.Merge(this, self);
}
var Transaction$1 = Object.freeze({
__proto__: null,
Transaction: Transaction,
TextareaTransaction: TextareaTransaction
});
function Snapshot() {
const self = {};
this.state = null;
this.initialize = function() {
if (false === !!this.state) {
throw "ERR_SNAPSHOT_INITIALIZE";
}
this.rollback();
return this;
};
this.attach = function(state) {
this.state = state;
return this;
};
this.detach = function() {
this.state = null;
return this;
};
this.rollback = function() {
throw "ERR_SNAPSHOT_ROLLBACK";
};
this.commit = function() {
throw "ERR_SNAPSHOT_COMMIT";
};
this.destroy = function() {
if (true === !!this.state) {
throw "ERR_SNAPSHOT_DESTROY";
}
return this;
};
return Elementary.Base.Merge(this, self);
}
function TextSnapshot(transaction) {
const self = new Snapshot;
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
// Handle state only in transaction.
this.transaction = transaction;
this.start /******/ = 0;
this.end /********/ = 0;
this.before /*****/ = null;
this.inside /*****/ = null;
this.after /******/ = null;
this.getValue = function() {
return this.before + this.inside + this.after;
};
this.initialize = function(state) {
self.attach(state);
return parent.initialize();
};
this.commit = function() {
this.transaction.commit(this, self.state);
return this;
};
this.rollback = function() {
this.transaction.rollback(this, self.state);
return this;
};
this.destroy = function() {
self.detach();
this.start /******/ = 0;
this.end /********/ = 0;
this.before /*****/ = null;
this.inside /*****/ = null;
this.after /******/ = null;
return parent.destroy();
};
return Elementary.Base.Merge(this, self);
}
var Snapshot$1 = Object.freeze({
__proto__: null,
Snapshot: Snapshot,
TextSnapshot: TextSnapshot,
Transaction: Transaction$1
});
function Action(handle) {
const self = {};
if (true === !!handle) {
this.handle = handle;
} else {
this.handle = function(state) {
throw "ERR_ACTION_HANDLE";
};
}
return Elementary.Base.Merge(this, self);
}
Action.registry = new Elementary.Collection.CollectionMap;
function ActionTab(name) {
const self = new Action(null);
this.name = name;
this.handle = function(state) {
if (state.tab.has(this.name)) {
const tab = state.tab.get(this.name);
tab.setActive(true);
tab.notify();
}
return this;
};
return Elementary.Base.Merge(this, self);
}
function ActionTabWrite() {
const self = new ActionTab("write");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("tab-write", new ActionTabWrite);
function ActionTabPreview() {
const self = new ActionTab("preview");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("tab-preview", new ActionTabPreview);
function ActionHistory() {
const self = new Action(null);
this.handle = function(state) {
this.change(state.history);
return this.update(state.history, state);
};
this.change = function(history) {
throw "ERR_ACTION_HISTORY_CHANGE";
};
this.update = function(history, state) {
if (history.hasCurrent() && state.hasTextarea()) {
const current = history.getCurrent();
// The transaction has to support the textarea state.
current.attach(state.getTextarea());
current.commit();
current.detach();
}
return this;
};
return Elementary.Base.Merge(this, self);
}
function ActionHistoryForward() {
const self = new ActionHistory;
this.change = function(history) {
history.moveForward();
return this;
};
return Elementary.Base.Merge(this, self);
}
Action.registry.add("history-forward", new ActionHistoryForward);
function ActionHistoryBackward() {
const self = new ActionHistory;
this.change = function(history) {
history.moveBackward();
return this;
};
return Elementary.Base.Merge(this, self);
}
Action.registry.add("history-backward", new ActionHistoryBackward);
function ActionText() {
const self = new Action(null);
const text = new TextSnapshot(new TextareaTransaction);
this.handle = function(state) {
if (state.hasTextarea()) {
text.initialize(state.getTextarea());
if (!this.check(state, text)) {
this.insert(state, text);
} else {
this.delete(state, text);
}
text.commit();
text.destroy();
}
return this;
};
this.check = function(state, text) {
throw "ERR_ACTION_TEXT_CHECK";
};
this.insert = function(state, text) {
throw "ERR_ACTION_TEXT_INSERT";
};
this.delete = function(state, text) {
throw "ERR_ACTION_TEXT_DELETE";
};
return Elementary.Base.Merge(this, self);
}
function ActionTextWrap(prefix, suffix) {
const self = new ActionText;
this.prefix = prefix;
this.suffix = suffix;
this.check = function(state, text) {
return text.before.endsWith(self.prefix) && text.after.startsWith(self.suffix);
};
this.insert = function(state, text) {
text.before = text.before + this.prefix;
text.after = this.suffix + text.after;
text.start = text.start + this.prefix.length;
text.end = text.end + this.prefix.length;
return this;
};
this.delete = function(state, text) {
text.before = text.before.substring(undefined, text.before.length - this.prefix.length);
text.after = text.after.substring(undefined, text.after.length - this.suffix.length);
text.start = text.start - this.prefix.length;
text.end = text.end - this.prefix.length;
return this;
};
return Elementary.Base.Merge(this, self);
}
function ActionTextPrefix(prefix) {
const self = new ActionTextWrap(prefix, "");
return Elementary.Base.Merge(this, self);
}
function ActionTextPrefixLine(prefix) {
const self = new ActionTextPrefix(prefix);
const parent = {
insert: self.insert,
delete: self.delete
};
this.insert = function(state, text) {
const line = "\n";
if (0 < text.before.length && !text.before.endsWith(line)) {
text.before = text.before + line;
text.start = text.start + line.length;
text.end = text.end + line.length;
}
return parent.insert(state, text);
};
return Elementary.Base.Merge(this, self);
}
function ActionTextHeadline() {
const self = new ActionTextPrefix("### ");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-headline", new ActionTextHeadline);
function ActionTextBold() {
const self = new ActionTextWrap("**", "**");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-bold", new ActionTextBold);
function ActionTextItalic() {
const self = new ActionTextWrap("_", "_");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-italic", new ActionTextItalic);
function ActionTextQuote() {
const self = new ActionTextPrefixLine("> ");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-quote", new ActionTextQuote);
function ActionTextCode() {
const self = new ActionTextWrap("`", "`");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-code", new ActionTextCode);
function ActionTextLink() {
const self = new ActionTextWrap("[", "]");
const parent = {
insert: self.insert,
delete: self.delete
};
this.check = function(state, text) {
return false;
};
this.insert = function(state, text) {
const line = "(url)";
text.after = line + text.after;
if (0 < text.inside.length) {
text.start = text.end + self.suffix.length + "(".length;
text.end = text.end + self.suffix.length + "(".length + "url".length;
}
return parent.insert(state, text);
};
this.delete = function(state, text) {
return parent.delete(state, text);
};
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-link", new ActionTextLink);
function ActionTextUl() {
const self = new ActionTextPrefixLine("- ");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-ul", new ActionTextUl);
function ActionTextOl() {
const self = new ActionTextPrefixLine("1. ");
return Elementary.Base.Merge(this, self);
}
Action.registry.add("text-ol", new ActionTextOl);
var Action$1 = Object.freeze({
__proto__: null,
Action: Action,
ActionTab: ActionTab,
ActionTabWrite: ActionTabWrite,
ActionTabPreview: ActionTabPreview,
ActionHistory: ActionHistory,
ActionHistoryForward: ActionHistoryForward,
ActionHistoryBackward: ActionHistoryBackward,
ActionText: ActionText,
ActionTextWrap: ActionTextWrap,
ActionTextPrefix: ActionTextPrefix,
ActionTextPrefixLine: ActionTextPrefixLine,
ActionTextHeadline: ActionTextHeadline,
ActionTextBold: ActionTextBold,
ActionTextItalic: ActionTextItalic,
ActionTextQuote: ActionTextQuote,
ActionTextCode: ActionTextCode,
ActionTextLink: ActionTextLink,
ActionTextUl: ActionTextUl,
ActionTextOl: ActionTextOl,
Snapshot: Snapshot$1
});
function History() {
const self = new Elementary.Collection.Collection;
const parent = {
add: self.add
};
this.index = 0;
this.getIndex = function() {
return this.index;
};
this.setIndex = function(index) {
if (0 > index) {
index = 0;
}
const size = self.size();
if (index >= size) {
index = size - 1;
}
this.index = index;
return this;
};
this.modifyIndex = function(by) {
this.setIndex(this.getIndex() + by);
return this;
};
this.moveForward = function() {
return this.modifyIndex(+1);
};
this.moveBackward = function() {
return this.modifyIndex(-1);
};
this.getCurrent = function() {
return self.get(this.getIndex());
};
this.hasCurrent = function() {
return self.has(this.getIndex());
};
this.add = function(index, value) {
// Remove the loose end of the list if we are behind.
while (this.getIndex() < self.size() - 1) {
self.pop();
}
return parent.add(index, value);
};
return Elementary.Base.Merge(this, self);
}
var Collection = Object.freeze({
__proto__: null,
History: History
});
function Configuration() {
const self = {};
this.theme = "default";
this.getTheme = function() {
return this.theme;
};
this.setTheme = function(theme) {
this.theme = theme;
return this;
};
this.tab = new Elementary.Collection.Collection;
this.button = new Elementary.Collection.CollectionMap;
this.key = new Elementary.Collection.CollectionMap;
return Elementary.Base.Merge(this, self);
}
var Configuration$1 = Object.freeze({
__proto__: null,
Configuration: Configuration
});
function MarkdownNotepad(document, element, state) {
const self = function(document, element, state) {
const presenter = new MarkdownNotepadPresenter(state);
return new Elementary.Component.Component(presenter, document, "div", element);
}(document, element, state || new MarkdownNotepadState);
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
this.theme = null;
this.tab = new Elementary.Collection.CollectionMap;
this.tabPane = new Elementary.Collection.CollectionMap;
let eventKeyDown = null;
let eventKeyUp = null;
let eventContextMenu = null;
this.textarea = null;
this.preview = null;
let eventMouseDown = new Elementary.Collection.CollectionMap;
let eventMouseUp = new Elementary.Collection.CollectionMap;
this.button = new Elementary.Collection.CollectionMap;
this.getConfigurationDefault = function() {
const configuration = new Configuration;
// Tab configuration.
configuration.tab.add("write");
configuration.tab.add("preview");
// Button configuration.
configuration.button.add("text-headline", "text-headline");
configuration.button.add("text-bold", "text-bold");
configuration.button.add("text-italic", "text-italic");
configuration.button.add("text-quote", "text-quote");
configuration.button.add("text-code", "text-code");
configuration.button.add("text-link", "text-link");
configuration.button.add("text-ul", "text-ul");
configuration.button.add("text-ol", "text-ol");
configuration.button.add("history-forward", "history-forward");
configuration.button.add("history-backward", "history-backward");
// Key configuration.
configuration.key.add("ctrl+shift+z", "history-forward");
configuration.key.add("ctrl+z", "history-backward");
return configuration;
};
this.initialize = function(configuration) {
if (false === !!configuration) {
configuration = this.getConfigurationDefault();
}
// Set the component information.
self.setAttribute("data-component", "markdown-notepad");
// Set the style.
self.classList.add("markdown-notepad");
this.theme = new Theme(document, configuration.getTheme());
// Only use the theme if no element is given.
if (false === !!element) {
this.theme.initialize(this);
}
self.presenter.setConfiguration(configuration);
parent.initialize();
return this;
};
this.initializeTab = function(state) {
let name = state.getName();
let tab = new Tab(document, FindOne(self, `div[data-component="markdown-notepad.tab.${name}"]`), state);
tab.initialize();
// Set the style.
tab.classList.add("tab", name);
self.tab.add(name, tab);
this.initializeTabPane(state.getPane());
return this;
};
this.initializeTabPane = function(state) {
let name = state.getName();
let tabPane = new TabPane(document, FindOne(self, `div[data-component="markdown-notepad.tab-pane.${name}"]`), state);
tabPane.initialize();
// Set the style.
tabPane.classList.add("tab-pane", name);
self.tabPane.add(name, tabPane);
return this;
};
this.initializeTextarea = function(state) {
let textarea = new Textarea(document, FindOne(self, 'textarea[data-component="markdown-notepad.textarea"]'), state);
textarea.initialize();
// Set the style.
textarea.classList.add("markdown-textarea");
textarea.addEventListener("keydown", eventKeyDown = function(event) {
// Only continue, when ctrl or alt are down.
if (event.ctrlKey || event.altKey) {
// Create an action identifier in the format 'ctrl+alt+shift+key'.
const action = [ event.ctrlKey ? "ctrl" : "", event.altKey ? "alt" : "", event.shiftKey ? "shift" : "",
// Grab the current key in lower case.
event.key.toLowerCase() ].filter((function(value, index, array) {
// Filter any empty value and join them with a '+' sign.
return 0 !== value.length;
})).join("+");
try {
// Try to trigger the action, if it exists.
self.presenter.triggerAction(action, true);
event.preventDefault();
} catch (error) {
if ("ERR_ACTION" === error) ; else {
throw error;
}
}
}
});
// textarea.addEventListener('keyup', eventKeyUp = function (event) {
// self.focusTextarea();
// });
textarea.addEventListener("contextmenu", eventContextMenu = function(event) {
event.preventDefault();
});
this.textarea = textarea;
return this;
};
this.initializePreview = function(state) {
const preview = new Preview(document, FindOne(self, 'div[data-component="markdown-notepad.preview"]'), state);
preview.initialize();
// Set the style.
preview.classList.add("markdown-preview");
this.preview = preview;
return this;
};
this.initializeButton = function(state) {
let name = state.getName();
const button = new Button(document, FindOne(self, `button[data-component="markdown-notepad.button.${name}"]`), state);
button.initialize();
// Set the style.
button.classList.add("button", name);
// Set click callback.
const onMouseDown = function(event) {
self.presenter.triggerAction(name, true);
};
eventMouseDown.add(name, onMouseDown);
button.addEventListener("mousedown", onMouseDown);
const onMouseUp = function(event) {
self.focusTextarea();
};
eventMouseUp.add(name, onMouseUp);
button.addEventListener("mouseup", onMouseUp);
self.button.add(name, button);
return this;
};
this.focusTextarea = function() {
if (null !== this.textarea) {
this.textarea.focus();
}
return this;
};
this.destroy = function() {
parent.destroy();
this.theme.destroy();
this.theme = null;
this.tab.each((function(value, key) {
value.destroy();
}));
this.tab.clear();
this.tabPane.each((function(value, key) {
value.destroy();
}));
this.tabPane.clear();
this.textarea.removeEventListener("keydown", eventKeyDown);
eventKeyDown = null;
this.textarea.removeEventListener("keyup", eventKeyUp);
eventKeyUp = null;
this.textarea.removeEventListener("contextmenu", eventContextMenu);
eventContextMenu = null;
this.textarea.destroy();
this.textarea = null;
this.preview.destroy();
this.preview = null;
this.button.each((function(value, key) {
value.destroy();
const onMouseDown = eventMouseDown.get(key);
value.removeEventListener("mousedown", onMouseDown);
const onMouseUp = eventMouseUp.get(key);
value.removeEventListener("mouseup", onMouseUp);
}));
this.button.clear();
eventMouseDown.clear();
eventMouseUp.clear();
return this;
};
return Elementary.Base.Merge(this, self);
}
function MarkdownNotepadPresenter(state) {
const self = new Elementary.Component.ComponentPresenter(state);
const parent = {
initialize: self.initialize,
destroy: self.destroy
};
this.update = function(state, id) {};
this.engine = null;
let timeoutReference = null;
let callback = null;
let callbackMirror = null;
let callbackHistory = null;
this.action = new Elementary.Collection.CollectionMap;
this.configuration = null;
this.getConfiguration = function() {
return this.configuration;
};
this.setConfiguration = function(configuration) {
this.configuration = configuration;
return this;
};
this.hasConfiguration = function() {
return !!this.configuration;
};
this.fresh = false;
this.initialize = function(component) {
this.engine = new EngineMd;
callback = new Elementary.Observe.Callback("TabChange", (function(state, id) {
if (state.isActive()) {
self.state.tab.each((function(value, index) {
if (state.getName() === value.getName()) {
return;
}
value.setActive(false);
value.notify();
}));
}
}));
callbackMirror = new Elementary.Observe.Callback("Mirror", (function(state, id) {
if (self.state.hasPreview()) {
let previewValue = state.getValue();
previewValue = self.engine.process(previewValue);
let preview = self.state.getPreview();
preview.setValue(previewValue);
preview.notify();
}
}));
callbackHistory = new Elementary.Observe.Callback("History", (function(state, id) {
const makeHistory = function(state) {
// TODO: Timeout offset.
if (true === !!timeoutReference) {
clearTimeout(timeoutReference);
}
timeoutReference = setTimeout((function() {
const snapshot = new TextSnapshot(new TextareaTransaction);
snapshot.attach(state.getTextarea());
snapshot.rollback();
state.history.add(snapshot);
state.history.moveForward();
snapshot.detach();
}), 1e3);
};
if (self.state.history.hasCurrent()) {
const current = self.state.history.getCurrent();
if (state.getValue() !== current.getValue()) {
makeHistory(self.state);
}
} else {
makeHistory(self.state);
}
}));
parent.initialize(component);
this.initializeState();
return this;
};
this.initializeState = function() {
this.fresh = self.state.isFresh();
self.state.setFresh(false);
const configuration = this.getConfiguration();
configuration.tab.each((function(value, index) {
// Convert index 0 to false to true.
const active = false === !!index;
self.initializeTab(value, active);
}));
configuration.button.each((function(value, key) {
self.initializeButton(key, value);
}));
configuration.key.each((function(value, key) {
self.initializeAction(key, value, true);
}));
this.initializeTextarea();
this.initializePreview();
};
this.initializeTab = function(name, active) {
const tabName = `tab-${name}`;
let tab;
if (self.state.tab.has(tabName)) {
tab = self.state.tab.get(tabName);
} else {
const tabPane = function(name, active) {
const tabPaneName = `tab-pane-${name}`;
const tabPane = new TabPaneState;
tabPane.setName(tabPaneName);
tabPane.setActive(active);
return tabPane;
}(name, active);
tab = new TabState;
tab.setName(tabName);
tab.setActive(active);
tab.setPane(tabPane);
self.state.tab.add(tabName, tab);
tab.attachCallback(callback);
}
self.component.initializeTab(tab);
return this;
};
this.initializeTextarea = function() {
let textarea;
if (self.state.hasTextarea()) {
textarea = self.state.getTextarea();
} else {
textarea = new TextareaState;
textarea.setValue("");
// This is set in the textarea presenter.
textarea.setSelection(null);
textarea.attachCallback(callbackMirror);
textarea.attachCallback(callbackHistory);
self.state.setTextarea(textarea);
}
self.component.initializeTextarea(textarea);
return this;
};
this.initializePreview = function() {
let preview;
if (self.state.hasPreview()) {
preview = self.state.getPreview();
} else {
preview = new PreviewState;
preview.setValue("");
self.state.setPreview(preview);
}
self.component.initializePreview(preview);
return this;
};
this.initializeButton = function(name, actionName) {
const buttonName = `button-${name}`;
let button;
if (self.state.button.has(buttonName)) {
button = self.state.button.get(buttonName);
} else {
button = new ButtonState;
button.setName(buttonName);
self.state.button.add(buttonName, button);
}
this.initializeAction(buttonName, actionName, true);
self.component.initializeButton(button);
return this;
};
this.initializeAction = function(name, actionName, strict) {
// Special case, where strict is not set, but it should be true.
if ("undefined" === typeof strict) {
strict = true;
}
if (Action.registry.has(actionName)) {
const action = Action.registry.get(actionName);
this.action.add(name, action);
} else {
if (strict) {
throw "ERR_ACTION";
}
}
return this;
};
this.triggerAction = function(name, strict) {
// Special case, where strict is not set, but it should be true.
if ("undefined" === typeof strict) {
strict = true;
}
if (this.action.has(name)) {
const action = this.action.get(name);
action.handle(self.state);
} else {
if (strict) {
throw "ERR_ACTION";
}
}
return this;
};
this.destroy = function() {
parent.destroy();
if (this.fresh) {
self.state.tab.each((function(value, index) {
value.detachCallback(callback);
}));
self.state.tab.clear();
}
timeoutReference = null;
callback = null;
if (this.fresh) {
if (self.state.hasTextarea()) {
let textarea = self.state.getTextarea();
textarea.detachCallback(callbackMirror);
textarea.detachCallback(callbackHistory);
self.state.setTextarea(null);
}
}
callbackMirror = null;
callbackHistory = null;
if (this.fresh) {
if (self.state.hasPreview()) {
self.state.setPreview(null);
}
}
this.action.clear();
if (this.fresh) {
//self.state.button.each(function (value, index) {
//});
self.state.button.clear();
}
this.engine = null;
this.configuration = null;
this.fresh = false;
};
return Elementary.Base.Merge(this, self);
}
function MarkdownNotepadState() {
const self = new Elementary.Component.ComponentState;
this.fresh = true;
this.isFresh = function() {
return this.fresh;
};
this.setFresh = function(fresh) {
this.fresh = fresh;
return this;
};
this.tab = new Elementary.Collection.CollectionMap;
this.textarea = null;
this.getTextarea = function() {
return this.textarea;
};
this.setTextarea = function(textarea) {
this.textarea = textarea;
return this;
};
this.hasTextarea = function() {
return !!this.textarea;
};
this.preview = null;
this.getPreview = function() {
return this.preview;
};
this.setPreview = function(preview) {
this.preview = preview;
return this;
};
this.hasPreview = function() {
return !!this.preview;
};
this.button = new Elementary.Collection.CollectionMap;
this.history = new History;
return Elementary.Base.Merge(this, self);
}
var MarkdownNotepad$1 = Object.freeze({
__proto__: null,
MarkdownNotepad: MarkdownNotepad,
MarkdownNotepadPresenter: MarkdownNotepadPresenter,
MarkdownNotepadState: MarkdownNotepadState
});
var Control = Object.freeze({
__proto__: null,
MarkdownNotepad: MarkdownNotepad$1
});
var Service = Object.freeze({
__proto__: null,
Action: Action$1,
Engine: Engine$1,
Collection: Collection
});
var version = "1.0.0";
const metadata = {
version: version
};
exports.Base = Base;
exports.Component = Component;
exports.Configuration = Configuration$1;
exports.Control = Control;
exports.Service = Service;
exports.State = State;
exports.Theme = Theme$1;
exports.metadata = metadata;
Object.defineProperty(exports, "__esModule", {
value: true
});
return exports;
}({}, Elementary, MarkdownNotepadPack);
//# sourceMappingURL=markdown-notepad.js.map