Skip to content

Commit

Permalink
Add lorem auto-complete for rich text editor
Browse files Browse the repository at this point in the history
  • Loading branch information
dgthanhan committed May 10, 2017
1 parent 7a6a481 commit f861dbc
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 2 deletions.
2 changes: 2 additions & 0 deletions app/views/common/Popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ Popup.prototype.isVisible = function () {
Popup.prototype.showAt = function (x, y, skipEvent, autoFlip) {
this.reparent();

console.log("Showing at: ", [x, y]);

if (this.mode) {
this.popupContainer.setAttribute("mode", this.mode);
}
Expand Down
125 changes: 124 additions & 1 deletion app/views/editors/OnScreenRichTextEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ function OnScreenRichTextEditor() {
BaseTemplatedWidget.call(this);

this.popup.allowMouseDragging = true;
this.acMenu = new Menu();
}
__extend(BaseTemplatedWidget, OnScreenRichTextEditor);

Expand Down Expand Up @@ -50,8 +51,10 @@ OnScreenRichTextEditor.prototype.install = function (canvas) {
}, false);
var thiz = this;

this.bind("keydown", this.handleACKeyDown, this.container);
this.bind("keypress", this.handleKeyPress, this.container);
this.bind("keyup", this.handleKeyPress, this.container);
this.bind("input", this.handleInput, this.container);
};
OnScreenRichTextEditor.prototype.addEditorEvent = function (name, handler) {
this.textEditor.addEventListener(name, handler, false);
Expand Down Expand Up @@ -186,13 +189,131 @@ OnScreenRichTextEditor.prototype._setupEditor = function () {
thiz.textToolOverlay.node().style.visibility = "visible";
}, 10);
};
OnScreenRichTextEditor.MIN_AC_LENGTH = 5;
OnScreenRichTextEditor.prototype.handleInput = function (event) {
var selection = window.getSelection();
if (!selection || !selection.anchorNode) {
this.hideAutoComplete();
return;
}

if (!this.container.contains(selection.anchorNode)) {
this.hideAutoComplete();
return;
}
if (selection.anchorOffset < OnScreenRichTextEditor.MIN_AC_LENGTH) {
this.hideAutoComplete();
return;
}

var textContent = selection.anchorNode.textContent;
var text = textContent.substring(selection.anchorOffset - OnScreenRichTextEditor.MIN_AC_LENGTH, selection.anchorOffset);
var acSet = this.getAutoCompleteSet(text, textContent == text);
if (acSet && acSet.items && acSet.items.length > 0) {
this.showAutoComplete(acSet, selection.anchorNode, selection.anchorOffset);
} else {
this.hideAutoComplete();
}
};

OnScreenRichTextEditor.prototype.showAutoComplete = function (set, node, offset) {
this.acMenu.items = [];
function createItem(acItem) {
var label = (typeof(acItem) == "string" ? acItem : (acItem.label || acItem.toString()));
if (label.length > 40) label = label.substring(0, 40) + "... (" + label.length + " characters)";
return {
label: label,
run: function () {
var text = typeof(acItem) == "string" ? acItem : (acItem.content || acItem.toString());

var textContent = node.textContent;
var start = Math.max(0, offset - set.replacementSize);
var newText = textContent.substring(0, start) + text + textContent.substring(offset);
node.textContent = newText;
var selection = window.getSelection();
selection.collapseToStart();
selection.extend(node, start + text.length);
selection.collapseToEnd();
}
};
}
for (var acItem of set.items) {
this.acMenu.register(createItem(acItem));
}

var anchorNode = node;
if (!anchorNode.localName) anchorNode = anchorNode.parentNode;
this.acSelectedItemIndex = -1;
this.acMenu.showMenu(anchorNode, "left-inside", "bottom", 0, 5, true);
};
OnScreenRichTextEditor.prototype.hideAutoComplete = function () {
this.acMenu.hideMenu();
};

//TODO: Refactor this into AC set registration from stencil
OnScreenRichTextEditor.prototype.getAutoCompleteSet = function (term, isFull) {
if (term && term.toLowerCase() == "lorem") {
var up = term.substring(0, 1) == "L";
return {
items: [
up ? capitalize(getLoremWord()) : getLoremWord(),
up ? loremIpsumSentence(3).trim() : loremIpsum(3),
up ? loremIpsumSentence(5).trim() : loremIpsum(5),
up ? loremIpsumSentence(10).trim() : loremIpsum(10),
up ? loremIpsumSentence(20).trim() : loremIpsum(20),
loremIpsumParagraph(40)
],
replacementSize: term.length
}
}

return null;
};
OnScreenRichTextEditor.prototype.handleACKeyDown = function (event) {
if (!this.acMenu.isVisible()) return;
if (event.keyCode == DOM_VK_UP || event.keyCode == DOM_VK_DOWN) {
var items = this.acMenu.getMenuItemNodes();
if (event.keyCode == DOM_VK_DOWN) {
this.acSelectedItemIndex ++;
if (this.acSelectedItemIndex >= items.length) this.acSelectedItemIndex = 0;
} else {
this.acSelectedItemIndex --;
if (this.acSelectedItemIndex < 0) this.acSelectedItemIndex = items.length - 1;
}

for (var i = 0; i < items.length; i ++) {
if (i == this.acSelectedItemIndex) {
items[i].setAttribute("selected", "true");
} else {
items[i].removeAttribute("selected");
}
}

event.stopPropagation();
event.preventDefault();
} else if (event.keyCode == DOM_VK_RETURN || event.keyCode == DOM_VK_ENTER) {
var items = this.acMenu.getMenuItemNodes();
if (this.acSelectedItemIndex >= items.length || this.acSelectedItemIndex < 0) return;
var item = items[this.acSelectedItemIndex]._item;
if (item && item.run) item.run();
this.hideAutoComplete();

event.stopPropagation();
event.preventDefault();

this.cancelNextCommit = true;
}
};
OnScreenRichTextEditor.prototype.handleKeyPress = function (event) {
if (this.textToolOverlay.settingFont) {
return;
}

if (event.keyCode == DOM_VK_RETURN && !event.shiftKey && !event.accelKey) {
if (this.cancelNextCommit) {
this.cancelNextCommit = false;
return;
}
var insideList = null;
try {
var node = window.getSelection().anchorNode;
Expand All @@ -201,7 +322,9 @@ OnScreenRichTextEditor.prototype.handleKeyPress = function (event) {
});
} catch (e) {}

if (!insideList || event.ctrlKey) this.commitChange(event);
if (!insideList || event.ctrlKey) {
this.commitChange(event);
}

} else if (event.keyCode == DOM_VK_ESCAPE && this.popup == BaseWidget.getTopClosable()) {
this.cancelChange();
Expand Down
3 changes: 3 additions & 0 deletions app/views/menus/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ Menu.prototype.renderItem = function (item) {

return hbox;
};
Menu.prototype.getMenuItemNodes = function () {
return this.popupContainer.childNodes;
};
Menu.prototype.render = function () {
Dom.empty(this.popupContainer);
var actualItems = [];
Expand Down
3 changes: 2 additions & 1 deletion app/views/menus/Menu.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
margin: 0.8em 0em;
}
body .MenuPopupContainer .MenuItem.Active:not([disabled='true']),
body .MenuPopupContainer .MenuItem:not([disabled='true']):hover {
body .MenuPopupContainer .MenuItem:not([disabled='true']):hover,
body .MenuPopupContainer .MenuItem[selected='true']:not([disabled='true']) {
background: @selected_bg;
color: @selected_fg;
}
Expand Down

0 comments on commit f861dbc

Please sign in to comment.