tw5-graph 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/package.json +39 -0
- package/plugins/graph/DefaultColourMappings.multids +8 -0
- package/plugins/graph/ViewTemplateBodyFilter/graph.tid +5 -0
- package/plugins/graph/ViewTemplateBodyFilter/properties.tid +5 -0
- package/plugins/graph/edges/fields/list.tid +7 -0
- package/plugins/graph/edges/fields/tags.tid +7 -0
- package/plugins/graph/edges/functions/links.tid +8 -0
- package/plugins/graph/edges/functions/transcludes.tid +8 -0
- package/plugins/graph/fieldtypes/filter.js +212 -0
- package/plugins/graph/fieldtypes/list.js +31 -0
- package/plugins/graph/fieldtypes/title.js +27 -0
- package/plugins/graph/filterrunprefixes/cache.js +118 -0
- package/plugins/graph/filters/gettyped.js +26 -0
- package/plugins/graph/graphengine.js +38 -0
- package/plugins/graph/icon.tid +16 -0
- package/plugins/graph/images/drag.tid +13 -0
- package/plugins/graph/macros/action.addedge.tid +16 -0
- package/plugins/graph/macros/edges.typed.tid +54 -0
- package/plugins/graph/macros/graph.view.tid +24 -0
- package/plugins/graph/macros/node.default.tid +9 -0
- package/plugins/graph/macros/nodes.neighbors.tid +62 -0
- package/plugins/graph/macros/nodes.writable.tid +37 -0
- package/plugins/graph/macros/properties.persistent.tid +27 -0
- package/plugins/graph/macros/properties.popup.tid +76 -0
- package/plugins/graph/macros/properties.river.tid +27 -0
- package/plugins/graph/macros/properties.settings.tid +54 -0
- package/plugins/graph/macros/properties.stack.tid +17 -0
- package/plugins/graph/macros/tiddlywiki.files +17 -0
- package/plugins/graph/nodes/neighbors/1.tid +8 -0
- package/plugins/graph/plugin.info +14 -0
- package/plugins/graph/presettypes/edges/fields.tid +18 -0
- package/plugins/graph/presettypes/edges/functions.tid +5 -0
- package/plugins/graph/presettypes/nodes/neighbors.tid +4 -0
- package/plugins/graph/presettypes/nodes/stack.tid +6 -0
- package/plugins/graph/propertytypes/actions.js +15 -0
- package/plugins/graph/propertytypes/boolean.js +23 -0
- package/plugins/graph/propertytypes/color.js +61 -0
- package/plugins/graph/propertytypes/enum.js +25 -0
- package/plugins/graph/propertytypes/filter.js +44 -0
- package/plugins/graph/propertytypes/image.js +185 -0
- package/plugins/graph/propertytypes/number.js +24 -0
- package/plugins/graph/propertytypes/string.js +13 -0
- package/plugins/graph/propertytypes/tiddlywiki.files +17 -0
- package/plugins/graph/readme.tid +5 -0
- package/plugins/graph/relink/attributes.js +52 -0
- package/plugins/graph/relink/attributes.multids +19 -0
- package/plugins/graph/relink/configprefix.js +76 -0
- package/plugins/graph/relink/fieldedgeprefix.js +39 -0
- package/plugins/graph/relink/fields.multids +11 -0
- package/plugins/graph/relink/graphprefix.js +28 -0
- package/plugins/graph/relink/operators.multids +3 -0
- package/plugins/graph/relink/propertyfields.js +81 -0
- package/plugins/graph/relink/utils.js +99 -0
- package/plugins/graph/settings.tid +13 -0
- package/plugins/graph/startup/messages.js +46 -0
- package/plugins/graph/stylesheets/base.css.tid +272 -0
- package/plugins/graph/stylesheets/fullscreen/core.css +11 -0
- package/plugins/graph/stylesheets/fullscreen/core.css.meta +3 -0
- package/plugins/graph/stylesheets/fullscreen.css.tid +48 -0
- package/plugins/graph/stylesheets/popup.css.tid +107 -0
- package/plugins/graph/subfilters.tid +23 -0
- package/plugins/graph/tags/GraphToolbar.tid +2 -0
- package/plugins/graph/tags/SettingsTab.tid +2 -0
- package/plugins/graph/tags/Template.tid +2 -0
- package/plugins/graph/tags/ViewEditTemplate.tid +2 -0
- package/plugins/graph/templates/live-graph.tid +34 -0
- package/plugins/graph/templates/standard-graph.tid +40 -0
- package/plugins/graph/ui/Buttons/center.tid +38 -0
- package/plugins/graph/ui/Buttons/export.tid +65 -0
- package/plugins/graph/ui/Buttons/fullscreen.tid +35 -0
- package/plugins/graph/ui/Buttons/settings.tid +28 -0
- package/plugins/graph/ui/Buttons/tiddlywiki.files +16 -0
- package/plugins/graph/ui/EditTemplate/Edges.tid +22 -0
- package/plugins/graph/ui/EditTemplate/Neighbors.tid +35 -0
- package/plugins/graph/ui/EditTemplate/Nodes.tid +12 -0
- package/plugins/graph/ui/EditTemplate/Popups.tid +27 -0
- package/plugins/graph/ui/EditTemplate/Properties.tid +21 -0
- package/plugins/graph/ui/EditTemplate/tiddlywiki.files +16 -0
- package/plugins/graph/ui/Modals/NewPropertySet.tid +20 -0
- package/plugins/graph/ui/Modals/NewView.tid +20 -0
- package/plugins/graph/ui/Modals/Select.tid +72 -0
- package/plugins/graph/ui/Modals/SelectField.tid +21 -0
- package/plugins/graph/ui/Modals/SelectTiddler.tid +15 -0
- package/plugins/graph/ui/Modals/Tiddler.tid +22 -0
- package/plugins/graph/ui/Modals/TiddlerHeader.tid +15 -0
- package/plugins/graph/ui/PropertyTypes/boolean.tid +11 -0
- package/plugins/graph/ui/PropertyTypes/color.tid +16 -0
- package/plugins/graph/ui/PropertyTypes/enum.tid +15 -0
- package/plugins/graph/ui/PropertyTypes/number.tid +17 -0
- package/plugins/graph/ui/Settings/Engine.tid +23 -0
- package/plugins/graph/ui/Settings/Graphs/EditTemplate.tid +52 -0
- package/plugins/graph/ui/Settings/Graphs.tid +49 -0
- package/plugins/graph/ui/Settings/Properties/ObjectsTypeTemplate.tid +46 -0
- package/plugins/graph/ui/Settings/Properties.tid +14 -0
- package/plugins/graph/ui/Settings/Toolbar.tid +5 -0
- package/plugins/graph/ui/SideBar/Graph.tid +77 -0
- package/plugins/graph/ui/Toolbars/GraphToolbar.tid +15 -0
- package/plugins/graph/ui/ViewTemplate/graph-object.tid +13 -0
- package/plugins/graph/ui/ViewTemplate/graph.tid +11 -0
- package/plugins/graph/ui/ViewTemplate/properties.tid +80 -0
- package/plugins/graph/ui/grouped-tabs.tid +109 -0
- package/plugins/graph/ui/selector.tid +74 -0
- package/plugins/graph/utils/propertyholder.js +80 -0
- package/plugins/graph/utils/widgetiterator.js +47 -0
- package/plugins/graph/utils.js +95 -0
- package/plugins/graph/widgets/action-delay.js +72 -0
- package/plugins/graph/widgets/action-modal.js +121 -0
- package/plugins/graph/widgets/action-typed.js +56 -0
- package/plugins/graph/widgets/action-with.js +70 -0
- package/plugins/graph/widgets/boundingbox.js +126 -0
- package/plugins/graph/widgets/fieldtranscriber/filter.js +93 -0
- package/plugins/graph/widgets/fieldtranscriber/json.js +34 -0
- package/plugins/graph/widgets/fieldtranscriber/plain.js +11 -0
- package/plugins/graph/widgets/fieldtranscriber.js +98 -0
- package/plugins/graph/widgets/graphobject.js +154 -0
- package/plugins/graph/widgets/messagerelay.js +115 -0
- package/plugins/graph/widgets/properties.js +130 -0
- package/plugins/graph/widgets/subclass/edge.js +55 -0
- package/plugins/graph/widgets/subclass/edit-color.js +52 -0
- package/plugins/graph/widgets/subclass/graph.js +380 -0
- package/plugins/graph/widgets/subclass/node.js +56 -0
- package/plugins/graph/widgets/tiddlywiki.files +43 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
title: $:/plugins/flibbles/graph/utils.js
|
|
3
|
+
type: application/javascript
|
|
4
|
+
module-type: library
|
|
5
|
+
|
|
6
|
+
Utility methods used by the graphing widgets.
|
|
7
|
+
|
|
8
|
+
\*/
|
|
9
|
+
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
var Engines = $tw.modules.createClassesFromModules("graphengine");
|
|
13
|
+
|
|
14
|
+
$tw.modules.applyMethods("graphutils", exports);
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
Returns the window for flibbles/graph to use. I do this so that the testing
|
|
18
|
+
framework can mock it out on Node.JS.
|
|
19
|
+
*/
|
|
20
|
+
exports.window = function() { return window; };
|
|
21
|
+
|
|
22
|
+
exports.getEngineMap = function() {
|
|
23
|
+
return Engines;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
exports.getEngine = function(name) {
|
|
27
|
+
var engineMap = exports.getEngineMap();
|
|
28
|
+
return engineMap[name] || null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
exports.getParentProperties = function(widget, type) {
|
|
32
|
+
// For now, we hard-stop at the graph. We don't take properties outside
|
|
33
|
+
// We may some day, but for now, support for that would be frought with
|
|
34
|
+
// complications
|
|
35
|
+
while (widget.parentWidget && widget.graphObjectType !== "graph") {
|
|
36
|
+
widget = widget.parentWidget;
|
|
37
|
+
if (widget.graphPropertiesWidget && widget.type === type) {
|
|
38
|
+
return widget;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return null;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/*** Destructor methods ***/
|
|
45
|
+
|
|
46
|
+
// Aren't we all eventually garbage?
|
|
47
|
+
var eventualGarbage = [];
|
|
48
|
+
var upkeepId;
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
Register an object for destruction. It's ready to pass on once its
|
|
52
|
+
isGarbage method returns true;
|
|
53
|
+
|
|
54
|
+
object requirements:
|
|
55
|
+
.garbageCollect()
|
|
56
|
+
.isGarbage() => true/false
|
|
57
|
+
*/
|
|
58
|
+
exports.registerForGarbageCollection = function(object) {
|
|
59
|
+
var destroy = object.destroy;
|
|
60
|
+
if (destroy) {
|
|
61
|
+
// This object has a destroy method, which means it's probably
|
|
62
|
+
// a widget in ^v5.4.0 TiddlyWiki, which are capable of
|
|
63
|
+
// destruction. We'll use that system instead. It's better.
|
|
64
|
+
object.destroy = function() {
|
|
65
|
+
// Call the widget core destroy method
|
|
66
|
+
destroy.apply(object, arguments);
|
|
67
|
+
// And then our own method
|
|
68
|
+
object.garbageCollect();
|
|
69
|
+
};
|
|
70
|
+
} else {
|
|
71
|
+
// Otherwise, we'll need to keep tabs on this so we can check
|
|
72
|
+
// back in occasionally to see if it should be destroyed.
|
|
73
|
+
if (!upkeepId) {
|
|
74
|
+
upkeepId = setInterval(exports.upkeep, 5000);
|
|
75
|
+
}
|
|
76
|
+
eventualGarbage.push(object);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
exports.upkeep = function() {
|
|
81
|
+
var i = eventualGarbage.length;
|
|
82
|
+
while (i > 0) {
|
|
83
|
+
i--;
|
|
84
|
+
var object = eventualGarbage[i];
|
|
85
|
+
if (object.isGarbage()) {
|
|
86
|
+
eventualGarbage.splice(i, 1);
|
|
87
|
+
object.garbageCollect();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// No managed objects. Let's shut down for now.
|
|
91
|
+
if (eventualGarbage.length == 0) {
|
|
92
|
+
clearInterval(upkeepId);
|
|
93
|
+
upkeepId = null;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Action widget that introduces a delay before executing any nested actions it contains.
|
|
4
|
+
|
|
5
|
+
\*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
|
10
|
+
|
|
11
|
+
var DelayWidget = function(parseTreeNode, options) {
|
|
12
|
+
this.initialise(parseTreeNode, options);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
DelayWidget.prototype = new Widget();
|
|
16
|
+
|
|
17
|
+
DelayWidget.prototype.render = function(parent, nextSibling) {
|
|
18
|
+
this.parentDomNode = parent;
|
|
19
|
+
this.computeAttributes();
|
|
20
|
+
this.execute();
|
|
21
|
+
this.renderChildren(parent, nextSibling);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
DelayWidget.prototype.execute = function() {
|
|
25
|
+
this.ms = this.getAttribute("$ms", 0);
|
|
26
|
+
this.state = this.getAttribute("$state");
|
|
27
|
+
this.makeChildWidgets();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
DelayWidget.prototype.refresh = function(changedTiddlers) {
|
|
31
|
+
var changedAttributes = this.computeAttributes();
|
|
32
|
+
if ($tw.utils.count(changedAttributes) > 0) {
|
|
33
|
+
this.refreshSelf();
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return this.refreshChildren(changedTiddlers);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
var counter = 0;
|
|
40
|
+
|
|
41
|
+
DelayWidget.prototype.invokeAction = function(triggeringWidget, event) {
|
|
42
|
+
// The id is used to make sure the later timed invocation corresponds
|
|
43
|
+
// to this method call here.
|
|
44
|
+
var id = (counter++).toString();
|
|
45
|
+
if (this.state) {
|
|
46
|
+
this.wiki.addTiddler({title: this.state, text: id});
|
|
47
|
+
}
|
|
48
|
+
return setTimeout(execute, this.ms, this, triggeringWidget, event, id);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
DelayWidget.prototype.allowActionPropagation = function() {
|
|
52
|
+
return false;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function execute(delayWidget, triggeringWidget, event, id) {
|
|
56
|
+
var state = delayWidget.state;
|
|
57
|
+
if (state) {
|
|
58
|
+
var text = delayWidget.wiki.getTiddlerText(state);
|
|
59
|
+
if (text !== id) {
|
|
60
|
+
// The state tiddler was touched by another action.
|
|
61
|
+
// So we should not act. We're stale.
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
delayWidget.refreshChildren();
|
|
66
|
+
delayWidget.invokeActions(triggeringWidget, event);
|
|
67
|
+
if (state) {
|
|
68
|
+
delayWidget.wiki.deleteTiddler(state);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
exports["action-delay"] = DelayWidget;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Action widget that creates a modal to select an existing tiddler, or specify a new one. After dialog is confirmed, it will execute any nested actions it contains.
|
|
4
|
+
|
|
5
|
+
\*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
|
10
|
+
var utils = require("../utils.js");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This points to whatever action invoked the current modal. It assumes that
|
|
14
|
+
* only one modal is possible at a time. Probably a safe assumption.
|
|
15
|
+
*/
|
|
16
|
+
var primedWidget;
|
|
17
|
+
|
|
18
|
+
var ModalWidget = function(parseTreeNode, options) {
|
|
19
|
+
this.initialise(parseTreeNode, options);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
ModalWidget.prototype = new Widget();
|
|
23
|
+
|
|
24
|
+
ModalWidget.prototype.render = function(parent, nextSibling) {
|
|
25
|
+
this.parentDomNode = parent;
|
|
26
|
+
this.computeAttributes();
|
|
27
|
+
this.execute();
|
|
28
|
+
this.renderChildren(parent, nextSibling);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
ModalWidget.prototype.execute = function() {
|
|
32
|
+
this.modalTiddler = this.getAttribute("$tiddler");
|
|
33
|
+
this.modal = $tw.modal;
|
|
34
|
+
if (!this.modal || this.modal.wiki !== this.wiki) {
|
|
35
|
+
// Ah, we have a modal working with a subwiki (or a Node.JS system
|
|
36
|
+
// which has no modal). We'll create an overlay of the modal
|
|
37
|
+
// that works with our subwiki instead.
|
|
38
|
+
this.modal = Object.create($tw.modal || null);
|
|
39
|
+
this.modal.wiki = this.wiki;
|
|
40
|
+
}
|
|
41
|
+
this.makeChildWidgets();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
ModalWidget.prototype.refresh = function(changedTiddlers) {
|
|
45
|
+
var changedAttributes = this.computeAttributes();
|
|
46
|
+
if ($tw.utils.count(changedAttributes) > 0) {
|
|
47
|
+
this.refreshSelf();
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return this.refreshChildren(changedTiddlers);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
ModalWidget.prototype.invokeAction = function(triggeringWidget, event) {
|
|
54
|
+
if (!$tw.rootWidget.eventListeners['tm-modal-finish']) {
|
|
55
|
+
$tw.rootWidget.addEventListener("tm-modal-finish", function(finishEvent) {
|
|
56
|
+
if (primedWidget) {
|
|
57
|
+
var value = finishEvent.param;
|
|
58
|
+
primedWidget.setVariable("selection", value);
|
|
59
|
+
primedWidget.refreshChildren();
|
|
60
|
+
primedWidget.invokeActions(primedWidget, event);
|
|
61
|
+
primedWidget = undefined;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
primedWidget = this;
|
|
66
|
+
var variables = Object.create(null);
|
|
67
|
+
for (var key in this.attributes) {
|
|
68
|
+
if (key.charAt(0) !== "$") {
|
|
69
|
+
variables[key] = this.attributes[key];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (this.modalTiddler) {
|
|
73
|
+
var self = this;
|
|
74
|
+
// We need to delay the assignment of click handlers for a few
|
|
75
|
+
// milliseconds. See Issue #43. If we don't, a click event might
|
|
76
|
+
// follow right after the "make modal" which closes the modal
|
|
77
|
+
// immediately.
|
|
78
|
+
delayEventListenerAssignment(function() {
|
|
79
|
+
self.modal.display(self.modalTiddler, {
|
|
80
|
+
variables: variables,
|
|
81
|
+
event: event});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
ModalWidget.prototype.allowActionPropagation = function() {
|
|
88
|
+
return false;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
function delayEventListenerAssignment(method) {
|
|
92
|
+
var assignments = [];
|
|
93
|
+
// We use this requested window instead of just accessing directly
|
|
94
|
+
// so that the test framework can mock it up.
|
|
95
|
+
var window = utils.window();
|
|
96
|
+
var oldListener = window.EventTarget.prototype.addEventListener;
|
|
97
|
+
window.EventTarget.prototype.addEventListener = function(type) {
|
|
98
|
+
var args = Array.prototype.slice.call(arguments);
|
|
99
|
+
if (type === "click") {
|
|
100
|
+
// This is the click which might accidentally apply to the
|
|
101
|
+
// backdrop if we're on mobile. Delay it.
|
|
102
|
+
assignments.push({self: this, args: args});
|
|
103
|
+
} else {
|
|
104
|
+
// Just call through. We don't care about this handler.
|
|
105
|
+
oldListener.apply(this, args);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
try {
|
|
109
|
+
method();
|
|
110
|
+
} finally {
|
|
111
|
+
window.EventTarget.prototype.addEventListener = oldListener;
|
|
112
|
+
}
|
|
113
|
+
setTimeout(function() {
|
|
114
|
+
for (var i = 0; i < assignments.length; i++) {
|
|
115
|
+
var set = assignments[i];
|
|
116
|
+
set.self.addEventListener.apply(set.self, set.args);
|
|
117
|
+
}
|
|
118
|
+
}, 10);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
exports["action-modal"] = ModalWidget;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Creates two action widgets for adding and removing from typed fields.
|
|
4
|
+
|
|
5
|
+
\*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
|
10
|
+
var typeOperators = $tw.modules.getModulesByTypeAsHashmap("fieldtype");
|
|
11
|
+
var relinkPrefix = "$:/config/flibbles/relink/fields/" ;
|
|
12
|
+
|
|
13
|
+
var TypedWidget = new Widget();
|
|
14
|
+
|
|
15
|
+
TypedWidget.render = function(parent, nextSibling) {
|
|
16
|
+
this.computeAttributes();
|
|
17
|
+
this.execute();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
TypedWidget.execute = function() {
|
|
21
|
+
this.actionTiddler = this.getAttribute("$tiddler") || (!this.hasParseTreeNodeAttribute("$tiddler") && this.getVariable("currentTiddler"));
|
|
22
|
+
this.actionField = this.getAttribute("$field");
|
|
23
|
+
this.actionValue = this.getAttribute("$value");
|
|
24
|
+
this.actionTimestamp = this.getAttribute("$timestamp","yes") === "yes";
|
|
25
|
+
this.actionClean = this.getAttribute("$clean","yes") === "yes";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
TypedWidget.invokeAction = function(triggeringWidget, event) {
|
|
29
|
+
var tiddler = this.wiki.getTiddler(this.actionTiddler),
|
|
30
|
+
hasChanged = false,
|
|
31
|
+
options = {wiki: this.wiki, widget: this};
|
|
32
|
+
if (this.actionTiddler && this.actionField) {
|
|
33
|
+
options.suppressTimestamp = !this.actionTimestamp;
|
|
34
|
+
var typeName = this.wiki.getTiddlerText(relinkPrefix + this.actionField, "list");
|
|
35
|
+
var type = typeOperators[typeName];
|
|
36
|
+
if (type) {
|
|
37
|
+
var newValue = type[this.actionMethod](tiddler, this.actionField, this.actionValue, options);
|
|
38
|
+
if (newValue !== undefined) {
|
|
39
|
+
if (newValue === "" && this.actionClean) {
|
|
40
|
+
newValue = undefined;
|
|
41
|
+
}
|
|
42
|
+
options.wiki.setText(this.actionTiddler, this.actionField, null, newValue, options);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
$tw.utils.each(["add", "remove"], function(method) {
|
|
49
|
+
var MethodWidget = function(parseTreeNode, options) {
|
|
50
|
+
this.initialise(parseTreeNode, options);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
MethodWidget.prototype = Object.create(TypedWidget);
|
|
54
|
+
MethodWidget.prototype.actionMethod = method;
|
|
55
|
+
exports["action-" + method + "typed"] = MethodWidget;
|
|
56
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Action widget that executes actions within it while exposing variables representing the mouse's current location.
|
|
4
|
+
|
|
5
|
+
\*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
|
10
|
+
|
|
11
|
+
var WithWidget = function(parseTreeNode, options) {
|
|
12
|
+
this.initialise(parseTreeNode, options);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
WithWidget.prototype = new Widget();
|
|
16
|
+
|
|
17
|
+
WithWidget.prototype.render = function(parent, nextSibling) {
|
|
18
|
+
this.parentDomNode = parent;
|
|
19
|
+
this.computeAttributes();
|
|
20
|
+
this.execute();
|
|
21
|
+
this.renderChildren(parent, nextSibling);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
WithWidget.prototype.execute = function() {
|
|
25
|
+
this.withOffset = this.getAttribute("$offset");
|
|
26
|
+
this.withCanvas = this.getAttribute("$canvas");
|
|
27
|
+
this.makeChildWidgets();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
WithWidget.prototype.refresh = function(changedTiddlers) {
|
|
31
|
+
var changedAttributes = this.computeAttributes();
|
|
32
|
+
for (var attribute in changedAttributes) {
|
|
33
|
+
this.refreshSelf();
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return this.refreshChildren(changedTiddlers);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
WithWidget.prototype.invokeAction = function(triggeringWidget, event) {
|
|
40
|
+
var graph = getGraphWidget(this);
|
|
41
|
+
if (graph) {
|
|
42
|
+
if (this.withOffset) {
|
|
43
|
+
var mouse = graph.mouse,
|
|
44
|
+
x = mouse.x.toString(),
|
|
45
|
+
y = mouse.y.toString();
|
|
46
|
+
this.setVariable(this.withOffset + "-x", x);
|
|
47
|
+
this.setVariable(this.withOffset + "-y", y);
|
|
48
|
+
}
|
|
49
|
+
if (this.withCanvas) {
|
|
50
|
+
var element = graph.graphElement;
|
|
51
|
+
this.setVariable(this.withCanvas + "-width", element.offsetWidth);
|
|
52
|
+
this.setVariable(this.withCanvas + "-height", element.offsetHeight);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.refreshChildren();
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function getGraphWidget(self) {
|
|
60
|
+
var widget = self.parentWidget;
|
|
61
|
+
while (widget) {
|
|
62
|
+
if (widget.graphObjectType === "graph") {
|
|
63
|
+
return widget;
|
|
64
|
+
}
|
|
65
|
+
widget = widget.parentWidget;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
exports["action-with"] = WithWidget;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Widget for creating a DOM element which has strictly controlled dimensions,
|
|
4
|
+
intended for a contained graph.
|
|
5
|
+
|
|
6
|
+
\*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
|
11
|
+
var utils = require("../utils.js");
|
|
12
|
+
|
|
13
|
+
var BoxWidget = function(parseTreeNode, options) {
|
|
14
|
+
this.initialise(parseTreeNode, options);
|
|
15
|
+
utils.registerForGarbageCollection(this);
|
|
16
|
+
this.resizeInstance = this.resize.bind(this);
|
|
17
|
+
this.window = utils.window();
|
|
18
|
+
this.window.addEventListener("resize", this.resizeInstance);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Inherit from the base widget class
|
|
23
|
+
*/
|
|
24
|
+
BoxWidget.prototype = new Widget();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Render this widget into the DOM
|
|
28
|
+
*/
|
|
29
|
+
BoxWidget.prototype.render = function(parent, nextSibling) {
|
|
30
|
+
this.parentDomNode = parent;
|
|
31
|
+
this.computeAttributes();
|
|
32
|
+
this.execute();
|
|
33
|
+
this.domNode = this.document.createElement("div");
|
|
34
|
+
this.domNode.className = this.boxClass;
|
|
35
|
+
parent.insertBefore(this.domNode, nextSibling);
|
|
36
|
+
// Make sure this comes AFTER inserting domNode into the tree,
|
|
37
|
+
// otherwise getBoundingClientRect() will be all zeros.
|
|
38
|
+
this.resize();
|
|
39
|
+
this.renderChildren(this.domNode, null);
|
|
40
|
+
this.domNodes.push(this.domNode);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Compute the internal state of the widget
|
|
45
|
+
*/
|
|
46
|
+
BoxWidget.prototype.execute = function() {
|
|
47
|
+
this.executeClass();
|
|
48
|
+
this.executeDimensions();
|
|
49
|
+
this.makeChildWidgets();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
BoxWidget.prototype.executeDimensions = function() {
|
|
53
|
+
var self = this;
|
|
54
|
+
this.widthFilter = this.wiki.compileFilter(this.getAttribute("width", ""));
|
|
55
|
+
this.heightFilter = this.wiki.compileFilter(this.getAttribute("height", ""));
|
|
56
|
+
// We set up the widget so it only gets values for these variables
|
|
57
|
+
// if needed. This keeps down unncessary calls to window and document,
|
|
58
|
+
// and makes the $graph widget usable on Node, if that ever comes up.
|
|
59
|
+
var variableMethods = {
|
|
60
|
+
// It may be better to use document.body.clientWidth,
|
|
61
|
+
// which doesn't consider the scrollbar.
|
|
62
|
+
windowWidth: function() { return self.window.innerWidth.toString(); },
|
|
63
|
+
windowHeight: function() { return self.window.innerHeight.toString(); },
|
|
64
|
+
boundingLeft: function() { return self.domNode.getBoundingClientRect().left.toString(); },
|
|
65
|
+
boundingTop: function() { return self.domNode.getBoundingClientRect().top.toString(); },
|
|
66
|
+
};
|
|
67
|
+
var variables = {};
|
|
68
|
+
for (var name in variableMethods) {
|
|
69
|
+
Object.defineProperty(variables, name, { get: variableMethods[name] });
|
|
70
|
+
}
|
|
71
|
+
this.dimensionWidget = this.makeFakeWidgetWithVariables(variables);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Selectively refreshes the widget if needed.
|
|
76
|
+
* Returns true if the widget or any of its children needed re-rendering
|
|
77
|
+
*/
|
|
78
|
+
BoxWidget.prototype.refresh = function(changedTiddlers) {
|
|
79
|
+
var changedAttributes = this.computeAttributes();
|
|
80
|
+
var changed = false;
|
|
81
|
+
if (changedAttributes["class"]) {
|
|
82
|
+
this.executeClass();
|
|
83
|
+
this.domNode.className = this.boxClass;
|
|
84
|
+
changed = true;
|
|
85
|
+
}
|
|
86
|
+
if ($tw.utils.count(changedAttributes) > 0) {
|
|
87
|
+
this.executeDimensions();
|
|
88
|
+
changed = true;
|
|
89
|
+
}
|
|
90
|
+
// We always try to resize.
|
|
91
|
+
// The $dimension filters might spit out something different.
|
|
92
|
+
this.resize();
|
|
93
|
+
return this.refreshChildren(changedTiddlers) || changed;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
BoxWidget.prototype.executeClass = function() {
|
|
97
|
+
var nodeClass = "boundingbox";
|
|
98
|
+
var extraClasses = this.getAttribute("class", "");
|
|
99
|
+
if (extraClasses) {
|
|
100
|
+
nodeClass += " " + extraClasses;
|
|
101
|
+
}
|
|
102
|
+
this.boxClass = nodeClass;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
BoxWidget.prototype.resize = function(event) {
|
|
106
|
+
var newWidth = this.widthFilter(null, this.dimensionWidget)[0] || "";
|
|
107
|
+
var newHeight = this.heightFilter(null, this.dimensionWidget)[0] || "";
|
|
108
|
+
var style = this.domNode.style;
|
|
109
|
+
if (newWidth !== style.width) {
|
|
110
|
+
style.width = newWidth;
|
|
111
|
+
}
|
|
112
|
+
if (newHeight !== style.height) {
|
|
113
|
+
style.height = newHeight;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
BoxWidget.prototype.garbageCollect = function() {
|
|
118
|
+
this.window.removeEventListener("resize", this.resizeInstance);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
BoxWidget.prototype.isGarbage = function() {
|
|
122
|
+
var body = this.document.body;
|
|
123
|
+
return !body || !body.contains(this.domNode);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
exports.boundingbox = BoxWidget;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Type for transcribing application/x-tiddler-filter between text and field.
|
|
4
|
+
|
|
5
|
+
Changes \n into " " between runs to mitigate the need for multilines.
|
|
6
|
+
However, it's impossible to guarantee a single line filter if newlines are
|
|
7
|
+
used in operands.
|
|
8
|
+
|
|
9
|
+
\*/
|
|
10
|
+
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
exports.name = "application/x-tiddler-filter";
|
|
14
|
+
|
|
15
|
+
exports.toField = function(text) {
|
|
16
|
+
return filterWhitespaceWalk(text, function(start, end) {
|
|
17
|
+
var substr = text.substring(start, end);
|
|
18
|
+
var change = substr.replaceAll("\n", " ");
|
|
19
|
+
return substr !== change ? change: null;
|
|
20
|
+
}).trim();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
exports.fromField = function(text) {
|
|
24
|
+
return filterWhitespaceWalk(text, function(start, end) {
|
|
25
|
+
var substr = text.substring(start, end);
|
|
26
|
+
var change = substr.replaceAll(" ", "\n");
|
|
27
|
+
return substr !== change ? change: null;
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
var brackets = { "{": "}", "[": "]", "<": ">" };
|
|
32
|
+
|
|
33
|
+
function filterWhitespaceWalk(filterString, method) {
|
|
34
|
+
var p = 0, replaceHead = 0, replacements = [], match, nextBracketPos;
|
|
35
|
+
var whitespaceRegExp = /(\s+)/mg;
|
|
36
|
+
var operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
|
37
|
+
while (p < filterString.length) {
|
|
38
|
+
whitespaceRegExp.lastIndex = p;
|
|
39
|
+
match = whitespaceRegExp.exec(filterString);
|
|
40
|
+
if (match && match.index === p) {
|
|
41
|
+
var end = p + match[0].length;
|
|
42
|
+
var replacement = method(p, end);
|
|
43
|
+
if (replacement !== null) {
|
|
44
|
+
replacements.push(filterString.substring(replaceHead, p));
|
|
45
|
+
replacements.push(replacement);
|
|
46
|
+
replaceHead = end;
|
|
47
|
+
}
|
|
48
|
+
p = end;
|
|
49
|
+
}
|
|
50
|
+
if (p < filterString.length) {
|
|
51
|
+
operandRegExp.lastIndex = p;
|
|
52
|
+
match = operandRegExp.exec(filterString);
|
|
53
|
+
if (!match || match.index !== p) {
|
|
54
|
+
// Broken filter
|
|
55
|
+
return filterString;
|
|
56
|
+
}
|
|
57
|
+
if (match[1]) {
|
|
58
|
+
// Skip the named prefix
|
|
59
|
+
p += match[1].length;
|
|
60
|
+
}
|
|
61
|
+
if (match[4]) {
|
|
62
|
+
// It's a filter operation. Skip by [...]
|
|
63
|
+
if (filterString.charAt(p++) !== "[") {
|
|
64
|
+
return filterString;
|
|
65
|
+
}
|
|
66
|
+
do {
|
|
67
|
+
if(filterString.charAt(p) === "!") {
|
|
68
|
+
filterString.charAt(p++);
|
|
69
|
+
}
|
|
70
|
+
// Get the operator name
|
|
71
|
+
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
|
|
72
|
+
if(nextBracketPos === -1) {
|
|
73
|
+
return filterString;
|
|
74
|
+
}
|
|
75
|
+
nextBracketPos += p;
|
|
76
|
+
var bracket = filterString.charAt(nextBracketPos);
|
|
77
|
+
nextBracketPos = filterString.indexOf(brackets[bracket], p);
|
|
78
|
+
if (nextBracketPos === -1) {
|
|
79
|
+
return filterString;
|
|
80
|
+
}
|
|
81
|
+
p = nextBracketPos + 1;
|
|
82
|
+
} while (filterString.charAt(p++) !== "]");
|
|
83
|
+
} else {
|
|
84
|
+
p = match.index + match[0].length;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (replacements.length > 0) {
|
|
89
|
+
replacements.push(filterString.substr(replaceHead));
|
|
90
|
+
return replacements.join("");
|
|
91
|
+
}
|
|
92
|
+
return filterString;
|
|
93
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*\
|
|
2
|
+
|
|
3
|
+
Type for transcribing application/json between text and field.
|
|
4
|
+
|
|
5
|
+
Focuses on keeping dataTiddler style pretty printing for text, but puts all
|
|
6
|
+
json on one line for fields.
|
|
7
|
+
|
|
8
|
+
\*/
|
|
9
|
+
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
exports.name = "application/json";
|
|
13
|
+
|
|
14
|
+
exports.toField = function(text) {
|
|
15
|
+
if (!text) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return JSON.stringify(JSON.parse(text));
|
|
20
|
+
} catch {
|
|
21
|
+
return "{}";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
exports.fromField = function(text) {
|
|
26
|
+
if (!text) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(JSON.parse(text), null, $tw.config.preferences.jsonSpaces);
|
|
31
|
+
} catch {
|
|
32
|
+
return "{}";
|
|
33
|
+
}
|
|
34
|
+
};
|