Skip to content

Commit

Permalink
added search and replace in the editor. (Closes #63)
Browse files Browse the repository at this point in the history
- added help for search and replace
- added icons in the editor
  • Loading branch information
redimp committed Mar 10, 2024
1 parent 4629a4c commit 9dc9388
Show file tree
Hide file tree
Showing 6 changed files with 863 additions and 6 deletions.
10 changes: 10 additions & 0 deletions otterwiki/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ To create a page use the <span class="help-button"><span class="btn btn-square b

You can preview your changes using <span class="btn btn-primary btn-sm btn-hlp"><i class="far fa-eye"></i></span>. Either while editing or from previewing the article your changes can be committed via <span class="btn btn-success btn-sm btn-hlp"> <i class="fas fa-save"></i></span>. This will open a modal where you can enter a commit message. To discard your changes use <span class="btn btn-danger btn-sm btn-hlp" style="border: None;" role="button"><i class="fas fa-window-close"></i></span> and return to the view of the page.

#### Editor Shortcuts

| Operation | Linux/Windows | MacOS |
|---------------|------------------------------|-----------------------------|
| Search | <kbd>Ctrl</kbd>-<kbd>F</kbd> | <kbd>Cmd</kbd>-<kbd>F</kbd> |
| Find next | <kbd>Ctrl</kbd>-<kbd>G</kbd> | <kbd>Cmd</kbd>-<kbd>G</kbd> |
| Find previous | <kbd>Shift</kbd>-<kbd>Ctrl</kbd>-<kbd>G</kbd> | <kbd>Shift</kbd>-<kbd>Cmd</kbd>-<kbd>G</kbd> |
| Replace | <kbd>Shift</kbd>-<kbd>Ctrl</kbd>-<kbd>F</kbd> | <kbd>Cmd</kbd>-<kbd>Option</kbd>-<kbd>F</kbd> |
| Replace all | <kbd>Shift</kbd>-<kbd>Ctrl</kbd>-<kbd>R</kbd> | <kbd>Shift</kbd>-<kbd>Cmd</kbd>-<kbd>Option</kbd>-<kbd>F</kbd> |

#### Page history

You can view the history of a page with <span class="help-button"><span class="btn btn-square btn-sm"><i class="fas fa-ellipsis-v"></i></span> <i class="fas fa-caret-right"></i> <span class="btn btn-square btn-sm"><i class="far fa-file-alt"></i></span> History</span>. All edits of the page will be listed in order. The date of the commit, the Author and the commit message are displayed.
Expand Down
45 changes: 45 additions & 0 deletions otterwiki/static/css/codemirror-dialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.CodeMirror-dialog {
position: absolute;
left: 0; right: 0;
z-index: 15;
padding: .1em .8em;
overflow: hidden;
background-color: #fff;
color: #000;
}

.dark-mode .CodeMirror-dialog {
background: #25282c;
color: #d8dee9;
}

.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
top: 0;
}

.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
bottom: 0;
}

.CodeMirror-dialog input {
border: none;
outline: none;
background-color: inherit;
width: 20em;
color: inherit;
font-family: monospace;
}

.CodeMirror-dialog button {
font-size: 70%;
color: var(--lm-button-text-color);
background-color: var(--lm-button-bg-color);
}

.dark-mode .CodeMirror-dialog button {
font-size: 70%;
color: var(--dm-button-text-color);
background-color: var(--dm-button-bg-color);
}
163 changes: 163 additions & 0 deletions otterwiki/static/js/cm-dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE

// Open simple dialogs on top of an editor. Relies on dialog.css.

(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
function dialogDiv(cm, template, bottom) {
var wrap = cm.getWrapperElement();
var dialog;
dialog = wrap.appendChild(document.createElement("div"));
if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";

if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
dialog.appendChild(template);
}
CodeMirror.addClass(wrap, 'dialog-opened');
return dialog;
}

function closeNotification(cm, newVal) {
if (cm.state.currentNotificationClose)
cm.state.currentNotificationClose();
cm.state.currentNotificationClose = newVal;
}

CodeMirror.defineExtension("openDialog", function(template, callback, options) {
if (!options) options = {};

closeNotification(this, null);

var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
function close(newVal) {
if (typeof newVal == 'string') {
inp.value = newVal;
} else {
if (closed) return;
closed = true;
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
me.focus();

if (options.onClose) options.onClose(dialog);
}
}

var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
inp.focus();

if (options.value) {
inp.value = options.value;
if (options.selectValueOnOpen !== false) {
inp.select();
}
}

if (options.onInput)
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
if (options.onKeyUp)
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});

CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur();
CodeMirror.e_stop(e);
close();
}
if (e.keyCode == 13) callback(inp.value, e);
});

if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) {
if (evt.relatedTarget !== null) close();
});
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});

if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);

button.focus();
}
return close;
});

CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var buttons = dialog.getElementsByTagName("button");
var closed = false, me = this, blurring = 1;
function close() {
if (closed) return;
closed = true;
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
me.focus();
}
buttons[0].focus();
for (var i = 0; i < buttons.length; ++i) {
var b = buttons[i];
(function(callback) {
CodeMirror.on(b, "click", function(e) {
CodeMirror.e_preventDefault(e);
close();
if (callback) callback(me);
});
})(callbacks[i]);
CodeMirror.on(b, "blur", function() {
--blurring;
setTimeout(function() { if (blurring <= 0) close(); }, 200);
});
CodeMirror.on(b, "focus", function() { ++blurring; });
}
});

/*
* openNotification
* Opens a notification, that can be closed with an optional timer
* (default 5000ms timer) and always closes on click.
*
* If a notification is opened while another is opened, it will close the
* currently opened one and open the new one immediately.
*/
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
var closed = false, doneTimer;
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;

function close() {
if (closed) return;
closed = true;
clearTimeout(doneTimer);
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
}

CodeMirror.on(dialog, 'click', function(e) {
CodeMirror.e_preventDefault(e);
close();
});

if (duration)
doneTimer = setTimeout(close, duration);

return close;
});
});
Loading

0 comments on commit 9dc9388

Please sign in to comment.