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 @@ + @@ -76,6 +77,799 @@

My Mind

+

+ Icons + +

+ GitHub project page 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(); }