Skip to content

Commit 9c1ebee

Browse files
committed
clipboard preparations
1 parent d8266e2 commit 9c1ebee

File tree

8 files changed

+188
-94
lines changed

8 files changed

+188
-94
lines changed

my-mind.js

+94-47
Original file line numberDiff line numberDiff line change
@@ -462,15 +462,15 @@ MM.Item.prototype.clone = function() {
462462
return this.constructor.fromJSON(data);
463463
}
464464

465-
MM.Item.prototype.focus = function() {
466-
/* going to mode 2c */
465+
MM.Item.prototype.select = function() {
467466
this._dom.node.classList.add("current");
468467
this.getMap().ensureItemVisibility(this);
469-
MM.publish("item-focus", this);
468+
MM.Clipboard.focus(); /* going to mode 2c */
469+
MM.publish("item-select", this);
470470
}
471471

472-
MM.Item.prototype.blur = function() {
473-
/* we were in 2b; finish that via 4b */
472+
MM.Item.prototype.deselect = function() {
473+
/* we were in 2b; finish that via 3b */
474474
if (MM.App.editing) { MM.Command.Finish.execute(); }
475475
this._dom.node.classList.remove("current");
476476
}
@@ -733,7 +733,7 @@ MM.Item.prototype.handleEvent = function(e) {
733733
if (e.keyCode == 9) { e.preventDefault(); } /* TAB has a special meaning in this app, do not use it to change focus */
734734
break;
735735

736-
case "blur": /* 4d */
736+
case "blur": /* 3d */
737737
MM.Command.Finish.execute();
738738
break;
739739

@@ -1160,7 +1160,7 @@ MM.Keyboard.handleEvent = function(e) {
11601160
var keys = command.keys;
11611161
for (var j=0;j<keys.length;j++) {
11621162
if (this._keyOK(keys[j], e)) {
1163-
e.preventDefault();
1163+
command.prevent && e.preventDefault();
11641164
command.execute(e);
11651165
return;
11661166
}
@@ -1409,14 +1409,33 @@ MM.Action.SetStatus.prototype.undo = function() {
14091409
}
14101410
MM.Clipboard = {
14111411
_data: null,
1412-
_mode: ""
1412+
_mode: "",
1413+
_node: document.createElement("textarea")
14131414
};
14141415

1416+
MM.Clipboard.init = function() {
1417+
this._node.style.position = "absolute";
1418+
this._node.style.width = 0;
1419+
this._node.style.height = 0;
1420+
this._node.style.left = "-100px";
1421+
this._node.style.top = "-100px";
1422+
document.body.appendChild(this._node);
1423+
}
1424+
1425+
MM.Clipboard.focus = function() {
1426+
this._node.focus();
1427+
}
1428+
14151429
MM.Clipboard.copy = function(sourceItem) {
14161430
this._endCut();
1417-
14181431
this._data = sourceItem.clone();
14191432
this._mode = "copy";
1433+
1434+
var plaintext = this._itemToPlaintext(sourceItem);
1435+
this._node.value = plaintext;
1436+
this._node.selectionStart = 0;
1437+
this._node.selectionEnd = this._node.value.length;
1438+
setTimeout(function() { this._node.value = ""; }.bind(this), 0);
14201439
}
14211440

14221441
MM.Clipboard.paste = function(targetItem) {
@@ -1468,6 +1487,19 @@ MM.Clipboard._endCut = function() {
14681487
this._data = null;
14691488
this._mode = "";
14701489
}
1490+
1491+
MM.Clipboard._itemToPlaintext = function(item, depth) {
1492+
depth = depth || 0;
1493+
1494+
var lines = item.getChildren().map(function(child) {
1495+
return this._itemToPlaintext(child, depth+1);
1496+
}, this);
1497+
1498+
var prefix = new Array(depth+1).join("\t");
1499+
lines.unshift(prefix + item.getText().replace(/\n/g, "<br/>"))
1500+
1501+
return lines.join("\n") + (depth ? "" : "\n");
1502+
}
14711503
MM.Menu = {
14721504
_dom: {},
14731505
_port: null,
@@ -1528,6 +1560,7 @@ MM.Menu = {
15281560
MM.Command = Object.create(MM.Repo, {
15291561
keys: {value: []},
15301562
editMode: {value: false},
1563+
prevent: {value: true}, /* prevent default keyboard action? */
15311564
label: {value: ""}
15321565
});
15331566

@@ -1771,6 +1804,7 @@ MM.Command.Pan.handleEvent = function(e) {
17711804

17721805
MM.Command.Copy = Object.create(MM.Command, {
17731806
label: {value: "Copy"},
1807+
prevent: {value: false},
17741808
keys: {value: [{keyCode: "C".charCodeAt(0), ctrlKey:true}]}
17751809
});
17761810
MM.Command.Copy.execute = function() {
@@ -1779,6 +1813,7 @@ MM.Command.Copy.execute = function() {
17791813

17801814
MM.Command.Cut = Object.create(MM.Command, {
17811815
label: {value: "Cut"},
1816+
prevent: {value: false},
17821817
keys: {value: [{keyCode: "X".charCodeAt(0), ctrlKey:true}]}
17831818
});
17841819
MM.Command.Cut.execute = function() {
@@ -1787,6 +1822,7 @@ MM.Command.Cut.execute = function() {
17871822

17881823
MM.Command.Paste = Object.create(MM.Command, {
17891824
label: {value: "Paste"},
1825+
prevent: {value: false},
17901826
keys: {value: [{keyCode: "V".charCodeAt(0), ctrlKey:true}]}
17911827
});
17921828
MM.Command.Paste.execute = function() {
@@ -3521,7 +3557,6 @@ MM.Backend.GDrive._auth = function(forceUI) {
35213557
}
35223558
MM.UI = function() {
35233559
this._node = document.querySelector(".ui");
3524-
this._node.addEventListener("click", this);
35253560

35263561
this._toggle = this._node.querySelector("#toggle");
35273562

@@ -3531,15 +3566,18 @@ MM.UI = function() {
35313566
this._value = new MM.UI.Value();
35323567
this._status = new MM.UI.Status();
35333568

3534-
MM.subscribe("item-focus", this);
3569+
MM.subscribe("item-select", this);
35353570
MM.subscribe("item-change", this);
35363571

3572+
this._node.addEventListener("click", this);
3573+
this._node.addEventListener("change", this);
3574+
35373575
this.toggle();
35383576
}
35393577

35403578
MM.UI.prototype.handleMessage = function(message, publisher) {
35413579
switch (message) {
3542-
case "item-focus":
3580+
case "item-select":
35433581
this._update();
35443582
break;
35453583

@@ -3550,23 +3588,29 @@ MM.UI.prototype.handleMessage = function(message, publisher) {
35503588
}
35513589

35523590
MM.UI.prototype.handleEvent = function(e) {
3553-
/* blur to return focus back to app commands (mode 2c) */
3554-
/* FIXME 1) re-select current node, 2) do it also after any select changes */
3555-
if (e.target.nodeName.toLowerCase() != "select") { e.target.blur(); }
3591+
switch (e.type) {
3592+
case "click":
3593+
if (e.target.nodeName.toLowerCase() != "select") { MM.Clipboard.focus(); } /* focus the clipboard (2c) */
35563594

3557-
if (e.target == this._toggle) {
3558-
this.toggle();
3559-
return;
3560-
}
3561-
3562-
var node = e.target;
3563-
while (node != document) {
3564-
var command = node.getAttribute("data-command");
3565-
if (command) {
3566-
MM.Command[command].execute();
3567-
return;
3568-
}
3569-
node = node.parentNode;
3595+
if (e.target == this._toggle) {
3596+
this.toggle();
3597+
return;
3598+
}
3599+
3600+
var node = e.target;
3601+
while (node != document) {
3602+
var command = node.getAttribute("data-command");
3603+
if (command) {
3604+
MM.Command[command].execute();
3605+
return;
3606+
}
3607+
node = node.parentNode;
3608+
}
3609+
break;
3610+
3611+
case "change":
3612+
MM.Clipboard.focus(); /* focus the clipboard (2c) */
3613+
break;
35703614
}
35713615
}
35723616

@@ -3906,8 +3950,7 @@ MM.UI.IO.prototype.show = function(mode) {
39063950
MM.UI.IO.prototype.hide = function() {
39073951
if (!this._node.classList.contains("visible")) { return; }
39083952
this._node.classList.remove("visible");
3909-
/* FIXME instead of blurring, just re-select current node => switch to 2c */
3910-
document.activeElement && document.activeElement.blur();
3953+
MM.Clipboard.focus();
39113954
window.removeEventListener("keydown", this);
39123955
}
39133956

@@ -4838,6 +4881,12 @@ MM.Mouse._visualizeDragState = function(state) {
48384881
node.style.boxShadow = (x*offset) + "px " + (y*offset) + "px 2px " + spread + "px #000";
48394882
}
48404883
}
4884+
4885+
setInterval(function() {
4886+
console.log(document.activeElement);
4887+
}, 1000);
4888+
4889+
48414890
/*
48424891
* Notes regarding app state/modes, activeElements, focusing etc.
48434892
* ==============================================================
@@ -4850,23 +4899,20 @@ MM.Mouse._visualizeDragState = function(state) {
48504899
* Keyboard shortcuts are disabled.
48514900
* 2b) Current item is being edited. It is contentEditable and focused.
48524901
* Blurring ends the edit mode.
4853-
* 2c) ELSE the focus belongs the the currently selected item.
4902+
* 2c) ELSE the Clipboard is focused (its invisible textarea)
48544903
*
4855-
* In 2a, we try to return focus (re-select, 2c) as soon as possible
4856-
* (after clicking, after changing select's value).
4904+
* In 2a, we try to lose focus as soon as possible
4905+
* (after clicking, after changing select's value), switching to 2c.
48574906
*
4858-
* 3) After selecting an item, we switch to 2c. In 2c, the current item
4859-
* focuses its invisible "paste" node to listen for ctrl+v data.
4860-
*
4861-
* 4) Editing mode (2b) can be ended by multiple ways:
4862-
* 4a) By calling current.stopEditing();
4907+
* 3) Editing mode (2b) can be ended by multiple ways:
4908+
* 3a) By calling current.stopEditing();
48634909
* this shall be followed by some resolution.
4864-
* 4b) By executing MM.Command.{Finish,Cancel};
4865-
* these call 4a internally.
4866-
* 4c) By blurring the item itself (by selecting another);
4867-
* this calls MM.Command.Finish (4b).
4868-
* 4b) By blurring the currentElement;
4869-
* this calls MM.Command.Finish (4b).
4910+
* 3b) By executing MM.Command.{Finish,Cancel};
4911+
* these call 3a internally.
4912+
* 3c) By blurring the item itself (by selecting another);
4913+
* this calls MM.Command.Finish (3b).
4914+
* 3b) By blurring the currentElement;
4915+
* this calls MM.Command.Finish (3b).
48704916
*
48714917
*/
48724918
MM.App = {
@@ -4912,11 +4958,11 @@ MM.App = {
49124958
},
49134959

49144960
select: function(item) {
4915-
if (this.current && this.current != item) { this.current.blur(); }
4961+
if (this.current && this.current != item) { this.current.deselect(); }
49164962
this.current = item;
4917-
this.current.focus();
4963+
this.current.select();
49184964
},
4919-
4965+
49204966
adjustFontSize: function(diff) {
49214967
this._fontSize = Math.max(30, this._fontSize + 10*diff);
49224968
this._port.style.fontSize = this._fontSize + "%";
@@ -4966,6 +5012,7 @@ MM.App = {
49665012
MM.Keyboard.init();
49675013
MM.Menu.init(this._port);
49685014
MM.Mouse.init(this._port);
5015+
MM.Clipboard.init();
49695016

49705017
window.addEventListener("resize", this);
49715018
window.addEventListener("beforeunload", this);

src/app.js

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
2+
setInterval(function() {
3+
console.log(document.activeElement);
4+
}, 1000);
5+
6+
17
/*
28
* Notes regarding app state/modes, activeElements, focusing etc.
39
* ==============================================================
@@ -10,23 +16,20 @@
1016
* Keyboard shortcuts are disabled.
1117
* 2b) Current item is being edited. It is contentEditable and focused.
1218
* Blurring ends the edit mode.
13-
* 2c) ELSE the focus belongs the the currently selected item.
19+
* 2c) ELSE the Clipboard is focused (its invisible textarea)
1420
*
15-
* In 2a, we try to return focus (re-select, 2c) as soon as possible
16-
* (after clicking, after changing select's value).
21+
* In 2a, we try to lose focus as soon as possible
22+
* (after clicking, after changing select's value), switching to 2c.
1723
*
18-
* 3) After selecting an item, we switch to 2c. In 2c, the current item
19-
* focuses its invisible "paste" node to listen for ctrl+v data.
20-
*
21-
* 4) Editing mode (2b) can be ended by multiple ways:
22-
* 4a) By calling current.stopEditing();
24+
* 3) Editing mode (2b) can be ended by multiple ways:
25+
* 3a) By calling current.stopEditing();
2326
* this shall be followed by some resolution.
24-
* 4b) By executing MM.Command.{Finish,Cancel};
25-
* these call 4a internally.
26-
* 4c) By blurring the item itself (by selecting another);
27-
* this calls MM.Command.Finish (4b).
28-
* 4b) By blurring the currentElement;
29-
* this calls MM.Command.Finish (4b).
27+
* 3b) By executing MM.Command.{Finish,Cancel};
28+
* these call 3a internally.
29+
* 3c) By blurring the item itself (by selecting another);
30+
* this calls MM.Command.Finish (3b).
31+
* 3b) By blurring the currentElement;
32+
* this calls MM.Command.Finish (3b).
3033
*
3134
*/
3235
MM.App = {
@@ -72,11 +75,11 @@ MM.App = {
7275
},
7376

7477
select: function(item) {
75-
if (this.current && this.current != item) { this.current.blur(); }
78+
if (this.current && this.current != item) { this.current.deselect(); }
7679
this.current = item;
77-
this.current.focus();
80+
this.current.select();
7881
},
79-
82+
8083
adjustFontSize: function(diff) {
8184
this._fontSize = Math.max(30, this._fontSize + 10*diff);
8285
this._port.style.fontSize = this._fontSize + "%";
@@ -126,6 +129,7 @@ MM.App = {
126129
MM.Keyboard.init();
127130
MM.Menu.init(this._port);
128131
MM.Mouse.init(this._port);
132+
MM.Clipboard.init();
129133

130134
window.addEventListener("resize", this);
131135
window.addEventListener("beforeunload", this);

0 commit comments

Comments
 (0)