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.
print-window-tiddler
Jeremy Ruston 2012-08-06 22:34:16 +01:00
rodzic 62762a370c
commit 8e6647b615
6 zmienionych plików z 179 dodań i 17 usunięć

Wyświetl plik

@ -241,8 +241,27 @@ $tw.modules.registerTypedModule = function(name,moduleType,moduleExports) {
Register all the module tiddlers that have a module type
*/
$tw.modules.registerTypedModules = function() {
for(var title in $tw.wiki.tiddlers) {
var tiddler = $tw.wiki.getTiddler(title);
var title, tiddler;
// 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) {
$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) {
var t = this.tiddlers[title];
if(t instanceof $tw.Tiddler) {
return t;
} else if(title in this.pluginTiddlers) {
return this.pluginTiddlers[title];
} else {
return null;
}
@ -681,6 +727,9 @@ $tw.loadTiddlersFromFolder(path.resolve($tw.boot.wikiPath,$tw.config.wikiTiddler
/////////////////////////// Final initialisation
// Install plugins
$tw.wiki.installPlugins();
// Register typed modules from the tiddlers we've just loaded
$tw.modules.registerTypedModules();

Wyświetl plik

@ -0,0 +1,9 @@
title: TemporaryTestPlugin
type: application/x-tiddlywiki-plugin
{
"tiddlers": [
{"title": "Test1", "text": "Some text"},
{"title": "Test2", "type": "Some more text"}
]
}

Wyświetl plik

@ -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"
}
}

Wyświetl plik

@ -1,5 +1,5 @@
/*\
title: $:/core/modules/macros/zoomer.js
title: $:/core/plugins/zoomigator/zoomigator.js
type: application/javascript
module-type: macro
@ -13,22 +13,22 @@ Zooming navigator macro
"use strict";
exports.info = {
name: "zoomer",
name: "zoomigator",
params: {
}
};
exports.startZoomer = function(x,y) {
this.inZoomer = true;
exports.startZoomigator = function(x,y) {
this.inZoomigator = true;
this.startX = x;
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
*/
exports.hoverZoomer = function(x,y) {
exports.hoverZoomigator = function(x,y) {
// Put the transform origin at the top in the middle
document.body.style[$tw.browser.transformorigin] = "50% 0";
// Some shortcuts
@ -68,37 +68,37 @@ exports.hoverZoomer = function(x,y) {
document.body.style[$tw.browser.transform] = transform;
};
exports.stopZoomer = function() {
exports.stopZoomigator = function() {
var newScrollY = this.yFactor * (this.bodyHeight - this.windowHeight);
this.inZoomer = false;
this.inZoomigator = false;
window.scrollTo(0,newScrollY);
document.body.style[$tw.browser.transform] = "translateY(" + newScrollY * this.xFactor + "px) " +
"scale(" + this.scale + ") " +
"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)";
};
exports.handleEvent = function(event) {
switch(event.type) {
case "touchstart":
this.startZoomer(event.touches[0].clientX,event.touches[0].clientY);
this.hoverZoomer(event.touches[0].clientX,event.touches[0].clientY);
this.startZoomigator(event.touches[0].clientX,event.touches[0].clientY);
this.hoverZoomigator(event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
return false;
case "touchmove":
this.hoverZoomer(event.touches[0].clientX,event.touches[0].clientY);
this.hoverZoomigator(event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
return false;
case "touchend":
this.stopZoomer();
this.stopZoomigator();
event.preventDefault();
return false;
}
};
exports.executeMacro = function() {
this.inZoomer = false;
this.inZoomigator = false;
var attributes = {
style: {
"position": "absolute",

Wyświetl plik

@ -11,7 +11,7 @@ title: $:/templates/PageTemplate
}}}
<!-- Zooming navigator -->
<<zoomer>>
<<zoomigator>>
<!-- The top navigation bar -->
<div class="navbar navbar-fixed-top">

Wyświetl plik

@ -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.