diff --git a/Makefile b/Makefile
index 00f5ac5..32182d5 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,7 @@ SOURCES = src/mm.js \
src/ui.value.js \
src/ui.status.js \
src/ui.color.js \
+ src/ui.icon.js \
src/ui.help.js \
src/ui.io.js \
src/ui.backend.js \
diff --git a/css/shape.css b/css/shape.css
index 25add8c..aa21a84 100644
--- a/css/shape.css
+++ b/css/shape.css
@@ -36,3 +36,8 @@
border-width: 2px;
font-size: 120%;
}
+
+.item .icon {
+ margin: 0 0.5em 0 0;
+ font-size: x-large;
+}
diff --git a/css/ui.css b/css/ui.css
index d5e9cfe..b010bc5 100644
--- a/css/ui.css
+++ b/css/ui.css
@@ -191,3 +191,9 @@ button:not(:disabled) {
.ui#io button:nth-child(even) {
float: right;
}
+
+.ui select.fa-select ,
+.ui select.fa-select option {
+ font-family: fontAwesome;
+ font-size: x-large;
+}
diff --git a/examples/features.mymind b/examples/features.mymind
index c0325fa..cef13e6 100644
--- a/examples/features.mymind
+++ b/examples/features.mymind
@@ -1,214 +1,215 @@
{
- "root": {
- "id": "ujfdpxoz",
- "text": "My Mind
Features",
- "layout": "map",
- "children": [
- {
- "id": "cmetfpcz",
- "text": "Basic features",
- "side": "right",
- "color": "#e33",
- "children": [
- {
- "id": "oufzmqvy",
- "text": "Node selection, manipulation"
- },
- {
- "id": "hsthllrc",
- "text": "Rich text editing"
- },
- {
- "id": "spnaifjx",
- "text": "Auto-linking"
- },
- {
- "id": "meeftorp",
- "text": "Colors"
- },
- {
- "id": "pphsgpvu",
- "text": "Zooming"
- },
- {
- "id": "avzourxv",
- "text": "Shapes"
- },
- {
- "id": "bhszysqp",
- "text": "Full keyboard control"
- },
- {
- "id": "zcrefcfz",
- "text": "Folding",
- "collapsed": 1,
- "children": [
- {
- "id": "znuvtncs",
- "text": "Collapsing"
- },
- {
- "id": "dbbxxzzs",
- "text": "Expanding"
- }
- ]
- }
- ]
- },
- {
- "id": "nkrfhzwg",
- "text": "I/O",
- "side": "left",
- "color": "#3e3",
- "children": [
- {
- "id": "fpbtygky",
- "text": "Multiple backends",
- "children": [
- {
- "id": "ezwvmtko",
- "text": "Local storage"
- },
- {
- "id": "sczzbjgf",
- "text": "Local filesystem"
- },
- {
- "id": "megldbuw",
- "text": "Firebase",
- "children": [
- {
- "id": "krsvhker",
- "text": "Realtime sync"
- }
- ]
- },
- {
- "id": "rmxmrpum",
- "text": "Google Drive"
- },
- {
- "id": "eiocvrju",
- "text": "REST DAV-like"
- }
- ]
- },
- {
- "id": "bmatbsiz",
- "text": "Multiple file formats",
- "children": [
- {
- "id": "lyzyawpn",
- "text": "Native"
- },
- {
- "id": "chfwbjrp",
- "text": "Freemind"
- },
- {
- "id": "vhsldewc",
- "text": "Mind Architect"
- },
- {
- "id": "whmzheiy",
- "text": "MindMup"
- }
- ]
- },
- {
- "id": "hjialeal",
- "text": "Permalinks"
- }
- ]
- },
- {
- "id": "scoyihcg",
- "text": "Advanced features",
- "side": "right",
- "color": "#33e",
- "children": [
- {
- "id": "bvfjpmrm",
- "text": "Numerical node values",
- "value": "sum",
- "children": [
- {
- "id": "fppldjcr",
- "text": "Constant values",
- "value": 3.14
- },
- {
- "id": "lhqwyqwq",
- "text": "Functions",
- "value": 42
- }
- ]
- },
- {
- "id": "pjqwdjad",
- "text": "Bool node statuses",
- "value": "maybe",
- "status": "computed",
- "children": [
- {
- "id": "jsrxnhgy",
- "text": "Yes/No",
- "value": "yes",
- "status": "no"
- },
- {
- "id": "oqzwbzvf",
- "text": "Auto-propagation to parent",
- "value": "no",
- "status": "computed"
- }
- ]
- },
- {
- "id": "adqojbln",
- "text": "Variable layouts",
- "layout": "tree-right",
- "children": [
- {
- "id": "uglsvbbi",
- "text": "Map"
- },
- {
- "id": "mvqxmutu",
- "text": "Graph"
- },
- {
- "id": "uonrboqe",
- "text": "Tree"
- }
- ]
- },
- {
- "id": "rsruxymh",
- "text": "Infinite undo/redo"
- },
- {
- "id": "jaalsyvs",
- "text": "Export to image"
- }
- ]
- },
- {
- "id": "rbahwqul",
- "text": "Planned features",
- "side": "left",
- "color": "#d3d",
- "children": [
- {
- "id": "vcmxdsvj",
- "text": "Custom styles"
- },
- {
- "id": "bsddmwik",
- "text": "Custom icons"
- }
- ]
- }
- ]
- }
+ "root": {
+ "id": "ujfdpxoz",
+ "text": "My Mind
Features",
+ "layout": "map",
+ "children": [
+ {
+ "id": "cmetfpcz",
+ "text": "Basic features",
+ "side": "right",
+ "color": "#e33",
+ "children": [
+ {
+ "id": "oufzmqvy",
+ "text": "Node selection, manipulation"
+ },
+ {
+ "id": "hsthllrc",
+ "text": "Rich text editing"
+ },
+ {
+ "id": "spnaifjx",
+ "text": "Auto-linking"
+ },
+ {
+ "id": "meeftorp",
+ "text": "Colors"
+ },
+ {
+ "id": "pphsgpvu",
+ "text": "Zooming"
+ },
+ {
+ "id": "avzourxv",
+ "text": "Shapes"
+ },
+ {
+ "id": "bhszysqp",
+ "text": "Full keyboard control"
+ },
+ {
+ "id": "zcrefcfz",
+ "text": "Folding",
+ "collapsed": 1,
+ "children": [
+ {
+ "id": "znuvtncs",
+ "text": "Collapsing"
+ },
+ {
+ "id": "dbbxxzzs",
+ "text": "Expanding"
+ }
+ ]
+ },
+ {
+ "id": "pkbumzof",
+ "text": "Icons",
+ "icon": "fa-hand-o-right"
+ }
+ ]
+ },
+ {
+ "id": "nkrfhzwg",
+ "text": "I/O",
+ "side": "left",
+ "color": "#3e3",
+ "children": [
+ {
+ "id": "fpbtygky",
+ "text": "Multiple backends",
+ "children": [
+ {
+ "id": "ezwvmtko",
+ "text": "Local storage"
+ },
+ {
+ "id": "sczzbjgf",
+ "text": "Local filesystem"
+ },
+ {
+ "id": "megldbuw",
+ "text": "Firebase",
+ "children": [
+ {
+ "id": "krsvhker",
+ "text": "Realtime sync"
+ }
+ ]
+ },
+ {
+ "id": "rmxmrpum",
+ "text": "Google Drive"
+ },
+ {
+ "id": "eiocvrju",
+ "text": "REST DAV-like"
+ }
+ ]
+ },
+ {
+ "id": "bmatbsiz",
+ "text": "Multiple file formats",
+ "children": [
+ {
+ "id": "lyzyawpn",
+ "text": "Native"
+ },
+ {
+ "id": "chfwbjrp",
+ "text": "Freemind"
+ },
+ {
+ "id": "vhsldewc",
+ "text": "Mind Architect"
+ },
+ {
+ "id": "whmzheiy",
+ "text": "MindMup"
+ }
+ ]
+ },
+ {
+ "id": "hjialeal",
+ "text": "Permalinks"
+ }
+ ]
+ },
+ {
+ "id": "scoyihcg",
+ "text": "Advanced features",
+ "side": "right",
+ "color": "#33e",
+ "children": [
+ {
+ "id": "bvfjpmrm",
+ "text": "Numerical node values",
+ "value": "sum",
+ "children": [
+ {
+ "id": "fppldjcr",
+ "text": "Constant values",
+ "value": 3.14
+ },
+ {
+ "id": "lhqwyqwq",
+ "text": "Functions",
+ "value": 42
+ }
+ ]
+ },
+ {
+ "id": "pjqwdjad",
+ "text": "Bool node statuses",
+ "value": "maybe",
+ "status": "computed",
+ "children": [
+ {
+ "id": "jsrxnhgy",
+ "text": "Yes/No",
+ "value": "yes",
+ "status": "no"
+ },
+ {
+ "id": "oqzwbzvf",
+ "text": "Auto-propagation to parent",
+ "value": "no",
+ "status": "computed"
+ }
+ ]
+ },
+ {
+ "id": "adqojbln",
+ "text": "Variable layouts",
+ "layout": "tree-right",
+ "children": [
+ {
+ "id": "uglsvbbi",
+ "text": "Map"
+ },
+ {
+ "id": "mvqxmutu",
+ "text": "Graph"
+ },
+ {
+ "id": "uonrboqe",
+ "text": "Tree"
+ }
+ ]
+ },
+ {
+ "id": "rsruxymh",
+ "text": "Infinite undo/redo"
+ },
+ {
+ "id": "jaalsyvs",
+ "text": "Export to image"
+ }
+ ]
+ },
+ {
+ "id": "rbahwqul",
+ "text": "Planned features",
+ "side": "left",
+ "color": "#d3d",
+ "children": [
+ {
+ "id": "vcmxdsvj",
+ "text": "Custom styles"
+ }
+ ]
+ }
+ ]
+ }
}
diff --git a/index.html b/index.html
index 1cf0ad3..adfae7e 100644
--- a/index.html
+++ b/index.html
@@ -11,6 +11,7 @@
+
+ Icons + +
+ diff --git a/my-mind.js b/my-mind.js index 67001af..f6ec6a9 100644 --- a/my-mind.js +++ b/my-mind.js @@ -299,6 +299,7 @@ MM.Item = function() { this._value = null; this._status = null; this._side = null; /* side preference */ + this._icon = null; this._id = MM.generateId(); this._oldText = ""; @@ -311,6 +312,7 @@ MM.Item = function() { node: document.createElement("li"), content: document.createElement("div"), status: document.createElement("span"), + icon: document.createElement("span"), value: document.createElement("span"), text: document.createElement("div"), children: document.createElement("ul"), @@ -320,6 +322,7 @@ MM.Item = function() { this._dom.node.classList.add("item"); this._dom.content.classList.add("content"); this._dom.status.classList.add("status"); + this._dom.icon.classList.add("icon"); this._dom.value.classList.add("value"); this._dom.text.classList.add("text"); this._dom.toggle.classList.add("toggle"); @@ -358,6 +361,7 @@ MM.Item.prototype.toJSON = function() { if (this._side) { data.side = this._side; } if (this._color) { data.color = this._color; } + if (this._icon) { data.icon = this._icon; } if (this._value) { data.value = this._value; } if (this._status) { data.status = this._status; } if (this._layout) { data.layout = this._layout.id; } @@ -378,6 +382,7 @@ MM.Item.prototype.fromJSON = function(data) { if (data.id) { this._id = data.id; } if (data.side) { this._side = data.side; } if (data.color) { this._color = data.color; } + if (data.icon) { this._icon = data.icon; } if (data.value) { this._value = data.value; } if (data.status) { this._status = data.status; @@ -409,6 +414,11 @@ MM.Item.prototype.mergeWith = function(data) { dirty = 2; } + if (this._icon != data.icon) { + this._icon = data.icon; + dirty = 1; + } + if (this._value != data.value) { this._value = data.value; dirty = 1; @@ -492,6 +502,7 @@ MM.Item.prototype.update = function(doNotRecurse) { } this._updateStatus(); + this._updateIcon(); this._updateValue(); this._dom.node.classList[this._collapsed ? "add" : "remove"]("collapsed"); @@ -563,6 +574,15 @@ MM.Item.prototype.getStatus = function() { return this._status; } +MM.Item.prototype.setIcon = function(icon) { + this._icon = icon; + return this.update(); +} + +MM.Item.prototype.getIcon = function() { + return this._icon; +} + MM.Item.prototype.getComputedStatus = function() { return this._computed.status; } @@ -791,6 +811,21 @@ MM.Item.prototype._updateStatus = function() { break; } } +MM.Item.prototype._updateIcon = function() { + this._dom.icon.className = "icon"; + this._dom.icon.style.display = ""; + + var icon = this._icon; + if (icon) + { + this._dom.icon.classList.add('fa'); + this._dom.icon.classList.add(icon); + this._computed.icon = true; + } else { + this._computed.icon = null; + this._dom.icon.style.display = "none"; + } +} MM.Item.prototype._updateValue = function() { this._dom.value.style.display = ""; @@ -1397,6 +1432,19 @@ MM.Action.SetStatus.prototype.undo = function() { this._item.setStatus(this._oldStatus); } +MM.Action.SetIcon = function(item, icon) { + this._item = item; + this._icon = icon; + this._oldIcon = item.getIcon(); +} +MM.Action.SetIcon.prototype = Object.create(MM.Action.prototype); +MM.Action.SetIcon.prototype.perform = function() { + this._item.setIcon(this._icon); +} +MM.Action.SetIcon.prototype.undo = function() { + this._item.setIcon(this._oldIcon); +} + MM.Action.SetSide = function(item, side) { this._item = item; this._side = side; @@ -2214,10 +2262,12 @@ MM.Layout._alignItem = function(item, side) { switch (side) { case "left": + dom.content.insertBefore(dom.icon, dom.content.firstChild); dom.content.appendChild(dom.value); dom.content.appendChild(dom.status); break; case "right": + dom.content.insertBefore(dom.icon, dom.content.firstChild); dom.content.insertBefore(dom.value, dom.content.firstChild); dom.content.insertBefore(dom.status, dom.content.firstChild); break; @@ -2977,6 +3027,7 @@ MM.Format.MMA._parseAttributes = function(node, parent) { json.color = "#" + [r,g,b].join(""); } + json.icon = node.getAttribute("icon"); return json; } @@ -2994,6 +3045,9 @@ MM.Format.MMA._serializeAttributes = function(doc, json) { var b = new Array(5).join(parts[3]); elm.setAttribute("color", "#" + [r,g,b].join("")); } + if (json.icon) { + elm.setAttribute("icon", json.icon); + } return elm; } @@ -3024,7 +3078,8 @@ MM.Format.Mup._MupToMM = function(item) { var json = { text: MM.Format.nl2br(item.title), id: item.id, - shape: "box" + shape: "box", + icon: item.icon } if (item.attr && item.attr.style && item.attr.style.background) { @@ -3061,6 +3116,7 @@ MM.Format.Mup._MMtoMup = function(item, side) { var result = { id: item.id, title: MM.Format.br2nl(item.text), + icon: item.icon, attr: {} } if (item.color) { @@ -3702,6 +3758,7 @@ MM.UI = function() { this._layout = new MM.UI.Layout(); this._shape = new MM.UI.Shape(); + this._icon = new MM.UI.Icon(); this._color = new MM.UI.Color(); this._value = new MM.UI.Value(); this._status = new MM.UI.Status(); @@ -3767,6 +3824,7 @@ MM.UI.prototype.getWidth = function() { MM.UI.prototype._update = function() { this._layout.update(); this._shape.update(); + this._icon.update(); this._value.update(); this._status.update(); } @@ -3894,6 +3952,19 @@ MM.UI.Color.prototype.handleEvent = function(e) { var action = new MM.Action.SetColor(MM.App.current, color); MM.App.action(action); } +MM.UI.Icon = function() { + this._select = document.querySelector("#icons"); + this._select.addEventListener("change", this); +} + +MM.UI.Icon.prototype.update = function() { + this._select.value = MM.App.current.getIcon() || ""; +} + +MM.UI.Icon.prototype.handleEvent = function(e) { + var action = new MM.Action.SetIcon(MM.App.current, this._select.value || null); + MM.App.action(action); +} MM.UI.Help = function() { this._node = document.querySelector("#help"); this._map = { diff --git a/screenshot.png b/screenshot.png index 59f8fb3..a152702 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/src/action.js b/src/action.js index 9880886..f3ec28b 100644 --- a/src/action.js +++ b/src/action.js @@ -187,6 +187,19 @@ MM.Action.SetStatus.prototype.undo = function() { this._item.setStatus(this._oldStatus); } +MM.Action.SetIcon = function(item, icon) { + this._item = item; + this._icon = icon; + this._oldIcon = item.getIcon(); +} +MM.Action.SetIcon.prototype = Object.create(MM.Action.prototype); +MM.Action.SetIcon.prototype.perform = function() { + this._item.setIcon(this._icon); +} +MM.Action.SetIcon.prototype.undo = function() { + this._item.setIcon(this._oldIcon); +} + MM.Action.SetSide = function(item, side) { this._item = item; this._side = side; diff --git a/src/format.mma.js b/src/format.mma.js index 08e3d9d..a5c50cc 100644 --- a/src/format.mma.js +++ b/src/format.mma.js @@ -31,6 +31,7 @@ MM.Format.MMA._parseAttributes = function(node, parent) { json.color = "#" + [r,g,b].join(""); } + json.icon = node.getAttribute("icon"); return json; } @@ -48,6 +49,9 @@ MM.Format.MMA._serializeAttributes = function(doc, json) { var b = new Array(5).join(parts[3]); elm.setAttribute("color", "#" + [r,g,b].join("")); } + if (json.icon) { + elm.setAttribute("icon", json.icon); + } return elm; } diff --git a/src/format.mup.js b/src/format.mup.js index 68d04e8..880bbae 100644 --- a/src/format.mup.js +++ b/src/format.mup.js @@ -25,7 +25,8 @@ MM.Format.Mup._MupToMM = function(item) { var json = { text: MM.Format.nl2br(item.title), id: item.id, - shape: "box" + shape: "box", + icon: item.icon } if (item.attr && item.attr.style && item.attr.style.background) { @@ -62,6 +63,7 @@ MM.Format.Mup._MMtoMup = function(item, side) { var result = { id: item.id, title: MM.Format.br2nl(item.text), + icon: item.icon, attr: {} } if (item.color) { diff --git a/src/item.js b/src/item.js index 8452289..6ff477d 100644 --- a/src/item.js +++ b/src/item.js @@ -10,6 +10,7 @@ MM.Item = function() { this._value = null; this._status = null; this._side = null; /* side preference */ + this._icon = null; this._id = MM.generateId(); this._oldText = ""; @@ -22,6 +23,7 @@ MM.Item = function() { node: document.createElement("li"), content: document.createElement("div"), status: document.createElement("span"), + icon: document.createElement("span"), value: document.createElement("span"), text: document.createElement("div"), children: document.createElement("ul"), @@ -31,6 +33,7 @@ MM.Item = function() { this._dom.node.classList.add("item"); this._dom.content.classList.add("content"); this._dom.status.classList.add("status"); + this._dom.icon.classList.add("icon"); this._dom.value.classList.add("value"); this._dom.text.classList.add("text"); this._dom.toggle.classList.add("toggle"); @@ -69,6 +72,7 @@ MM.Item.prototype.toJSON = function() { if (this._side) { data.side = this._side; } if (this._color) { data.color = this._color; } + if (this._icon) { data.icon = this._icon; } if (this._value) { data.value = this._value; } if (this._status) { data.status = this._status; } if (this._layout) { data.layout = this._layout.id; } @@ -89,6 +93,7 @@ MM.Item.prototype.fromJSON = function(data) { if (data.id) { this._id = data.id; } if (data.side) { this._side = data.side; } if (data.color) { this._color = data.color; } + if (data.icon) { this._icon = data.icon; } if (data.value) { this._value = data.value; } if (data.status) { this._status = data.status; @@ -120,6 +125,11 @@ MM.Item.prototype.mergeWith = function(data) { dirty = 2; } + if (this._icon != data.icon) { + this._icon = data.icon; + dirty = 1; + } + if (this._value != data.value) { this._value = data.value; dirty = 1; @@ -203,6 +213,7 @@ MM.Item.prototype.update = function(doNotRecurse) { } this._updateStatus(); + this._updateIcon(); this._updateValue(); this._dom.node.classList[this._collapsed ? "add" : "remove"]("collapsed"); @@ -274,6 +285,15 @@ MM.Item.prototype.getStatus = function() { return this._status; } +MM.Item.prototype.setIcon = function(icon) { + this._icon = icon; + return this.update(); +} + +MM.Item.prototype.getIcon = function() { + return this._icon; +} + MM.Item.prototype.getComputedStatus = function() { return this._computed.status; } @@ -502,6 +522,21 @@ MM.Item.prototype._updateStatus = function() { break; } } +MM.Item.prototype._updateIcon = function() { + this._dom.icon.className = "icon"; + this._dom.icon.style.display = ""; + + var icon = this._icon; + if (icon) + { + this._dom.icon.classList.add('fa'); + this._dom.icon.classList.add(icon); + this._computed.icon = true; + } else { + this._computed.icon = null; + this._dom.icon.style.display = "none"; + } +} MM.Item.prototype._updateValue = function() { this._dom.value.style.display = ""; diff --git a/src/layout.js b/src/layout.js index b0644c7..8eec5aa 100644 --- a/src/layout.js +++ b/src/layout.js @@ -136,10 +136,12 @@ MM.Layout._alignItem = function(item, side) { switch (side) { case "left": + dom.content.insertBefore(dom.icon, dom.content.firstChild); dom.content.appendChild(dom.value); dom.content.appendChild(dom.status); break; case "right": + dom.content.insertBefore(dom.icon, dom.content.firstChild); dom.content.insertBefore(dom.value, dom.content.firstChild); dom.content.insertBefore(dom.status, dom.content.firstChild); break; diff --git a/src/ui.icon.js b/src/ui.icon.js new file mode 100644 index 0000000..3f01ed3 --- /dev/null +++ b/src/ui.icon.js @@ -0,0 +1,13 @@ +MM.UI.Icon = function() { + this._select = document.querySelector("#icons"); + this._select.addEventListener("change", this); +} + +MM.UI.Icon.prototype.update = function() { + this._select.value = MM.App.current.getIcon() || ""; +} + +MM.UI.Icon.prototype.handleEvent = function(e) { + var action = new MM.Action.SetIcon(MM.App.current, this._select.value || null); + MM.App.action(action); +} diff --git a/src/ui.js b/src/ui.js index c649d7c..6f11eef 100644 --- a/src/ui.js +++ b/src/ui.js @@ -5,6 +5,7 @@ MM.UI = function() { this._layout = new MM.UI.Layout(); this._shape = new MM.UI.Shape(); + this._icon = new MM.UI.Icon(); this._color = new MM.UI.Color(); this._value = new MM.UI.Value(); this._status = new MM.UI.Status(); @@ -70,6 +71,7 @@ MM.UI.prototype.getWidth = function() { MM.UI.prototype._update = function() { this._layout.update(); this._shape.update(); + this._icon.update(); this._value.update(); this._status.update(); }