mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-27 12:07:19 +00:00
Initial tranche of the plugin implementation
This first commit adds some of the code needed for the plugin mechanism, but it isn't all fully operational yet. I'm merging the branch so that I can get some other pre-requisites done quickly on the main branch, and then plan to return to this branch.
This commit is contained in:
parent
62762a370c
commit
8e6647b615
53
core/boot.js
53
core/boot.js
@ -241,8 +241,27 @@ $tw.modules.registerTypedModule = function(name,moduleType,moduleExports) {
|
|||||||
Register all the module tiddlers that have a module type
|
Register all the module tiddlers that have a module type
|
||||||
*/
|
*/
|
||||||
$tw.modules.registerTypedModules = function() {
|
$tw.modules.registerTypedModules = function() {
|
||||||
for(var title in $tw.wiki.tiddlers) {
|
var title, tiddler;
|
||||||
var tiddler = $tw.wiki.getTiddler(title);
|
// Execute and register any modules from plugins
|
||||||
|
for(title in $tw.wiki.pluginTiddlers) {
|
||||||
|
tiddler = $tw.wiki.getTiddler(title);
|
||||||
|
if(!(title in $tw.wiki.tiddlers)) {
|
||||||
|
if(tiddler.fields.type === "application/javascript" && tiddler.fields["module-type"] !== undefined) {
|
||||||
|
// Execute the module
|
||||||
|
var source = [
|
||||||
|
"(function(module,exports,require) {",
|
||||||
|
tiddler.fields.text,
|
||||||
|
"})"
|
||||||
|
];
|
||||||
|
$tw.modules.define(tiddler.fields.text,tiddler.fields["module-type"],window.eval(source.join("")));
|
||||||
|
// Register the module
|
||||||
|
$tw.modules.registerTypedModule(title,tiddler.fields["module-type"],$tw.modules.execute(title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Register any modules in ordinary tiddlers
|
||||||
|
for(title in $tw.wiki.tiddlers) {
|
||||||
|
tiddler = $tw.wiki.getTiddler(title);
|
||||||
if(tiddler.fields.type === "application/javascript" && tiddler.fields["module-type"] !== undefined) {
|
if(tiddler.fields.type === "application/javascript" && tiddler.fields["module-type"] !== undefined) {
|
||||||
$tw.modules.registerTypedModule(title,tiddler.fields["module-type"],$tw.modules.execute(title));
|
$tw.modules.registerTypedModule(title,tiddler.fields["module-type"],$tw.modules.execute(title));
|
||||||
}
|
}
|
||||||
@ -370,10 +389,37 @@ $tw.Wiki.prototype.addTiddlers = function(tiddlers,isShadow) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Install tiddlers contained in plugin tiddlers
|
||||||
|
*/
|
||||||
|
$tw.Wiki.prototype.installPlugins = function() {
|
||||||
|
this.plugins = {}; // Hashmap of plugin information by title
|
||||||
|
this.pluginTiddlers = {}; // Hashmap of constituent tiddlers from plugins by title
|
||||||
|
// Collect up all the plugin tiddlers
|
||||||
|
for(var title in this.tiddlers) {
|
||||||
|
var tiddler = this.tiddlers[title];
|
||||||
|
if(tiddler.fields.type === "application/x-tiddlywiki-plugin") {
|
||||||
|
// Save the plugin information
|
||||||
|
var pluginInfo = this.plugins[title] = JSON.parse(tiddler.fields.text);
|
||||||
|
// Extract the constituent tiddlers
|
||||||
|
for(var t=0; t<pluginInfo.tiddlers.length; t++) {
|
||||||
|
var constituentTiddler = pluginInfo.tiddlers[t];
|
||||||
|
// Don't overwrite tiddlers that already exist
|
||||||
|
if(!(constituentTiddler.title in this.pluginTiddlers)) {
|
||||||
|
// Save the tiddler object
|
||||||
|
this.pluginTiddlers[constituentTiddler.title] = new $tw.Tiddler(constituentTiddler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$tw.Wiki.prototype.getTiddler = function(title) {
|
$tw.Wiki.prototype.getTiddler = function(title) {
|
||||||
var t = this.tiddlers[title];
|
var t = this.tiddlers[title];
|
||||||
if(t instanceof $tw.Tiddler) {
|
if(t instanceof $tw.Tiddler) {
|
||||||
return t;
|
return t;
|
||||||
|
} else if(title in this.pluginTiddlers) {
|
||||||
|
return this.pluginTiddlers[title];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -681,6 +727,9 @@ $tw.loadTiddlersFromFolder(path.resolve($tw.boot.wikiPath,$tw.config.wikiTiddler
|
|||||||
|
|
||||||
/////////////////////////// Final initialisation
|
/////////////////////////// Final initialisation
|
||||||
|
|
||||||
|
// Install plugins
|
||||||
|
$tw.wiki.installPlugins();
|
||||||
|
|
||||||
// Register typed modules from the tiddlers we've just loaded
|
// Register typed modules from the tiddlers we've just loaded
|
||||||
$tw.modules.registerTypedModules();
|
$tw.modules.registerTypedModules();
|
||||||
|
|
||||||
|
9
core/plugins/TemporaryTestPlugin.tid
Normal file
9
core/plugins/TemporaryTestPlugin.tid
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
title: TemporaryTestPlugin
|
||||||
|
type: application/x-tiddlywiki-plugin
|
||||||
|
|
||||||
|
{
|
||||||
|
"tiddlers": [
|
||||||
|
{"title": "Test1", "text": "Some text"},
|
||||||
|
{"title": "Test2", "type": "Some more text"}
|
||||||
|
]
|
||||||
|
}
|
11
core/plugins/zoomigator/plugin.tiddlywiki
Normal file
11
core/plugins/zoomigator/plugin.tiddlywiki
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"title": "Zoomigator",
|
||||||
|
"pluginType": "plugin"
|
||||||
|
"description": "Enables touch devices to swipe in from the right to zoom out to a birds eye view for navigating within the page",
|
||||||
|
"author": "JeremyRuston",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"coreVersion": ">=5.0.0",
|
||||||
|
"source": "http://tiddlywiki.com/core/plugins/zoomigator"
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*\
|
/*\
|
||||||
title: $:/core/modules/macros/zoomer.js
|
title: $:/core/plugins/zoomigator/zoomigator.js
|
||||||
type: application/javascript
|
type: application/javascript
|
||||||
module-type: macro
|
module-type: macro
|
||||||
|
|
||||||
@ -13,22 +13,22 @@ Zooming navigator macro
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
exports.info = {
|
exports.info = {
|
||||||
name: "zoomer",
|
name: "zoomigator",
|
||||||
params: {
|
params: {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.startZoomer = function(x,y) {
|
exports.startZoomigator = function(x,y) {
|
||||||
this.inZoomer = true;
|
this.inZoomigator = true;
|
||||||
this.startX = x;
|
this.startX = x;
|
||||||
this.startY = y;
|
this.startY = y;
|
||||||
$tw.utils.addClass(document.body,"in-zoomer");
|
$tw.utils.addClass(document.body,"in-zoomigator");
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Zoom the body element given a touch/mouse position in screen coordinates
|
Zoom the body element given a touch/mouse position in screen coordinates
|
||||||
*/
|
*/
|
||||||
exports.hoverZoomer = function(x,y) {
|
exports.hoverZoomigator = function(x,y) {
|
||||||
// Put the transform origin at the top in the middle
|
// Put the transform origin at the top in the middle
|
||||||
document.body.style[$tw.browser.transformorigin] = "50% 0";
|
document.body.style[$tw.browser.transformorigin] = "50% 0";
|
||||||
// Some shortcuts
|
// Some shortcuts
|
||||||
@ -68,37 +68,37 @@ exports.hoverZoomer = function(x,y) {
|
|||||||
document.body.style[$tw.browser.transform] = transform;
|
document.body.style[$tw.browser.transform] = transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.stopZoomer = function() {
|
exports.stopZoomigator = function() {
|
||||||
var newScrollY = this.yFactor * (this.bodyHeight - this.windowHeight);
|
var newScrollY = this.yFactor * (this.bodyHeight - this.windowHeight);
|
||||||
this.inZoomer = false;
|
this.inZoomigator = false;
|
||||||
window.scrollTo(0,newScrollY);
|
window.scrollTo(0,newScrollY);
|
||||||
document.body.style[$tw.browser.transform] = "translateY(" + newScrollY * this.xFactor + "px) " +
|
document.body.style[$tw.browser.transform] = "translateY(" + newScrollY * this.xFactor + "px) " +
|
||||||
"scale(" + this.scale + ") " +
|
"scale(" + this.scale + ") " +
|
||||||
"translateY(" + ((this.windowHeight / this.scale) - this.bodyHeight) * this.yFactor * this.xFactor + "px)";
|
"translateY(" + ((this.windowHeight / this.scale) - this.bodyHeight) * this.yFactor * this.xFactor + "px)";
|
||||||
$tw.utils.removeClass(document.body,"in-zoomer");
|
$tw.utils.removeClass(document.body,"in-zoomigator");
|
||||||
document.body.style[$tw.browser.transform] = "translateY(0) scale(1) translateY(0)";
|
document.body.style[$tw.browser.transform] = "translateY(0) scale(1) translateY(0)";
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.handleEvent = function(event) {
|
exports.handleEvent = function(event) {
|
||||||
switch(event.type) {
|
switch(event.type) {
|
||||||
case "touchstart":
|
case "touchstart":
|
||||||
this.startZoomer(event.touches[0].clientX,event.touches[0].clientY);
|
this.startZoomigator(event.touches[0].clientX,event.touches[0].clientY);
|
||||||
this.hoverZoomer(event.touches[0].clientX,event.touches[0].clientY);
|
this.hoverZoomigator(event.touches[0].clientX,event.touches[0].clientY);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
case "touchmove":
|
case "touchmove":
|
||||||
this.hoverZoomer(event.touches[0].clientX,event.touches[0].clientY);
|
this.hoverZoomigator(event.touches[0].clientX,event.touches[0].clientY);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
case "touchend":
|
case "touchend":
|
||||||
this.stopZoomer();
|
this.stopZoomigator();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.executeMacro = function() {
|
exports.executeMacro = function() {
|
||||||
this.inZoomer = false;
|
this.inZoomigator = false;
|
||||||
var attributes = {
|
var attributes = {
|
||||||
style: {
|
style: {
|
||||||
"position": "absolute",
|
"position": "absolute",
|
@ -11,7 +11,7 @@ title: $:/templates/PageTemplate
|
|||||||
}}}
|
}}}
|
||||||
|
|
||||||
<!-- Zooming navigator -->
|
<!-- Zooming navigator -->
|
||||||
<<zoomer>>
|
<<zoomigator>>
|
||||||
|
|
||||||
<!-- The top navigation bar -->
|
<!-- The top navigation bar -->
|
||||||
<div class="navbar navbar-fixed-top">
|
<div class="navbar navbar-fixed-top">
|
||||||
|
93
tw5.com/tiddlers/mechanisms/PluginMechanism.tid
Normal file
93
tw5.com/tiddlers/mechanisms/PluginMechanism.tid
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
title: PluginMechanism
|
||||||
|
tags: docs mechanism
|
||||||
|
|
||||||
|
Plugins are bundles of tiddlers that are distributed and managed as a single unit.
|
||||||
|
|
||||||
|
Plugins usually have a title of the form `$:/plugins/publisher/name`. Plugins that are part of the core TiddlyWiki distribution have titles of the form `$:/core/plugins/name`.
|
||||||
|
|
||||||
|
Plugins that define macros, views or other named entities are expected to prefix the name with their publisher identifier, for example: "tiddlytools.slider".
|
||||||
|
|
||||||
|
Plugins can contain tiddlers with arbitrary titles but are expected to sandbox their tiddlers into the shadow tiddler namespace, for example "$:/plugins/tiddlytools/superslider/handle.js".
|
||||||
|
|
||||||
|
On both the server and in the browser, the constituent tiddlers of a plugin are created with the `tiddler.fromPlugin` member containing the title of the plugin. They can be filtered out with `[is[fromPlugin]]` and `[plugin[$:/plugins/name]]` (the latter selects all the tiddlers that are constituents of the plugin, regardless of whether they still hold the value specified in the plugin).
|
||||||
|
|
||||||
|
Note that plugins can contain any tiddler content, and so can be used to distribute documentation, templates, or clip art.
|
||||||
|
|
||||||
|
! Plugin format
|
||||||
|
|
||||||
|
Plugins are stored as tiddlers of type `application/x-tiddlywiki-plugin` containing a JSON structure encoding the plugin metadata and the list of tiddlers within it. After tiddlers have been loaded, plugins are exploded by iterating through the plugin tiddlers and creating the individual constituent tiddlers that they contain. Any tiddlers that already exist are preserved, and not overwritten.
|
||||||
|
|
||||||
|
The JSON structure for plugin tiddlers is as follows:
|
||||||
|
|
||||||
|
{{{
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"title": "MyPlugin",
|
||||||
|
"pluginType": "plugin"
|
||||||
|
"description": "An exemplary plugin for demonstration purposes",
|
||||||
|
"author": "JeremyRuston",
|
||||||
|
"version": "1.2.3-alpha3",
|
||||||
|
"coreVersion": ">=5.0.0",
|
||||||
|
"source": "http://tiddlywiki.com/MyPlugin",
|
||||||
|
"dependencies": [
|
||||||
|
"dependent-plugin-1",
|
||||||
|
"dependent-plugin-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tiddlers": [
|
||||||
|
{"title": "MyTitle", "type": "image/png", "text": "<base64>"},
|
||||||
|
{"title": "MyTitle2", "text": "Text"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}}}
|
||||||
|
|
||||||
|
The title specified in the metadata block of the plugin must match the actual title of the tiddler in which the plugin is stored.
|
||||||
|
|
||||||
|
! Plugin folders
|
||||||
|
|
||||||
|
On the server, plugins can be stored in folders containing a `plugin.tiddlywiki` file that contains the metadata for the plugin and specifies the files comprising it. It can also optionally override any metadata provided by the files themselves. Loading a plugin creates the JSON tiddler containing the plugin. It only creates the constituent tiddlers if they do not already exist.
|
||||||
|
|
||||||
|
The `plugin.tiddlywiki` file should contain the following JSON structure:
|
||||||
|
|
||||||
|
{{{
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
<as described above>
|
||||||
|
},
|
||||||
|
"externalTiddlers": [
|
||||||
|
{"file": "../MyFile.png", "fields": {"title": "MyTitle", "type": "image/png"}},
|
||||||
|
{"file": "../MyFile.txt", "fields": {"title": "MyTitle2"}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}}}
|
||||||
|
|
||||||
|
! Plugin types
|
||||||
|
|
||||||
|
Plugins can have an optional plugin type:
|
||||||
|
|
||||||
|
* ''plugin'' - an ordinary plugin loaded at boot time
|
||||||
|
* ''pagetheme'' - a dynamically loaded page theme
|
||||||
|
* ''tiddlertheme'' - a dynamically loaded tiddler theme
|
||||||
|
* ''translation'' - a dynamically loaded translation
|
||||||
|
|
||||||
|
In the browser, any constituent tiddlers that are static styles (ie shadow tiddlers of content type `text/css`) or javascript library modules (ie shadow tiddlers of content type `application/javascript` and possessing the field `library`) are added to the DOM during processing.
|
||||||
|
|
||||||
|
! Loading plugins
|
||||||
|
|
||||||
|
A folder containing an exploded TiddlyWiki can contain a `wiki.tiddlywiki` file that identifies the plugins to be included in this wiki, and specifies the load order:
|
||||||
|
|
||||||
|
{{{
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"slider",
|
||||||
|
"chooser",
|
||||||
|
"tiddlytools.superslider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Plugins can also be included manually by copying them into the `plugins` subfolder of the wiki.
|
||||||
|
|
||||||
|
! Plugin implementation
|
||||||
|
|
||||||
|
The wiki object keeps track of all of the currently loaded plugins. If a request for a tiddler isn't in the store then the wiki looks through the cascade of plugins to find the requested tiddler. It is a similar idea to the way that shadow tiddlers are implemented in classic TiddlyWiki.
|
Loading…
Reference in New Issue
Block a user