Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
This is an extract from the tiddlywiki reference wiki at http://tiddlywiki.org, to support development when offline. I used Martin Budden's mediawiki adaptor (http://tiddlywiki.org/wiki/Dev:Server_Adaptor_Mechanism) to place all "Dev" category tiddlers in a tiddlywiki.
- Michael Mahemoff (http://softwareas.com)
{{Incomplete}}
{{Quote|1=[http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/d14ec01af14e4f8f Schilke]|2=
There have been many discussions around this problem but there doesn't
seem to be any satisfying solution - or at least conclusion - so [we should start] to collect the related threads, solutions, requests and suggestions [...]
Here's the thread which had been the trigger for this approach:
[http://groups.google.com/group/TiddlyWiki/browse_frm/thread/9ae489df5c69f19f <nowiki>[tw]</nowiki> Google sitemap again]
A search in all three groups gives a quick overview about the issue:
* [http://groups.google.com/group/TiddlyWiki/search?q=seo&start=0&scoring=d "seo" in <nowiki>[tw]</nowiki>]
* [http://groups.google.com/group/GTD-TiddlyWiki/search?q=seo&start=0&scoring=d "seo" in <nowiki>[twdev]</nowiki>]
* [http://groups.google.com/group/TiddlyWikiDev/search?q=seo&start=0&scoring=d "seo" in <nowiki>[gtdtw]</nowiki>]
(You might also search for "(web) publishing", "sitemap" and related
terms)
}}
== Saving Static HTML ==
* [http://jaybyjayfresh.com/2008/01/24/tiddlytemplating-part-2-a-prototype-that-works/ TiddlyTemplating]
== See Also ==
* [[Accessibility]]
<code>
Adaptor.close()
</code><br/>
Should only be called after the last callback has completed
Closes an adaptor, severing the connection to the workspace and host (see ServerAdaptorConcepts).
* [[Dev:Adaptor.close|Adaptor.close]] shouldn't be called while there are outstanding callbacks
* [[Dev:Adaptor.close|Adaptor.close]] must be called to ensure that all pending write operations are completed
'''Parameters:'''
None.
'''Returns:'''
{| border="1"
|true||on success
|-
|string||an error message if there was an error issuing the request
|}
[[Category:Adaptors]]
<code>
Adaptor.getTiddler(title,context,userParams,callback)
</code><br/>
Retrieves a named tiddler from an adaptor opened to a workspace on a host (see [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]]). This function uses the [[Dev:AsynchronousPattern|AsynchronousPattern]].
'''Parameters:'''
{| class="wikitable"
!title
|Title of the tiddler to retrieve
|-
!context
|context object that is passed to the callback function
|-
!userParams
|user settable object object that is passed on unchanged to the callback function
|-
!callback
|Reference to callback function, see [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]]
|}
'''Returns:'''
{| class="wikitable"
!true
|on success
|-
!string
|an error message if there was an error issuing the request
|}
'''Callback signature''' as per [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]] with additional fields:
{| class="wikitable"
!context.tiddler
|The retrieved tiddler object, with all the standard fields and the [[Dev:ServerAdaptorExtendedFields|ServerAdaptorExtendedFields]] filled in
|}
[[Category:Adaptors]]
<code>
Adaptor.getTiddlerList(context,userParams,callback,filter)
</code><br/>
Gets a list of the tiddlers available on an adaptor opened to a workspace on a host (see ServerAdaptorConcepts). This function uses the AsynchronousPattern.
'''Parameters:'''
{| border="1"
|context||context object that is passed to the callback function
|-
|userParams||user settable object object that is passed on unchanged to the callback function
|-
|callback||reference to callback function, see [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]]
|-
|filter||optional string to select tiddlers returned in list, see TiddlerSelection
|}
'''Returns:'''
{| border="1"
|true||on success
|-
|string||an error message if there was an error issuing the request
|}
'''Callback signature''' as per [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]] with additional fields:
{| border="1"
|context.tiddlers||Array of tiddlers, with a minimum set of fields filled in, see below
|}
The tiddlers in the <code>context.tiddlers</code> array have the following minimum set of fields, although particular adaptors may choose to return additional information in further fields.
{| border="1"
|title||Title of the tiddler (as used in [[Dev:Adaptor.getTiddler|Adaptor.getTiddler]])
|}
[[Category:Adaptors]]
<code>
Adaptor.getWorkspaceList(context,userParams,callback)
</code><br/>
Gets a list of the workspaces available on a particular host (see [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]]). This function uses the [[Dev:AsynchronousPattern|AsynchronousPattern]].
'''Parameters:'''
{| border="1"
|context||context object that is passed to the callback function
|-
|userParams||user settable object object that is passed on unchanged to the callback function
|-
|callback||Reference to callback function, see [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]]
|}
'''Returns:'''
{| border="1"
|true||on success
|-
|string||an error message if there was an error issuing the request
|}
'''Callback signature''' as per [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]] with additional fields:
{| border="1"
|context.workspaces||An array of information about each available workspace (see below)
|}
The entries in the <code>context.workspaces</code> array have the following minimum set of fields, although particular adaptors may choose to return additional information in further fields.
{| border="1"
|title||Title of the workspace (as used in [[Dev:Adaptor.openWorkspace|Adaptor.openWorkspace]])
|}
[[Category:Adaptors]]
<code>
Adaptor.openHost(host,context,userParams,callback)
</code><br/>
Creates an adaptor for talking to a particular host (see [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]]). This function uses the [[Dev:AsynchronousPattern|AsynchronousPattern]].
'''Parameters:'''
{| border="1"
|host||URI of server
|-
|context||context object that is passed to the callback function
|-
|userParams||user settable object object that is passed on unchanged to the callback function
|-
|callback||Reference to callback function, see [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]]
|}
'''Returns:'''
{| border="1"
|true||on success
|-
|string||an error message if there was an error issuing the request
|}
'''Callback signature''' as per [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]].
[[Category:Adaptors]]
<code>
Adaptor.openWorkspace(host,context,userParams,callback)
</code><br/>
Opens a particular workspace in an adaptor that has already been opened on a host (see [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]]). This function uses the [[Dev:AsynchronousPattern|AsynchronousPattern]].
'''Parameters:'''
{| border="1"
|workspace||Name of workspace
|-
|context||context object that is passed to the callback function
|-
|userParams|user settable object object that is passed on unchanged to the callback function
|-
|callback||Reference to callback function, see [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]]
|}
'''Returns:'''
{| border="1"
|true||on success
|-
|string||an error message if there was an error issuing the request
|}
'''Callback signature''' as per [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]].
[[Category:Adaptors]]
<code>
Adaptor.putTiddler(tiddler,context,userParams,callback)
</code><br/>
Stores a named tiddler to an adaptor opened to a workspace on a host (see [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]]). This function uses the [[Dev:AsynchronousPattern|AsynchronousPattern]].
'''Parameters:'''
{| border="1"
|tiddler||The Tiddler to store
|-
|context||context object that is passed to the callback function
|-
|userParams||user settable object object that is passed on unchanged to the callback function
|-
|callback||Reference to callback function, see [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]]
|}
'''Returns:'''
{| border="1"
|true||on success
|-
|string||an error message if there was an error issuing the request
|}
'''Callback signature''' as per [[Dev:AdaptorCallbackSignature|AdaptorCallbackSignature]].
[[Category:Adaptors]]
<code>
callback(context,userParams)
</code><br/>
{| border="1"
|userParams||userParams object (unchanged) as passed by the original caller
|}
context contains the following fields:
{| border="1"
|context.status||True if original call was successful, false if there was an error
|-
|context.statusText||Error message if context.status is false
|-
|context.adaptor||Reference to the adaptor object
|}
Additionally, context may contain the following fields, depending on the adaptor function originally called:
{| border="1"
|context.workspaces||Array of workspaces, see [[Dev:Adaptor.getWorkspaceList|Adaptor.getWorkspaceList]]
|-
|context.tiddler||Tiddler retrieved, see [[Dev:Adaptor.getTiddler|Adaptor.getTiddler]]
|-
|context.tiddlers||Array of Tiddlers retrieved, not all Tiddler fields filled in, see [[Dev:Adaptor.getTiddlerList|Adaptor.getTiddlerList]]
|-
|context.revisions||Array of revisions of the current Tiddler, not all Tiddler fields filled in, see [[Dev:Adaptor.getTiddlerRevisionList|Adaptor.getTiddlerRevisionList]]
|}
[[Category:Adaptors]]
The <tt>addClass</tt> global function adds a CSS class to a DOM element. It take two parameters: the DOM element to modify, and the name of the class to add. It does not return anything.
The <tt>addEvent</tt> global function adds an event listener to a DOM element. It takes three parameters:
* the DOM element to modify.
* the event to listen to.
* the function to attach to be triggered when the event occurs.
This does not return anything.
The <tt>addNotification</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object ties an external function (called a callback here) to a tiddler. Whenever that tiddler is changed by the author, the callback function is invoked. TheTiddlyWiki code only passes the title of the changed tiddler to the callback function; it's up to the callback to dig up what it needs to act appropriately based on the change. This method takes two parameters:
* the title of the tiddler
* a reference to the callback function
It is possible to call this method with a null value for the first parameter. You must specify null, not an empty string. The callback function then is invoked for every tiddler, but only if either:
* notifications are running via the [[TiddlyWiki.notifyAll|notifyAll]] method
* the <tt>notify</tt> method is called with the second parameter set to true
<pre>
$ patch -p0 < [filepath]
</pre>
If a patch was created using an alternative [http://en.wikipedia.org/wiki/Revision_control VCS] (e.g. Git), it might be necessary to use <code>-p1</code> instead.
{{WIP}}
* standards compliance
** e.g. valid element attributes
* tiddler ambiguity (store vs. story)
* object namespaces
** inconsistencies (e.g. ''TiddlyWiki'' vs. ''Tiddler'')
** omissions (e.g. ''plugins''/''extensions'')
* multiple stories
** no hardcoding of a specific instance of Store and Story into toolbar commands or similar functions
* hooks for plugins at various stages of the start-up and save process
* persistent IDs for tiddlers (might also prevent [http://trac.tiddlywiki.org/ticket/235 invalid titles])
* asynchronous behavior
** loading on demand (needs to be built into the store/retrieval mechanism)
** "detached store" (e.g. server-side storage)
* lack of namespaces (can lead to pollution by system/config tiddlers)
== See Also ==
* [[Dev:UI Issues]]
== External Resources ==
* [http://mindbox.projects.unamesa.org MindBox]
Asynchronous methods are used by the low level http functions, and by higher level mechanisms such as the [[Dev:ServerAdaptorMechanism|ServerAdaptorMechanism]].
The standard TiddlyWiki approach is illustrated with this example of a <code>procrastinate()</code> function that takes a string parameter which is then returned after 1 second by a callback function:
<code>
// Call an asynchronous function
function test()
{
// The context object is for storing information that you're going to need inside the callback function
var myContext = {
param1: "a string",
param2: {an: "object"},
param3: ["an","array"]
};
// Call the function including a reference to the callback function and it's context
procrastinate("hello tomorrow's world",myCallback,myContext);
}
// The callback function that gets invoked after 1 second
function myCallback(status,context,procrastinatedString)
{
displayMessage("Message from the past: " + procrastinatedString);
displayMessage("Message context: " + context.param1);
}
// The procrastinate function invokes a callback after 1 second with a specified string parameter
// procrastinatedString - the string to pass back to the callback function
// callback - the callback function
// context - a context object that is passed to the callback function
// Returns true if the request was issued or a string error message if it failed
function procrastinate(procrastinatedString,callback,context)
{
window.setInterval(callback,1000,true,context,procrastinatedString);
}
</code>
Asynchronous programming is necessary in several situations but is full of pitfalls for the unwary:
* On many browsers, <code>alert()</code> doesn't work reliably in some asynchronous callbacks
* It's very easy to miss errors by ignoring callback returns
[[Category:Adaptors]]
This method of a [[Tiddler]] object returns whether someone rendering this tiddler should create links for WikiWords. Currently, tiddlers marked <tt>systemConfig</tt> or <tt>excludeMissing</tt> are the only exceptions. There are no parameters to this method.
== Customization ==
=== Backstage Tabs ===
The following sample code adds the contents of [[SideBarOptions]] as a new backstage tab (called "myTab"):
<pre>
//{{{
config.tasks.myTab = {
text: "my custom tab",
tooltip: "A custom backstage tab",
content: "<<tiddler SideBarOptions>>"
};
config.backstageTasks.push("myTab");
//}}}
</pre>
=== Command Buttons ===
In order to add a command button directly to the backstage bar, the <code>action</code> property should be used to reference a JavaScript function (no arguments are passed).
For example, the standard backstage button for ''Save'' is defined as follows:
<pre>
save: {
text: "save",
tooltip: "Save your changes to this TiddlyWiki",
action: saveChanges
}
</pre>
If the desired function does not have a convenient entry point, or if certain functions need arguments passed to them, an anonymous function declaration can be used as a "wrapper":
<pre>
//{{{
config.tasks.myTab = {
text: "username",
tooltip: "set your TiddlyWiki user name",
action: function() {
var newName = prompt("Please enter a new username:", config.options.txtUserName);
if(!newName || !newName.length)
return false; // canceled by user
config.options.txtUserName = newName;
saveOptionCookie("txtUserName");
}
};
config.backstageTasks.push("myTab");
//}}}
</pre>
=== Other Elements ===
Adding content other than tabs or buttons to the backstage bar requires [[Dev:Hijacking|hijacking]] the <code>backstage.init()</code> function in order to render those elements directly into the <code>backstageToolbar</code> element.
{{Note|
While hijacking the core function is necessary for this purpose, it could corrupt the backstage if there is an error in the respective code.
Since the backstage serves as a fallback mechnanism in case there are other problems, this should be used with caution.
}}
For example, the following code adds the search box to the backstage toolbar (inside a container with the [[Wiki Markup#Custom Styling|class]] <code>searchBox</code>):
<pre>
//{{{
backstage.init_old = backstage.init;
backstage.init = function() {
var s = "{{searchBox{<<search>>}}}";
this.init_old.apply(this, arguments);
wikify(s, document.getElementById("backstageToolbar"));
};
//}}}
</pre>
== See Also ==
* [[Backstage]]
{{Incomplete}}
== Metadata ==
cf. [[Dev:Plugin Specifications|Plugin Specifications]]
== Code Structure ==
{{Note|The samples provided here are merely suggestions, not strictly-enforced regulations.}}
cf. [[Dev:Plugin Specifications#Template|Plugin Specifications]]
=== Namespacing ===
Plugins' functions should be registered in the <code>config.extensions</code> namespace (built in from v2.5):
<source lang="javascript">
config.extensions.SamplePlugin = {
sampleFunction: function() {
/* ... */
}
};
</source>
Similarly, functions specific to a certain [[Dev:Macros|macro]] or [[Dev:Toolbar Commands|toolbar command]] should be attached to the <code>config.macros</code> or <code>config.commands</code> object, respectively:
<source lang="javascript">
config.macros.SampleMacro = {
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
/* ... */
}
};
config.commands.sampleCommand = {
handler: function(event, src, title) {
/* ... */
}
};
</source>
Overview of common TiddlyWiki namespaces:
* <code>config.extensions</code>: [[Dev:Plugins|Plugins]]
* <code>config.macros</code>: [[Dev:Macros|Macros]]
* <code>config.commands</code>: [[Dev:Toolbar Commands|Toolbar Commands]]
* <code>config.paramifiers</code>: [[Dev:Paramifiers|Paramifiers]]
* <code>config.formatters</code>: [[Dev:Formatters|Formatters]]
* <code>config.adaptors</code>: [[Dev:Adaptors|Adaptors]]
=== Documentation ===
Well-structured code simplifies readability and maintainability.
Commenting is an important part of structuring code.
There are two basic levels of comments:
* block comments (<code>/* [...] */</code>)
* inline comments (<code>// [...]</code>)
This distinction can be used to create a semantic and visual distinction between headers and annotations:
<source lang="javascript">
/*
** [section]
*/
// [function description]
function foo() {
// [procedure description]
var foo = bar; // [explanatory comment]
}
/* [sub-section] */
function bar() {
var foo = bar;
}
</source>
{{Note|
While CSS only provides syntax for block comments, the same principle can be applied to [[StyleSheets]].
}}
== Extending Core Functionality ==
Instead of ''overwriting'' core functions, [[Dev:Hijacking|hijacking]] should be used whenever possible to add the desired functionality either before or after invoking the hijacked code (via the <code>apply(this,arguments);</code> method).
This allows other plugins relying on these functions to continue to operate.
However, hijacking the function is not always possible, given the nature of the specific core function or the desired changes.
In that case, it is important to clearly note in the respective plugin's documentation that installing the plugin may have adverse affects on other plugins installed in the same document (also see [[Dev:Plugin Library#Optional Fields|fields]]).
== Declaring Global Functions ==
Function statements should be avoided within plugins.
Function statements are of the form:
<source lang="javascript">
function foo() {
// ...
}
</source>
Because plugins' code is processed using <code>eval()</code>, certain browsers (e.g. IE7 and Safari 3) leave the function in <code>eval()</code>'s local scope.
The way around this is to use expressions to declare such functions as variables, ideally adding an explicit <code>window</code> prefix:
<source lang="javascript">
window.foo = function() {
// ...
}
</source>
This is especially relevant when overriding core functions that are declared using function statements, such as <code>displayMessage()</code>, event handlers, etc.
== Dependency Handling ==
While TiddlyWiki recognizes a ''Requires'' slice in plugins, this is only used to determine plugins' loading order and will not result in an error if the respective dependencies are not found in the document.
Strict dependency checking has to be implemented by the respective plugin itself.
However, rather than checking for the respective plugin's name, [http://trac.tiddlywiki.org/ticket/632#comment:6 ticket object detection should be employed] for maximum flexibility:
<source lang="javascript">
if(!window.foo) { //# "window" only required to prevent ReferenceError if "foo" is a global variable
throw "Missing dependency: Foo";
}
</source>
(<code>foo</code> might be a property of TiddlyWiki's ''config'' namespace, e.g. <code>config.extensions.SamplePlugin)</code>
{{Note|
The <code>pluginInfo.log</code> array can be extended to add a message without stopping execution of the plugin:
<pre>
pluginInfo.log.push("lorem ipsum");
</pre>
}}
== Creating Aliases ==
Sometimes variable names can become very long - especially when using [[#Namespacing|namespaces]].
A closure (anonymous wrapper function) can be used to create a local scope in which local variables can be used as [http://docs.jquery.com/Plugins/Authoring#Custom_Alias aliases], essentially providing "shortcuts" for accessing variables from the parent scope:
<source lang="javascript">
(function($) { //# set up local scope and jQuery alias
var plugin; //# alias
plugin = config.extensions.SamplePlugin = {
sampleMessage: "lorem ipsum",
sampleFunction: function() {
/* ... */
}
};
displayMessage(plugin.sampleMessage);
plugin.sampleFunction();
})(jQuery); //# end of local scope, passing in jQuery object
</source>
Here <code>$</code> is used as a local alias for <code>jQuery</code>.
Similarly, <code>plugin</code> works as an alias for <code>config.extensions.SamplePlugin</code>.
== See Also ==
* [[Dev:Plugin Specifications]]
The global function <tt>blurElement</tt> removes keyboard focus from a DOM element. It takes one parameter, the DOM element to lose focus, and does not return any values.
The <tt>blurTiddler</tt> method of the [[Story]] class takes the focus away from a tiddler — that is, any text fields it may currently show lose keyboard focus. It expects one parameter, the title of the tiddler to lose focus, and returns nothing.
== This page is the developer documentation for ccTiddly, please feel free to extend this documentation. ==
Information on using ccTiddly if you are not a developer can be found here :
* [[ccTiddly|ccTiddly User Guide]]
Issues will soon be tracked using the '''TiddlyWiki Trac System''' which can be found at :
* [http://trac.tiddlywiki.org ccTiddly Trac ]
The most up to date (and possibly broken) code can be found in SVN at :
* [http://svn.tiddlywiki.org/Trunk/association/serversides/cctiddly/ cctiddly SVN]
You can keep an eye on the '''developer blogs''' here :
* [http://coolcold.wordpress.com coolcold.wordpress.com]
* [http://simonmcmanus.com simonmcmanus.com]
** (includes data sourced from: [http://simonmcmanus.wordpress.com/])
For more information interacting with TiddlyWiki from code please see the [[Dev:CcTiddly/API|ccTiddly API documentation]].
==System Tiddler Plugins ==
This area talks about the future plans for ccTiddly 2.x
ccTiddly currently uses a modified version of the basic TiddlyWiki HTML code. This means that whenever there’s an update to the core code, we have to manually update ccTiddly to the new version (which involves some fiddly manual steps).
The goal is to make it so that ccTiddly uses a clean, unadorned copy of empty.html from tiddlywiki.com, and to have it automatically splice in the extra bits and pieces that are needed by ccTiddly.
These extra bits and pieces are a bunch of plugins and some “stock” tiddlers that use macros from those plugins to expose them to the user.
If, like me, you get confused about the difference between a plugin and a macro, remember that a plugin is just a tiddler tagged “systemConfig”, a plugin can can contain many different macros (which use config.macros)
For instance, there is a plugin called “CreateWorkspacePlugin” that provides the <<ccCreateWorkspace>> macro. The macro produces a user interface that allows users to interactively create a new workspace. So, we need to include the plugin in the HTML file, and we also need to, say, add a new tiddler called “CreateWorkspace” that contains a call to <<ccCreateWorkspace>> the create workspace macro.
Here’s the complete list:
'''Macro <<ccCreateWorkspace>> (from CreateWorkspacePlugin)'''
This macro allows the user to create a new ccTiddly workspace by specifying a name, and the permissions for both anonymous and registered users. We’ll make the macro available to users via the backstage area, since this isn’t a particularly common operation.
'''Macro <<ccEditWorkspace>> (from EditWorkspacePlugin)'''
This plug in will allow users to manage permissions on a workspace which already exists (assuming they have the correct permission). Going forward this will probably include the master workspace management which will then be moved to its own plug in.
'''Macro <<ccUserTiddler>> (From UserTiddlerPlugin)'''
This tiddler will be used to change your email address, password and user specific tiddlers.
'''Macro <<ccLogin>> - (From LoginPlugin)'''
This macro displays the login UI. When not logged in it will allow the users to login using OpenID, LDAP or the internal user database. When logged in the user will be informed who they are logged in as and be given the option to log out.
==Create Workspace==
POST the following variables to the page you wish to create. Please note that the workspace name can only contain letter and numbers.
* '''ccAnonPerm'''
- ''a four character string in the form AADD. Each letter represents anonymous users ability to Read Create Update and Delete Tiddlers - for more details please read up on ccTiddly permissions. ''
A = Allow
D = Deny
ADDD would mean that anonymous users can Read but not Create Update or Delete tiddlers.
HTTP status code 201 will be returned if the resource has been successfully created.
==Create Tiddlers==
The following fields need to be posted to handle/save.php.
* workspace
* tiddler
If you do NOT URIEncode the tiddler field before POSTing you should also post
* decode = 1
In order to have the file decoded on the server.
The is an example form which should let you create a tiddler.
<pre>
<h1> New Tiddler </h1>
<form method='post' action='handle/save.php'>
Workspace Name : <input name='workspace' value='simoa1'><br />
<textarea cols=110 name='tiddler'>
%3Cdiv%20tiddler%3D%22menosayma%22%20modifier%3D%22username%22%20created%3D%222008071109544%22%20modified%3D%22200807110954%22%20tags%3D%22%22%20changecount%3D%221%22%3ETysdfsdfsdfsdfsdf%3C%2Fdiv%3E
</textarea><br />
decodeTidler : <input name='decode' value='1'><br />
<input type='submit'>
</form>
</pre>
==Updating Tiddlers==
Conditions :
Change count should be higher than number than the existing change count.
ochangecount should be the existing revision number
Modified Date should be more recent that the previous modified date in the database.
<pre>
<h1> Update Tiddler </h1>
<form method='post' action='handle/save.php'>
Workspace Name : <input name='workspace' value='simoa1'><br />
<textarea cols=110 name='tiddler'>%3Cdiv%20tiddler%3D%22panda1%22%20modifier%3D%22username%22%20created%3D%22200807110954%22%20modified%3D%22200807110954%22%20tags%3D%22%22%20changecount%3D%229%22%3ETbPOLLY9%3C%2Fdiv%3E</textarea><br />
ochangecount: <input name='ochangecount' value='9'><br />
decodeTidler : <input name='decode' value='1'><br />
<input type='submit'>
</form>
</pre>
==Deleting Tiddlers==
==File Uploads==
==Get List of Tiddlers==
==Get List of Workspaces==
==RSS==
'''This has not been implemented yet!'''
'''URL'''
The url of the subscribed RSS would be like ''example.com/workspace_name/?action=rss&username=user_A&code=hashed_string&tag=tag_list''
note:
#the workspace name is described by '''workspace_name''' or '''?workspace=workspace_name''' without mod_rewrite
#action=rss was used such that ccT could be easily break into modules in the future
#username is for defining username such that RSS can be generated for logged in users only
#code is similar to a password. It is the mixed hash of the hashed password from the database and the hashseed. It is different to the session code from the login cookie
#tag list (planned for future version) can do RSS for certain or without certain tags
*load header.php to include all required plugins and functions
*create user
The <tt>changed</tt> method of a [[Tiddler]] object recalculates various things about it after its properties are changed. This is mainly for internal use by the TiddlyWiki code, and has no visible effect.
{{Incomplete}}
[http://chef.tiddlywiki.org Chef] provides a simple way to [[Dev:Cook|cook]] TiddlyWiki [[Dev:Recipe|recipes]].
== See Also ==
* [[Dev:Cook|Cook]]
* [[Dev:Ginsu|Ginsu]]
* [[Dev:Recipe|Recipes]]
[[Category:Developer Tools]]
The <tt>chooseTemplateForTiddler</tt> method of the [[Story]] class returns the name of a template for a tiddler view. It takes two parameters:
* the title of the tiddler
* a constant corresponding to the type of template to use
The second parameter may be omitted, in which case <tt>DEFAULT_VIEW_TEMPLATE</tt> is assumed.
The <tt>clear</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object removes all tiddlers from it and clears its dirty flag. It takes no parameters.
The global function <tt>clearMessage</tt> hides the TiddlyWiki's message area from view. This function expects no parameters.
The <tt>closeAllTiddlers</tt> method of the [[Story]] class closes all tiddlers currently visible on the page and scrolls the browser window to the top of the page. It accepts one optional parameter, a title of a tiddler to keep open. If a tiddler with that title is currently visible, it is not closed, and any tiddlers with unsaved changes remain open as well.
== Lightweight Editors ==
Simple code editors usually provide basic functionality, such as syntax highlighting.
Many editors are extensible via plugins, adding more advanced functionality.
* Windows
** [http://notepad-plus.sourceforge.net Notepad++]
** [http://www.jedit.org jEdit]
** [http://www.contexteditor.org ConTEXT]
** [http://cream.sourceforge.net Cream]
** [http://www.e-texteditor.com E-TextEditor]
* Linux
** GEdit (GNOME)
** Kate (KDE)
* Mac OS X
** TextMate
** Coda
== Advanced Editors ==
These editors provide powerful features, but usually have a steep learning curve.
* [http://www.gnu.org/software/emacs/ Emacs]
* [http://www.vim.org Vim]
== Integrated Development Environments ==
IDEs usually provide more elaborate features, such as code completion, debugging or validation.
* [http://www.aptana.com Aptana] (based on [http://www.eclipse.org Eclipse])
* [http://www.netbeans.org NetBeans]
== External Resources ==
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/8f7fc35b66bf0867 <nowiki>[twdev]</nowiki> TiddlyWiki Development IDE]
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/425628028d7c59a <nowiki>[twdev]</nowiki> JavaScript/TiddlyWiki IDE]
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/1eaa7a845357c799 <nowiki>[twdev]</nowiki> Code Editor Recommendations?]
The TiddlyWiki developers are using Subversion for revision control.
The SVN repository is located at http://svn.tiddlywiki.org.
There is also a [http://trac.tiddlywiki.org/browser Web-based viewer] for browsing the repository.
[[Category:Incomplete]]
== Prevent Opening Non-Existent Tiddlers on Startup ==
<pre>
// override default handling
config.paramifiers.open.onstart = function(v) {
if(store.tiddlerExists(v))
story.displayTiddler("bottom",v,null,false,null);
};
</pre>
{{Approved}}
There are various context-dependent options for embedding wikified contents in tiddlers that are being processed by TiddlyWiki.
__TOC__
== Monospaced Blocks ==
In order to display processed code as monospaced blocks in tiddlers, the wiki markup needs to be escaped using the respective language's comments syntax.
While this is not necessary for a plugin to function properly, it makes the wikified tiddler contents generally look more appealing.
{| class="wikitable"
|-
! Context
! Syntax
|-
| HTML code
(e.g. in [[PageTemplate]], [[ViewTemplate]] or [[EditTemplate]])
| <pre>
<!--{{{-->
[HTML code]
<!--}}}-->
</pre>
|-
| CSS code
(e.g. in [[StyleSheet]])
| <pre>
/*{{{*/
[CSS code]
/*}}}*/
</pre>
|-
| JavaScript code
(in [[systemConfig]] tiddlers)
| <pre>
//{{{
[JavaScript code]
//}}}
</pre>
|}
== Wikified Contents ==
Wiki markup can be used within processed tiddlers by using a special variation of the respective language's comments syntax:
{| class="wikitable"
|-
! Context
! Syntax
|-
| HTML code
(e.g. in [[PageTemplate]], [[ViewTemplate]] or [[EditTemplate]])
| <pre>
<!---
wikified contents
--->
</pre>
|-
| CSS code
(e.g. in [[StyleSheet]])
| <pre>
/***
wikified contents
***/
</pre>
|-
| JavaScript code
(in [[systemConfig]] tiddlers)
| <pre>
/***
wikified contents
***/
</pre>
|}
[[Category:Wiki Markup]]
== See Also ==
* [[Escaping]]
{{Incomplete}}
{{Merge|[[Dev:Core Code Overview]] and [[Dev:Introduction]]}}
This page is meant as an overview of the basic TiddlyWiki architecture.
* [[Dev:TiddlyWiki|store]] (TiddlyWiki class)
* [[Dev:Story|story]]
* [[Dev:notify|notifications]]
* [[Dev:Wikifier|wikifier]]
* [[Dev:Formatters|formatters]]
* [[Dev:Macros|macros]]
* [[Dev:Toolbar Commands |toolbar commands]]
* [[Dev:FileSystem|saving]]
* [[Dev:Server Adaptor Mechanism|adaptors]]
== Index ==
* [[/1|#1]] (2008-02-11)
* [[/2|#2]] (2008-03-18)
* [[/3|#3]] (2008-11-10)
* [[/4|#4]] (2009-04-03)
: '''2008-02-11, 1800 UTC'''
== Participants ==
* Jeremy
* Saq
* Eric
* Jon
* Paul
* Fred
== Agenda ==
* search behaviour and UI ([http://trac.tiddlywiki.org/ticket/17 ticket 17]) (''Fred'')
* clarification of items on the [http://trac.tiddlywiki.org/milestone/2.3.1 roadmap for v2.3.1]; details with regards to the nature of the changes (''Saq'')
** new presentation of import tiddlers macro, optimized for upgrading TiddlyWikis
** sync (incomplete; does not do what the user expects)
* collaboration and community contributions
** encouraging community contributions to the core via Trac (tickets and patches) (''Fred'')
** providing specifications where possible, for roadmap items and in tickets, to make it easier for community members to contribute (''Saq'')
** collaboration process (e.g. [twdev], where participation has been low lately) (''Fred'')
== Minutes ==
recorded by Fred in [irc://irc.freenode.net/tiddlywiki-minutes #tiddlywiki-minutes on Freenode] (log slightly revised)
<pre>
Feb 11 19:00:56 ----- conference call started -----
Feb 11 19:01:32 participants: Jeremy, Saq, Eric, Jon, Paul, Fred
Feb 11 19:02:47 Saq: purpose of this meeting: enhancing communication and collaboration among the community members
Feb 11 19:04:39 **topic:** search UI
Feb 11 19:04:50 prevent destroying story
Feb 11 19:05:05 Eric: SearchOptionsPlugin (TiddlyTools)
Feb 11 19:05:50 Jeremy: plugin still separate
Feb 11 19:06:13 Eric: don't break plugins (e.g. YourSearchPlugin)
Feb 11 19:08:32 Eric & Saq: option to disable incremental search
Feb 11 19:09:59 Saq: Eric, please add comment to ticket 17
Feb 11 19:11:21 Eric: problem with popups: dimensions (screen estate), popups dismissed through interaction
Feb 11 19:13:03 Saq: discuss details on the groups and on Trac
Feb 11 19:14:35 **topic:** clarification of items on the roadmap for 2.3.1
Feb 11 19:18:55 cf. http://trac.tiddlywiki.org/milestone/2.3.1
Feb 11 19:14:46 **topic:** New presentation of import tiddlers macro, optimized for upgrading TiddlyWikis
Feb 11 19:16:03 Jeremy: problem of upgrading (e.g. backwards compatibility)
Feb 11 19:16:22 Jeremy: easy, guided upgrade process required
Feb 11 19:19:47 Jeremy: currently no way to change the store format because of upgrading constraints
Feb 11 19:20:15 Fred: easy, one-click upgrade mechanism would make more frequent core updates possible
Feb 11 19:22:48 Saq: write down specifications for planned features/changes - would make community participation easier
Feb 11 19:23:28 **topic:** sync
Feb 11 19:23:45 Saq: sync is incomplete / broken
Feb 11 19:23:57 Jeremy: users don't get the expected behavior
Feb 11 19:24:44 Saq: "TiddlyWikiAdaptor" would be less opaque than "FileAdaptor"
Feb 11 19:26:25 Fred: override default StyleSheet shadow tiddlers (e.g. StyleSheetLayout)
Feb 11 19:26:29 Saq: plugin feature request
Feb 11 19:26:51 Eric: more of a confusion
Feb 11 19:28:28 Saq / Eric: overriding shadow tiddlers might be dangerous
Feb 11 19:30:24 Saq: ticket this, discuss it on [twdev]
Feb 11 19:31:46 **topic:** community contributions (via Trac, via [twdev])
Feb 11 19:33:29 Fred: TiddlyTools CoreTweaks as Trac tickets
Feb 11 19:34:18 Eric: not all CoreTweaks suitable for the core
Feb 11 19:35:54 Eric: will make sure to ticket these things
Feb 11 19:38:01 **topic:** participation on [twdev]
Feb 11 19:38:15 Jeremy / Saq / Eric: actively following the group
Feb 11 19:39:43 Fred: two issues: responding to requests & initiating discussions
Feb 11 19:40:01 Eric: clarification required - duality [tw] vs. [twdev]
Feb 11 19:40:57 Jeremy: [twdev] as "JavaScript filter" for technical/coding issues
Feb 11 19:41:12 Fred: [twdev] not just for *core* development, but also for plugin developers' issues
Feb 11 19:41:44 Eric: start using TiddlyWiki core-dev group
Feb 11 19:42:49 Jeremy: [twdev] low traffic anyway
Feb 11 19:43:03 Eric: gray zone [tw] vs. [twdev]
Feb 11 19:43:23 Fred: no further fragmenting between [tw], [twdev] and [?]
Feb 11 19:44:30 Eric: [twdev] details might discourage budding developers
Feb 11 19:45:19 Saq: conference calls should not lead to a "clique", excluding the public => discuss on [twdev] first
Feb 11 19:46:02 **topic:** feedback
Feb 11 19:46:17 Saq: please submit feedback on how to organize these calls
Feb 11 19:46:25 **topic:** unit tests
Feb 11 19:46:43 Saq / Jon: will discuss on [twdev] (together with Paul)
Feb 11 19:48:05 ----- conference call ended -----
</pre>
: '''2008-03-18, 1000 UTC'''
== Participants ==
* Jeremy
* Martin
* Saq
* Jon
* PhilH
* Fred
== Agenda ==
[''TBC'']
== Actionables ==
=== [http://trac.tiddlywiki.org/ticket/451 Ticket #451 - DOM helper <code>$id()</code>] ===
The $id shortcut will not be implemented at this time, but we will add support for <code>getElementsByClassName</code>.
A ticket and patch should be submitted for <code>Dom.js</code> with a version of the <code>getElementsByClassName()</code> function. Said implementation should be compatible with upcoming browsers (FF3 and IE8).
Phil H has volunteered to submit a patch.
=== [http://trac.tiddlywiki.org/ticket/488 Ticket #488 - Friendlier toolbar customization] ===
This ticket will will be implemented pending the removal of the <code>listAllCommands</code> macro from the patch, and performance testing by Martin, Jeremy and Fred.
=== [http://trac.tiddlywiki.org/ticket/313 Ticket #313 - Temporary Tiddlers] ===
The submitted patch will be applied pending the rename of the Tiddler prototype function and tiddler field to <code>isSaved</code>.
=== [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/c12866467a03f929/ New upgrade mechanism] ===
* Verification file should be generated by Cook and should include the version number, date and filesize - Martin will patch Cook
* UI: Jeremy will have a play with it and provide feedback via TiddlyWikiDev at which point it can be discussed further.
=== [http://trac.tiddlywiki.org/ticket/454 Ticket #454 - Negation syntax for filter expression] ===
The patch will be applied pending sort testing by Jon.
== Minutes ==
<pre>
== Framework Functions ==
#451 (http://trac.tiddlywiki.org/ticket/451)
e.g. getElementById() & getElementsByClass()
mostly helpful for plugins rather than the core
Martin: don't clash with existing frameworks
Jeremy: don't reinvent the wheel
* $id() just an abbreviation - if we do abbreviations, then across the board
* getElementsByClass() is okay - into DOM.js (not separate library plugin)
== Toolbar Modularization ==
Martin:
* performance concerns
* incomplete (e.g. second toolbar)
* inconsistent (e.g. subtitle)
* existing TiddlyWikis don't benefit
Saq: requested often for plugins
Jeremy: reuse existing toolbar (e.g. for one at the top, one at the bottom)
FND: increases readability of templates
Saq: properly documented on release
Jeremy: no code change - just a shadow tiddler => ideal for a how-to guide
Saq: new users are not going to benefit from that
Jeremy: teach users about the general feature (giving fish vs. giving rods)
consensus: implement the toolbar redirection, but not the commands macro help thingie
ACTION: performance testing
== Temporary Tiddlers ==
#313
Jeremy: implement temporary flag as tiddler property (rather than providing a hook)
Martin: don't like the idea of a tiddler property - prefer a function to define this
Saq: Eric prefers tag as flags => function allows him that flexibility
Jeremy: makes sense - agreed
field and function should be called "isSaved"
== Upgrade Mechanism ==
Cook to generate meta-data file: version, date, filesize
consensus: no benefit in hash
UI question: alerts vs. wizard
== Plugin Template ==
no synonyms
supported by the core / plugin library vs. by convention
on wiki, post to groups
== Filtering ==
further testing required before implementation
</pre>
== Participants ==
* Jeremy
* Saq
* Eric
* Phil
* Paul
* Jon
* Fred
== Agenda ==
# jQuery and TiddlyWiki: [http://svn.tiddlywiki.org/Branches/jcore/ jcore] branch
# process for updating the version info in <code>version.js</code> immediately after each release
# review of tickets related to TiddlyTools [[#Core_Tweaks|core tweaks]]
# state of the community
=== Core Tweaks ===
'''Greatest benefit:'''
* [http://trac.tiddlywiki.org/ticket/784 #784] allow tiddler sections in TiddlyLinks to be used as anchor points like HTML intra-page anchors
* [http://trac.tiddlywiki.org/ticket/683 #683] Firefox 3 import restriction: "browse" button replacement
* [http://trac.tiddlywiki.org/ticket/604 #604] cross-platform <code>askForFilename()</code>
* [http://trac.tiddlywiki.org/ticket/444 #444] "tiddler" and "place" - global variables for computed macro parameters
* [http://trac.tiddlywiki.org/ticket/609 #609] / [http://trac.tiddlywiki.org/ticket/610 #610] toolbars - separators and transclusion
* [http://trac.tiddlywiki.org/ticket/608 #608] toolbar - more/less toggle
* [http://trac.tiddlywiki.org/ticket/607 #607] add <code>HREF</code> on permaview command
* [http://trac.tiddlywiki.org/ticket/471 #471] "creator" field for new tiddlers
* [http://trac.tiddlywiki.org/ticket/458 #458] add permalink-like HREFs on internal TiddlyLinks
'''Minimal risk and/or simple code:'''
* [http://trac.tiddlywiki.org/ticket/757 #757] add <code>removeOptionCookie()</code> function
* [http://trac.tiddlywiki.org/ticket/749 #749] <code>ieCreatePath</code> fixup for handling / in UNC paths
* [http://trac.tiddlywiki.org/ticket/742 #742] <code>animate</code> paramifier
* [http://trac.tiddlywiki.org/ticket/741 #741] allow <code><nowiki><hr></nowiki></code> directly in wiki-formatted content
* [http://trac.tiddlywiki.org/ticket/657 #657] wrap tabs onto multiple lines
* [http://trac.tiddlywiki.org/ticket/637 #637] TiddlyLink tooltip - custom formatting
* [http://trac.tiddlywiki.org/ticket/401 #401] PageTitle - instead of combined SiteTitle/SiteSubtitle in window titlebar
* (no ticket) <code><<tag>></code> macro - <code>sortby</code> parameter
* (no ticket) backslash-quoting for embedding newlines in 'line-mode' formats
'''Other:'''
* [http://trac.tiddlywiki.org/ticket/676 #676] <code>story</code> paramifier
* [http://trac.tiddlywiki.org/ticket/664 #664] Loose links (case-folded/space-folded wiki words)
* [http://trac.tiddlywiki.org/ticket/628 #628] hide "no such macro" errors
* [http://trac.tiddlywiki.org/ticket/529 #529] IE fixup - case-sensitive element lookup of tiddler elements
* [http://trac.tiddlywiki.org/ticket/67 #67] Missing links - ignore non-wiki syntax source content
== Minutes ==
=== Action Items ===
* <del>[http://trac.tiddlywiki.org/ticket/637 #637] (Fred): wontfix; should be a plugin</del>
* <del>[http://trac.tiddlywiki.org/ticket/757 #757] (Eric): cross-link with Jeremy's cookie-baking patch (included there)</del>
* <del>[http://trac.tiddlywiki.org/ticket/830 #830] (Fred): change beta tag to alpha; raise ticket (auto-update date?)</del>
* [http://trac.tiddlywiki.org/ticket/607 #607] (Eric): needs to be discussed on [twdev] (cf. #458)
* [http://trac.tiddlywiki.org/ticket/458 #458] (Eric): needs to be discussed on [twdev] (cf. #607)
* <del>[http://trac.tiddlywiki.org/ticket/741 #741] (Eric): needs to be discussed on [twdev]</del>
* [http://trac.tiddlywiki.org/ticket/683 #683] (Eric): needs to be discussed on [twdev] (cf. #604)
* [http://trac.tiddlywiki.org/ticket/604 #604] (Eric): needs to be discussed on [twdev] (cf. #683)
* <del>[http://trac.tiddlywiki.org/ticket/657 #657] (Phil): explore pure-CSS solution</del>
* <del>[http://trac.tiddlywiki.org/ticket/749 #749] (Martin): accept</del>
* [http://trac.tiddlywiki.org/ticket/444 #444] (??): accept pending discussion of implementation on [twdev]
* [http://trac.tiddlywiki.org/ticket/609 #609] (??): 2.5.1
* [http://trac.tiddlywiki.org/ticket/529 #529] (??): accept
* [http://trac.tiddlywiki.org/ticket/608 #608] (??): 2.5.1
* [http://trac.tiddlywiki.org/ticket/471 #471] (??): accept
* <del>[http://trac.tiddlywiki.org/ticket/742 #742] (??): replaced with #823</del>
* <del>[http://trac.tiddlywiki.org/ticket/67 #67] (??): accept (possibly with a different solution?)</del>
* [http://trac.tiddlywiki.org/ticket/401 #401] (??): needs to be discussed on [twdev]
* [http://trac.tiddlywiki.org/ticket/610 #610] (??): wontfix; should be a plugin -- however, requires core refactoring
* [http://trac.tiddlywiki.org/ticket/664 #664] (??): wontfix; should be a plugin -- however, might require core refactoring
* <del>[http://trac.tiddlywiki.org/ticket/831 #831] (Martin, Eric): unclear</del>
* [http://trac.tiddlywiki.org/ticket/628 #628] (??): unclear
* <del>[http://trac.tiddlywiki.org/ticket/784 #784] (??): unclear</del>
* <del>[http://trac.tiddlywiki.org/ticket/676 #676]: (Eric) wontfix; should be a plugin -- see TiddlyTools StoryViewerPlugin</del>
* [http://trac.tiddlywiki.org/ticket/829 #829] (??): unclear
=== Log ===
<pre>
2008-11-10 18:03:00 <FND> links on the wiki: http://www.tiddlywiki.org/wiki/Dev:Conference_Calls/3
2008-11-10 18:03:07 <FND> **** topic: jcore
2008-11-10 18:04:53 <FND> Jeremy: TW provides framework for browser agnosticism - jQuery now a more modern, more complete solution, avoiding duplication within TW (alternative was Dojo, but jQuery now the best match)
2008-11-10 18:05:58 <FND> conservative approach: no re-engineering from scratch - instead integrating jQuery into the core, allowing plugin authors to take advantage of jQuery
2008-11-10 18:06:53 <FND> jQuery (~30k) should replace large parts of the current TW core, preventing excessive bloat
2008-11-10 18:08:15 <FND> Eric: is 30k really all that costly? couldn't we just put it into the core without big adjustments?
2008-11-10 18:09:26 <FND> Phil: it should be fairly simple to save ~15k
2008-11-10 18:10:21 <FND> Fred: should stay below 300k
2008-11-10 18:10:45 <FND> Eric: the desire to trim should not lead to instability
2008-11-10 18:11:39 * FND wonders why there is not collaborative minuting going on
2008-11-10 18:12:40 * cdent (n=cdent@212.183.136.195) has joined #tiddlywiki
2008-11-10 18:16:33 <PhilHawksworth> sorry FND , can't hear on hands free and concentrating on this
2008-11-10 18:16:35 <PhilHawksworth> !
2008-11-10 18:17:02 <FND> 'k (I'm struggling with the crappy loudspeaker too)
2008-11-10 18:18:34 <FND> Jeremy: we should be careful about what else should we put into the "jcore" release, to keep filesize increase to a minimum
2008-11-10 18:19:16 <FND> Jeremy: conservative jQuery integration ASAP
2008-11-10 18:21:50 <PhilHawksworth> Topic: Agree on a process for updating the version info in version.js immediately after a release.
2008-11-10 18:22:21 <FND> brb
2008-11-10 18:23:34 <FND> Jeremy: alpha as automated nightly vs. beta as deliberate release
2008-11-10 18:24:01 <PhilHawksworth> jeremyruston: in favor of gleaming the svn buid number for use in alpha builds
2008-11-10 18:25:26 <PhilHawksworth> Saq1: tiddlywiki.org already uses svn revision number in the build number
2008-11-10 18:25:55 <FND> !!! actionable: change beta tag to alpha
2008-11-10 18:26:22 <FND> also auto-update the date
2008-11-10 18:26:30 <FND> !!! actionable: ticket this
2008-11-10 18:27:12 <FND> ***** topic: CoreTweaks
2008-11-10 18:27:18 <FND> http://www.tiddlywiki.org/wiki/Dev:Conference_Calls/3#Agenda
2008-11-10 18:27:31 <FND> http://www.tiddlywiki.org/wiki/Dev:Conference_Calls/3#Core_Tweaks
2008-11-10 18:28:25 <FND> #757 removeOptionCookie function
2008-11-10 18:28:46 <FND> Jeremy: included in Jeremy's cookie-baking patch
2008-11-10 18:29:27 <FND> !!! actionable: cross-link tickets
2008-11-10 18:30:06 <FND> #749 ieCreatePath function
2008-11-10 18:30:17 <FND> Jeremy: seems reasonable - should be accepted
2008-11-10 18:30:26 <FND> #742 animate paramifier
2008-11-10 18:31:32 <FND> Jeremy: should be generalized
2008-11-10 18:32:00 <FND> !!! actionable: Eric will look into that
2008-11-10 18:32:40 <pauld> could be cool for overriding "YourName" etc
2008-11-10 18:33:10 <FND> Eric to submit a "fantastic patch"
2008-11-10 18:33:21 * pauld pound sign .. bristle
2008-11-10 18:33:28 <PhilHawksworth> :)
2008-11-10 18:33:36 <Saq1> :)
2008-11-10 18:33:40 * FND is confused
2008-11-10 18:34:03 <FND> #741 allow <hr> directly in wiki-formatted content
2008-11-10 18:34:08 <pauld> http://blog.whatfettle.com/2004/03/13/pound-hash-and-octothorpes/
2008-11-10 18:34:25 * FND bookmarks for later reading
2008-11-10 18:34:34 <Saq1> pauld: amen!
2008-11-10 18:36:06 <FND> character-mode HR needed (line-mode HR present)
2008-11-10 18:36:14 <jayfresh> illustration of what eric is talking about:
2008-11-10 18:36:22 <jayfresh> http://grail.sourceforge.net/tests/listitem.html
2008-11-10 18:37:28 <FND> Saq: look at wiki creole
2008-11-10 18:38:07 <FND> !!! actionable: Eric will start thread on [twdev]
2008-11-10 18:38:19 <jeremyruston> http://www.wikicreole.org/wiki/Creole1.0#section-Creole1.0-HorizontalRule
2008-11-10 18:38:33 <FND> #657 wrap tabs onto multiple lines
2008-11-10 18:39:30 <FND> Jeremy: there should be a CSS way to resolve this rather than inserting a character
2008-11-10 18:39:47 <FND> Eric: inconsistent browser implementation
2008-11-10 18:40:31 <FND> !!! actionable: Phil will look into that
2008-11-10 18:40:44 <FND> #637 TiddlyLink tooltip - custom formatting
2008-11-10 18:44:05 <FND> Jeremy: it should be a plugin
2008-11-10 18:44:20 <FND> #401 PageTitle - instead of combined SiteTitle/SiteSubtitle in window titlebar
2008-11-10 18:45:12 * Saq1 had planned to start with the more important tickets and is a little worried we are still only going through the *simple* ones :)
2008-11-10 18:45:30 <FND> Saq1: we're almost through, but do push for the important ones
2008-11-10 18:45:56 <jeremyruston> shadow PageTitle = "<<tiddler SiteTitle>> - <<tiddler SiteSubtitle>>"
2008-11-10 18:46:26 <FND> s/PageTitle/WindowTitle/
2008-11-10 18:47:02 <FND> Saq: this should be discussed on [twdev]
2008-11-10 18:47:05 * FND applauds Saq1
2008-11-10 18:47:12 <PhilHawksworth> moooving on....
2008-11-10 18:47:25 <FND> # (no ticket) <<tag>> macro - sortby parameter
2008-11-10 18:49:09 <FND> Jeremy: agree, but implementation should be changed (use filter function)
2008-11-10 18:49:14 <FND> !!! actionable: raise ticket
2008-11-10 18:49:25 <FND> # (no ticket) backslash-quoting for embedding newlines in 'line-mode' formats
2008-11-10 18:50:55 <FND> Saq: this is in line with wiki creole
2008-11-10 18:51:04 <FND> Jeremy: would be great to have
2008-11-10 18:51:10 <FND> !!! actionable: ticket, ask Martin
2008-11-10 18:51:45 <FND> #784 allow tiddler sections in TiddlyLinks to be used as anchor points like HTML intra-page anchors
2008-11-10 18:51:51 <FND> Eric: not crucial for the next release
2008-11-10 18:52:32 <FND> Saq: there is a plugin for this already, which should not be broken by this
2008-11-10 18:52:48 <FND> #683 Firefox 3 import restriction: "browse" button replacement
2008-11-10 18:52:55 <FND> (cf. #604)
2008-11-10 18:54:14 <FND> Jeremy: generally, FF3 only grants access to the current directory
2008-11-10 18:55:00 * Saq1 has quit (Read error: 104 (Connection reset by peer))
2008-11-10 18:55:06 * Saq1 (n=Saq@212251167064.customer.cdi.no) has joined #TiddlyWiki
2008-11-10 18:55:12 * Saq1 has quit (Read error: 104 (Connection reset by peer))
2008-11-10 18:55:22 * Saq1 (n=Saq@212251167064.customer.cdi.no) has joined #TiddlyWiki
2008-11-10 18:55:35 * Saq1 has quit (Read error: 104 (Connection reset by peer))
2008-11-10 18:56:22 <FND> Eric: that's not correct
2008-11-10 18:56:57 <FND> !!! actionable: discuss on [twdev]
2008-11-10 18:57:35 <FND> Fred: reservation: should this sort of "override" go into the core
2008-11-10 18:58:29 <FND> Jeremy: reservation: #604 code is lengthy (bloat)
2008-11-10 18:58:47 <FND> # #444 "tiddler" and "place" - global variables for computed macro parameters
2008-11-10 18:58:55 <FND> !!! actionable: should be accepted
2008-11-10 18:59:02 <FND> #609 / #610 toolbars - separators and transclusion
2008-11-10 19:00:16 <FND> powerful, but complex
2008-11-10 19:00:29 <FND> Jeremy: separator is ok, but choose one or the other
2008-11-10 19:00:48 <FND> !!! actionable: accept #609
2008-11-10 19:01:06 <FND> Jeremy: #610 should be a plugin
2008-11-10 19:01:26 <FND> !!! actionable: requires refactoring to provide override hook
2008-11-10 19:01:41 <FND> #608 toolbar - more/less toggle
2008-11-10 19:03:10 <FND> Fred: might be on hover
2008-11-10 19:03:27 <FND> Fred: nvm, that's nonsense
2008-11-10 19:03:49 <pauld> can't get excited by "less" but linebreak sounds fair
2008-11-10 19:03:53 <FND> !!! actionable: should be accepted
2008-11-10 19:04:07 <FND> #607 add HREF on permaview command
2008-11-10 19:04:41 <cdent> ++
2008-11-10 19:04:56 <FND> Jeremy: don't like this - danger of opening multiple instances
2008-11-10 19:06:55 <FND> Jeremy: it's ok to require a moderate investment
2008-11-10 19:07:04 <FND> Eric: it's okay to leave it as a plugin
2008-11-10 19:07:13 <FND> #458 add permalink-like HREFs on internal TiddlyLinks
2008-11-10 19:07:28 <FND> same issues apply as with #607
2008-11-10 19:07:45 <FND> !!! actionable: discuss on the groups
2008-11-10 19:07:58 * FND makes mental note: mention Jeremy more often
2008-11-10 19:08:10 <FND> #471 "creator" field for new tiddlers
2008-11-10 19:08:32 <FND> Jeremy: should have been included in the beginning - but current patch is incomplete
2008-11-10 19:08:54 <FND> !!! actionable: should be implemented
2008-11-10 19:09:18 <FND> #676 story paramifier
2008-11-10 19:10:54 <FND> Jeremy: behavioral mismatch
2008-11-10 19:12:09 <FND> #664 Loose links (case-folded/space-folded wiki words)
2008-11-10 19:12:26 <FND> Jeremy: plugin territory - core refactoring might make this easier to implement
2008-11-10 19:12:46 <PhilHawksworth> perhaps with a ResolveTiddlerLink function
2008-11-10 19:12:47 <FND> #628 hide "no such macro" errors
2008-11-10 19:13:45 <FND> #529 IE fixup - case-sensitive element lookup of tiddler elements
2008-11-10 19:14:29 <FND> Jeremy: should be accepted
2008-11-10 19:14:43 <pauld> couldn't find similar code in JQuery, but maybe JQuery will mop this up?
2008-11-10 19:14:53 <FND> was wondering that as well!
2008-11-10 19:14:55 <FND> speak up
2008-11-10 19:14:59 <FND> thanks
2008-11-10 19:15:20 <FND> Jeremy: JS libraries don't fix this
2008-11-10 19:15:51 <FND> !!! actionable: implement #529
2008-11-10 19:15:34 <FND> #67 Missing links - ignore non-wiki syntax source content
2008-11-10 19:16:36 <FND> Jeremy: should be accepted, but there might be a better fix
2008-11-10 19:16:52 <FND> !!! actionable: implement #67
2008-11-10 19:18:11 <FND> !!! actionable: quarterly conference calls
2008-11-10 19:18:25 <FND> !!! actionable: next call's agenda: state of the community
</pre>
== Participants ==
* Jeremy
* Martin
* Saq
* Eric
* Fred
== Agenda ==
* TiddlyWiki core refactoring in light of jQuery: progress so far, tracking changes and testing
* TiddlyWiki 2.5.1 release schedule
* TiddlyWiki core documentation (see [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/35b9c2ea5433c36b discussion thread])
* [http://trac.tiddlywiki.org/ticket/974 Ticket #974]: jQuery code should be below the store area:
* Tickets [http://trac.tiddlywiki.org/ticket/608 #608], [http://trac.tiddlywiki.org/ticket/609 #609] (if time permits)
== Summary ==
* A re-commitment by everyone to ensure that every change to the TiddlyWiki core code has a ticket on Trac referencing it. This is particularly important since changesets do not allow comments and discussion whereas tickets do.
** Agreement to attempt using Eric's "Inside TiddlyWiki" document to track jQuery related refactoring. (One tiddler per function, to record the changes made to said function and the reasons behind it.)
** Saq and Eric will work together to deploy said document using TiddlyWeb so that everyone may begin using it.
* Releases during the jQuery related refactoring period will be more frequent. Possibly once every month, or every other month. However, internal releases amongst all TiddlyWiki developers rather than public releases might be utilized to optimize this process and ensure that proper testing takes place before new code goes public.
* Unanimous decision that we need more documentation of the TiddlyWiki core code and that this documenation needs to reside in the JavaScript files themselves. A syntax for this documentation will be agreed upon which allows readability of the documentation in the JavaScript files as well parsing via tools such as Code Illuminated or JSDoc. Towards this end, those who have not already done so have committed to reading and providing feedback within the next two weeks on the syntax for core code documentation in the following thread: http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/35b9c2ea5433c36b#
* Tickets 974, 609 and 608 to be implemented.
* Eric commits to submitting patches for his tickets in the future. (pending resolution of his problems with generating patches)
== Minutes ==
<pre>
agenda item #1: jQuery-related refactoring
Saq: concerned that refactoring efforts are not ticketed - thus nowhere to comment, risk of mistakes not being detected
Martin: we haven't been focusing on refactoring yet
Saq: there have been refactoring changesets already
agreement: everything needs to be ticketed
question of where to keep track of comments on changes -- e.g. committers explaining certain decisions
e.g. in-code documentation, external document/spreadsheet
each core commit should be reviewed by everyone
repeating: ticket everything.
consensus: use Eric's document to keep track of thought process
agenda item #2: 2.5.1 release schedule
work off list of open 2.5.1 tickets, catching up on outstanding issues
2.5.2 will be more refactoring-oriented
more frequent releases during the refactoring phase
Eric: one release per month
Martin: ambitious but we can do that
Fred: Eric might want nightlies
nightly.tiddlywiki.org = auto built every hour from the latest code
Eric: what's required a monthly "stability review"
agreement: will aim for once a month, or perhaps once every other month
#3. core documentation
Fred: code illuminated
extracts function documentation from source code and lets you view it alongside the function itself
action: Fred send reminder mail to look at [twdev] code documentation
Jeremy, Martin and Eric commit to review code documentation syntax within next 2 weeks so we can start documenting the core code
Everyone agrees that documenting the core code in the js files for the code themselves is a good idea and should be adopted. While there is no commitment to using code illuminated, we should make sure that the syntax we agree on allows for it.
agenda item #4: jQuery code position
Fred: view-source experience important, compressed jQuery code at the very top is an obstacle
Martin: concerned about copyright; should be at the top, and not separated from the code
Jeremy: view-source experience is important - solution: reference jQuery copyright in TiddlyWiki copyright notice
agenda item #5: #608 and #609
conclusion: implement
new agenda item (#6): #610
conclusion: core refactoring required
new agenda item (#7): misc.
rest of Eric's tickets will be discussed for 2.5.2
Fred: Eric should try to create patches
Eric: technical issues on Windows
Martin will help Eric
agreement: Eric will try to create proper patches in the future
Jeremy: re code documentation: lots of knowledge locked up in Jeremy's head
possibility of talking code through with colleagues (e.g. podcast)
</pre>
TiddlyWiki is a community project.
As such, volunteer contributions are essential to continuously improving the TiddlyWiki core.
Such contributions can take many forms — from [http://trac.tiddlywiki.org/wiki/ReportingBugs reporting bugs] to [http://trac.tiddlywiki.org/wiki/ContributingCode contributing code].
Of course [[TiddlyWiki Resources#Community Support|community support]] is a crucial part of this process as well, ensuring that new and inexperienced users are not left in the dark.
The global function <tt>convertUnicodeToUTF8 </tt>converts a string encoded in Unicode to a UTF-8 formatting. It takes one parameter, the string to convert, and returns a converted string.
{{Incomplete}}
Cook is the build tool for [[TiddlyWiki]].
It utilizes [[recipes]] to stitch together TiddlyWiki HTML files from their constituent chunks.
== External Resources ==
* [http://trac.tiddlywiki.org/wiki/Cook Cook]
* [http://trac.tiddlywiki.org/wiki/Ginsu Ginsu]
== See Also ==
* [[Dev:Ginsu|Ginsu]]
* [[Dev:Recipe|Recipes]]
* [[Dev:Chef|Chef]]
[[Category:Developer Tools]]
== Startup ==
== Event handler ==
== Classes ==
* [[Dev:TiddlyWiki Class|TiddlyWiki]]: store tiddlers and has functions for loading and saving tiddlers. It is instantiated in main() as the 'store' object.
* [[Dev:Tiddler]]: used to store information about an individual tiddler.
* [[Dev:Story]]: manages an HTML div containing a sequence of tiddlers. It is instantiated as 'story'.
* [[Dev:Wikifier]]: is used to display the contents of a tiddler.
* [[Dev:Formatter]]: is used (by a wikifier) to convert wikitext into HTML.
* [[Dev:Animator]]: manages different types of animations.
* [[Dev:Cascade]]: a specific type of animation used when opening a tiddler.
* [[Dev:Scroller]]: a specific type of animation that scrolls the browser window so that a DOM element is in view.
* [[Dev:Slider]]: a specific type of animation that "slides" open a DOM element. This is used, for example, when you click the options link on the right sidebar.
* [[Dev:String Methods]]: a number of utility methods are provided for the String class
* [[Dev:Array Methods]]: a number of utility methods are provided for the Array class
* [[Dev:Date Methods]]: a number of utility methods are provided for the Date class
== Global Objects ==
* [[Dev:config]]: contains user preferences, [[macros]], and [[Shadow Tiddlers|shadow tiddlers]].
* [[Dev:version]]: contains information about the currently running version of TiddlyWiki and plugins.
== Functions ==
* [[Dev:Overriding core functions]]
* [[Dev:addClass|addClass]]: add a CSS class to a DOM element
* [[Dev:addEvent|addEvent]]: add an event listener to a DOM element
* [[Dev:blurElement|blurElement]]: remove focus from a DOM element
* [[Dev:clearMessage|clearMessage]]: clear the TiddlyWiki's message area
* [[Dev:convertUTF8toUnicode|convertUTF8toUnicode]] and [[Dev:convertUnicodeToUTF8|convertUnicodeToUTF8]]: convert between UTF-8 and Unicode text encodings
* [[Dev:createExternalLink|createExternalLink]]: create an external link
* [[Dev:createTagButton|createTagButton]]: create a button that pops open a list of tiddlers with a certain tag
* [[Dev:createTiddlyButton|createTiddlyButton]]: create a button on the page
* [[Dev:createTiddlyElement|createTiddlyElement]]: create a DOM element
* [[Dev:createTiddlyLink|createTiddlyLink]]: create a link to another tiddler
* [[Dev:createTiddlyText|createTiddlyText]]: stuff text into a DOM element
* [[Dev:displayMessage|displayMessage]]: display a message in the TiddlyWiki's message area
* [[Dev:ensureVisible|ensureVisible]]: find the vertical scroll position required to show a DOM element
* [[Dev:findPosX|findPosX]]: find the horizontal position of a DOM element
* [[Dev:findPosY|findPosY]]: find the vertical position of a DOM element
* [[Dev:findScrollX|findScrollX]]: find the horizontal page scroll position
* [[Dev:findScrollY|findScrollY]]: find the vertical page scroll position
* [[Dev:findWindowHeight|findWindowHeight]]: return the height of the browser window
* [[Dev:findWindowWidth|findWindowWidth]]: return the width of the browser window
* [[Dev:generateRSS|generateRSS]]: render recent changes to the TiddlyWiki as an RSS feed
* [[Dev:getNodeText|getNodeText]]: get text inside a DOM element
* [[Dev:getPlainText|getPlainText]]: get a plain text version of a DOM element
* [[Dev:hasClass|hasClass]]: check whether a DOM element has a particular CSS class
* [[Dev:refreshTiddlyLink|refreshTiddlyLink]]: re-render a link to a tiddler
* [[Dev:removeChildren|removeChildren]]: remove all child nodes from a DOM element
* [[Dev:removeClass|removeClass]]: remove a CSS class from a DOM element
* [[Dev:removeEvent|removeEvent]]: remove an event listener from a DOM element
* [[Dev:resolveTarget|resolveTarget]]: determine the target of a DOM event
* [[Dev:saveOptionCookie|saveOptionCookie]]: save user options to a cookie
* [[Dev:saveChanges|saveChanges]]: save the TiddlyWiki to disk
* [[Dev:setStylesheet|setStylesheet]]: add a stylesheet to the page
* [[Dev:wikify|wikify]]: render source code into a DOM element
* [[Dev:wikifyStatic|wikifyStatic]]: render source code offscreen
* [[Dev:wikifyPlain|wikifyPlain]]: render plain text
== Variables ==
[[Category:Development]][[Category:Incomplete]]
The <tt>createExternalLink</tt> global function renders a link to an outside Web page. It requires two parameters:
* the DOM element to render into. The link is appended to any existing content.
* the URL to point to.
This function returns a DOM element reference to the link.
The global function <tt>createTagButton</tt> renders a button that displays a list of tiddlers that belong to a certain tag. Clicking a menu item opens the tiddler. This function takes three parameters in order:
* the DOM element to render into. This appends to existing content.
* the name of the tag.
* the title of a tiddler to exclude. You may leave this value off; in that case, all tiddlers matching the tag will be displayed.
This function returns a reference to the newly-created button, which is a DOM element itself.
The <tt>createTiddler</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object instantiates a [[Tiddler class|Tiddler]] object. It takes one parameter, which is the title of the tiddler requested. If a tiddler hasn't previously been created by the author, an empty Tiddler object is returned.
This method does not link the Tiddler in any way to the TiddlyWiki. You may want to use the global function [[createTiddler global function|createTiddler]] instead.
The global function <tt>createTiddlyButton</tt> renders a button on the page similar the ones you see in the top-right corner of a tiddler. It takes seven parameters in order:
* the DOM element to add the button to. The button is appended to end of any existing content.
* the text to display as the button
* the text to display as a tooltip on the button
* a callback function that is invoked when the button is clicked (e.g. <code>function() { foo(); }</code>)
* a CSS class to apply to the button. If not specified, this is 'button'.
* a DOM id to give the button
* an access key for the button
You may specify a null value for any of these; if the first parameter is null, the button is created but not displayed. This function returns the created button as a DOM element.
The global function <tt>createTiddlyElement</tt> creates a DOM element. This function takes five parameters:
* the parent DOM element to add the new element to
* the type of element to create. This is a lowercase string matching the name of the HTML tag you'd like to create -- so, for example, to create a div element, you'd pass 'div'.
* the DOM id to give the element.
* the CSS class to give the element.
* text to put inside the element.
Any of these parameters may be null. This function returns the created DOM element.
The global function <tt>createTiddlyLink</tt> renders a link to a tiddler. This function takes five parameters:
* the DOM element to render into. The link is appended to any existing content.
* the title of the tiddler to link to.
* a boolean indicating whether the link should display the title of the tiddler. If false is passed here, the link's text will be the empty string.
* the CSS class to assign to the link. This parameter is optional.
* a boolean indicating whether the link should act as a permalink, or it should be a normal link. This parameter is optional.
The <tt>createTiddlyText</tt> global function adds a text node to an existing DOM element. It takes two parameters:
* the DOM element to add the text to
* the text to add
This function returns the first parameter.
{{Merge|[[Dev:Developing and Testing a Plugin]]}}
== Introduction ==
This guide describes creating a basic custom macro. Macros in TiddlyWiki look like this in a tiddler's markup (i.e. unformatted contents):
<<myMacro>>
== Macro parameter formatting ==
Macros can take parameters like so:
<<myMacro param1 param2>>
Parameters with spaces can be quoted using either single quotes, double quotes, or double square brackets. For example:
<<myMacro <nowiki>[[first param]]</nowiki> 'second param' "third param">>
(To prevent confusion, it's a good idea to pick one quoting character and stick with it.)
Macros can also have named parameters:
<<myMacro this:Foo that:Bar>>
Named parameters can be passed quoted strings, just like positional parameters:
<<myMacro this:'hello there' that:"what's up">>
If a quote is required inside a parameter, put a backslash in front of it:
<<myMacro this:'what\'s up'>>
Parameters can be the result of executing a JavaScript expression. TiddlyWiki evaluates the expression before passing it along to the macro.
<nowiki><<myMacro {{'tiddler.getTiddlerText("TiddlerName")'}} >></nowiki>
The JavaScript expression can span multiple lines of text, so it can be formatted for readability.
Parameters cannot contain double closing angle brackets. This won't work, for example:
<<myMacro 'over this way ->>'>>
This confuses the parser, which thinks the macro invocation ended early.
Parameters can be placed on multiple lines to help readability:
<<myMacro
thing1
thing2
>>
== Hello world, part 1 ==
Creating a custom macro requires writing a Plugin. A Plugin in TiddlyWiki is merely a tiddler that contains some JavaScript that will be executed when TiddlyWiki starts up. First create a very basic Plugin and make sure it's running.
=== Creating a Plugin ===
* Create a new tiddler by clicking "new tiddler"
* Give it a title, e.g. "MyFirstPlugin"
* Give it the tag "systemConfig" (without the quotes). Tags are case sensitive in TiddlyWiki, so you must have the lowercase 's' and upper case C correct.
* In the body of the tiddler, paste in the following:
// this will run on startup
alert("Hello world");
* Click "done" to finish editing the tiddler. Save the TiddlyWiki and reload.
* When the TiddlyWiki loads an alert should appear with the message "Hello world". If so, then the Plugin is working! If not, check to make sure it is tagged properly.
The Plugin is not very useful so far, but it's a good first step.
=== Fixing JavaScript Errors ===
When TiddlyWiki 2.1 or later, there is a PluginManager that handles problems with Plugins gracefully. It can be used to diagnose problems.
* Edit the MyFirstPlugin tiddler.
* Make a deliberate typo by changing <code>alert</code> to <code>alerrt</code>.
* Click "done", save the TiddlyWiki and reload.
* The PluginManager should display this message next to the tiddler's name: "Error: ReferenceError: alerrt is not defined"
* Fix the typo, save, and reload the TiddlyWiki.
=== Making the Code Look Better ===
To make the code more readable, one can use a special version of the preformatted text formatting syntax, <code>{{{</code> and <code>}}}</code>. It's specifically for use in Plugins.
Edit the Plugin tiddler so it looks like this:
<source lang="javascript">
//{{{
// this will run on startup
alert("Hello world");
//}}}
</source>
Double forward slashes (<code>//</code>) introduce a JavaScript comment. When these edits are finished, it should look more like code. Also, TiddlyWiki won't try to interpret any of the code as formatting instructions.
== Hello world, part 2 ==
The above shows how to call a macro from within a tiddler and how to plug some code into TiddlyWiki itself. The following section puts these two concepts together.
===Linking a Macro to the Code===
When TiddlyWiki encounters a macro in a tiddler, it looks for an identically named 'macro' within the global object <code>config.macros</code> and then calls the <code>handler</code> function of that macro. Edit the Plugin tiddler so it looks like this:
//{{{
config.macros.helloWorld = {
handler: function (place, macroName, params, wikifier, paramString, tiddler)
{
// this will run when macro is called from a tiddler
alert("Hello world");
}
};
//}}}
Save the TiddlyWiki and reload. This time, the alert shouldn't pop up immediately. That's because the macro needs to be invoked by a tiddler to act. Do that by following these steps:
# Create a new tiddler by clicking "new tiddler".
# Give it a title, e.g. "MyFirstPluginTest".
# In the body of the tiddler, type in the following: <<helloWorld>>
# Click "done" to finish editing the tiddler. As soon as this is done the alert box should pop up again. This is because TiddlyWiki immediately re-displays the tiddler after it is edited.
If there is an error message after reloading the TiddlyWiki, check the punctuation. A stray semicolon can sometimes mess things up.
===Getting Parameters===
The macro does something now when called. Making it more flexible requires parameters as discussed above. The argument <code>params</code> provides the macro with an array of strings that are passed in from the tiddler.
Try it out. Replace:
alert("Hello world");
with
var who = params[0] || "world";
alert("Hello " + who);
Save, reload, and look at MyFirstPluginTest again. So far it's the same old popup message... edit it as well, so that it looks like:
<<helloWorld 'all you TiddlyWiki fans'>>
The message will change. In many cases, it's a good idea to have defaults for macro parameters, as in the example. JavaScript's logical-or operator makes this straightforward; if params[0] is missing, as when the macro is invoked as <code>helloWorld</code>, the variable who gets the value "world".
In order to use named parameters, the parameter string must first be parsed. For example, if the macro was called like so <code><<foo key:"bar baz">></code> then the handler
config.macros.foo.handler = function(place, macroName, params,
wikifier, paramString) {
var prms = paramString.parseParams(null, null, true);
var bar = getParam(prms, "key");
alert(bar);
};
would pop an alert box containing the words "bar baz".
cf. [[Dev:Macro Parameters]]
===The Handler Function===
So what do the rest of those arguments to the handler function do? Here's a quick summary:
{| border="1"
| place
| the DOM object that provides the document location where the macro is being rendered in the tiddler
|-
| macroName
| the name of the macro being called. It's possible to have several different invocations call the same macro; in that case, this would allow the code to determine how the macro is being called.
|-
| params
| An array corresponding to the parameters passed into this macro.
|-
|wikifier
| The Wikifier object displaying the current tiddler. Modifying this can alter how the rest of a tiddler is rendered.
|-
| paramString
| all the params passed to the macro, as a single string (suitable for calling parseParams)
|-
| tiddler
| a TiddlyWiki object representing the tiddler from which the macro was called
|-
|}
<br>
Finally, combine these parameters with a TiddlyWiki global function named [[wikify]] to finish the macro. Instead of popping open an alert box, it'll display its message right in the tiddler itself.
//{{
config.macros.helloWorld = {
handler: function (place, macroName, params, wikifier, paramString, tiddler)
{
// this will run when macro is called from a tiddler
var who = params[0] || 'world';
wikify('Hello //' + who + '// from the "' + macroName + '" macro in tiddler [[' + tiddler.title + ']].', place);
}
};
//}}
==Hello World, Part 3==
===Adding Plugin Documentation===
This is under construction
===The Complete Hello World Plugin===
The complete Plugin for the HelloWorld macro is available for importing or viewing here:
[http://mptw2.tiddlyspot.com/#HelloWorldMacro HelloWorldMacro]
[[Category:Custom Macros]][[Category:Macros]]
{{Incomplete}}
{{Review}}
==The Edit/Test Cycle==
Initially when the developer starts working on a Plugin, the Edit/Test cycle will typically be:
* edit the Plugin Tiddler in the TiddlyWiki
* save the [http://www.TiddlyWiki.com TiddlyWiki]
* re-load the HTML file in the browser
* execute the test case(s) (assuming there were no syntax errors in the code)
As the size and complexity of the Plugin grows, it can be helpful to factor the Plugin code into its own file and to use certain browser Add-ons as an aid. This allows editing the Plugin code in any editor, including programmers editors that have syntax highlighting, auto-complete, code-folding, and other useful development features.
* Keep the Plugin source code in a separate file
* Have the test [http://www.TiddlyWiki.com TiddlyWiki] load the Plugin from the Plugin source file
* Use Firefox add on [https://addons.mozilla.org/en-US/firefox/addon/60 Firefox Web Developer], very useful for finding those uncaught JavaScript exceptions.
* Firefox add on [https://addons.mozilla.org/en-US/firefox/addon/1419 IE Tab] is also extremely useful for developing under Windows as it allows the developer to switch rendering engines between IE and Firefox - testing Firefox/IE compatibility in one browser.
==Procedure for Loading a Plugin From An External File==
===The Simplest Method===
To make a Test [http://www.TiddlyWiki.com TiddlyWiki] that loads the Plugin from a file, a couple of things are needed.
* An empty [http://www.TiddlyWiki.com TiddlyWiki] into which is installed
* A test Tiddler called '''MarkupPostBody''' with the following contents:
<script src="file:///Path/to/your/new/plugin.js" language="javascript" type="text/javascript"></script>
#Save the Test [http://www.TiddlyWiki.com TiddlyWiki].
#Reload the Test [http://www.TiddlyWiki.com TiddlyWiki] - the Plugin will be loaded.
#It should now be possible to edit the Plugin code in any editor and simply refresh the browser to load any changes.
Please note:
* Make sure that no tiddler contains a duplicate copy of the Plugin source code.
* The Plugin will no longer be visible in the ''Plugin Manager''.
* This method will not work for ''all'' Plugins. Some Plugins require access to 'story', which does not exist at the time that this script would execute the plugin code.
===The Comprehensive Method===
To fix the problems noted above, Saq Imtiaz suggested (in [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/7417b8332ad23a10/4ff4fa43141a20e6?lnk=gst&q=Lyall#4ff4fa43141a20e6 a TiddlyWikiDev Google Group post]) the following method:
#Create the same PostMarkupBody tiddler as before, but instead of loading plugin.js, load a JavaScript file called 'loadExternal.js', which contains the following text
old_load_plugins = window.loadPlugins;
window.loadPlugins=function()
{
old_load_plugins.apply(this,arguments);
loadExternalScripts.apply(this,arguments);
};
function loadExternalScripts()
{
if (!store.isTiddler("ExternalScripts"))
return;
var originalPath = document.location.toString();
var localPath = getLocalPath(originalPath);
var backSlash = true;
var dirPathPos = localPath.lastIndexOf("\\");
if(dirPathPos == -1) {
dirPathPos = localPath.lastIndexOf("/");
backSlash = false;
}
var scriptPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/");
var scripts = store.getTiddlerText("ExternalScripts").readBracketedList();
for (var i=0; i<scripts.length;i++)
{
eval(loadFile(scriptPath+scripts[i]));
}
};
TiddlyWiki.prototype.isTiddler= function (title)
{
return store.tiddlerExists(title) || store.isShadowTiddler(title);
};
#After creating the PostMarkupBody and the above file, create a New Tiddler called 'ExternalScripts' and place within it a list of files (newline delimited) of the javascript source code for the Plugin under development. This script hijacks the Plugin load module and loads the Plugin(s) from files, rather than from tiddlers, ''after'' the story has been created, allowing the Plugin to execute exactly as it would as if it had been loaded from a tiddler.
#Save the test TiddlyWiki and reload. The Plugin still won't appear in the Plugins backstage area but it will have executed.
==Debugging Tips==
During development, the developer can use the alert() javascript function, but a more useful [http://www.TiddlyWiki.com TiddlyWiki] one is [http://www.tiddlywiki.org/wiki/Dev:DisplayMessage displayMessage()] - this is the one that puts those 'saved' messages at the top right of the screen.
It is also possible to have 'permalink' setup to the testing tiddler(s) such that the refresh brings up the test tiddlers straight away, without having to search and click. Simply display all the relevant testing tiddlers, click the permalink button and then refresh as appropriate.
== See Also ==
* [[Dev:Plugin Specifications]]
* [[Dev:Best Practices]]
The global function <tt>displayMessage</tt> displays a message in the TiddlyWiki's message area. It accepts two parameters:
* a string to display
* a title of a tiddler to link to
This function does not return any value.
The <tt>displayTiddler</tt> method of the [[Story]] class works the same way that the createTiddler method does, except that the newly-displayed tiddler is animated opening, and the browser window scrolls to display it. This method takes five parameters:
* the parent DOM element to display the tiddler inside (required but may be <tt>null</tt> if the tiddler already exists, or the special strings "bottom" and "top" to place the newly created tiddler at the bottom or top of the story)
* the title of the tiddler or a tiddler itself
* optionally the template to view the tiddler as. This should be either the constant <tt>DEFAULT_VIEW_TEMPLATE</tt> or <tt>DEFAULT_EDIT_TEMPLATE</tt>.
* optionally a boolean value indicating whether to animate opening the tiddler
* optionally a boolean value indicating whether to animate slowly. To see this in action, hold down the Shift, Option, or Alt key when clicking a tiddler link.
This method does not return any values.
<pre>
//# Display a given tiddler with a given template. If the tiddler is already displayed but with a different
//# template, it is switched to the specified template. If the tiddler does not exist, and if server hosting
//# custom fields were provided, then an attempt is made to retrieve the tiddler from the server
//# srcElement - reference to element from which this one is being opened -or-
//# special positions "top", "bottom"
//# tiddler - tiddler or title of tiddler to display
//# template - the name of the tiddler containing the template -or-
//# one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or-
//# null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not
//# animate - whether to perform animations
//# customFields - an optional list of name:"value" pairs to be assigned as tiddler fields (for edit templates)
//# toggle - if true, causes the tiddler to be closed if it is already opened
//# animationSrc - optional. If provided, this will specify the element which is to act as the start of the animation -or-
//# the source of the animation will be the srcElement.
</pre>
==Examples==
<pre>
story.displayTiddler(null, "Title of an Existing Tiddler");
</pre>
<pre>
story.displayTiddler("bottom", "Title of Tiddler To Create", DEFAULT_EDIT_TEMPLATE);
</pre>
The <tt>displayTiddlers</tt> method of the [[Story]] class works the same way that the [[Dev:DisplayTiddler|displayTiddler]] method does, only it takes an array of tiddler titles as its second parameter. Each tiddler in the array is opened on the page.
dynamically loading content from server
== Goals ==
* reduce initial load time
* improve scalability
== Approaches ==
=== Lazy Loading ===
* serve client with metadata on all tiddlers
* load tiddler contents as needed
* limited scalability
=== On-demand Loading ===
* only serve basic client
* load entire tiddlers from server as needed
* shift operations involving all tiddlers (e.g. search, tiddler relations) to the server
* infinite scalability
== Issues ==
* determining what constitutes [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/6176bd7c7ae2400f/e02077f7ccfa3353?#e02077f7ccfa3353 essential tiddlers] for the basic client (e.g. shadow tiddlers, plugins etc. - possibly also [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/6176bd7c7ae2400f/98a4d5fc56671145?#98a4d5fc56671145 recursively])
* modifying TiddlyWiki processing to handle asynchronous operations
* plugin compatibility
== Miscellaneous ==
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/6176bd7c7ae2400f/27df8cea590575b4?#27df8cea590575b4 streaming] tiddlers in the background after startup
* tiddler objects could have a [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/e3a0eb091ec4efb5/aafddd90ef00c095?#aafddd90ef00c095 status flag] indicating whether they are "skinny" or complete
* a [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/e3a0eb091ec4efb5/cb99164bf661866e?#cb99164bf661866e splash screen] might improve user experience
== See Also ==
* [[Server-Side Implementations]]
== External References ==
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/e3a0eb091ec4efb5 <nowiki>[twdev]</nowiki> TiddlyWeb: Lazy loading and load-on-demand]
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/6176bd7c7ae2400f <nowiki>[twdev]</nowiki> skinny tiddlers]
The global function <tt>ensureVisible</tt> returns the horizontal position, in pixels, required to bring a DOM element into view. It takes one parameter, the DOM element to scroll into view.
The <tt>escapeLineBreaks</tt> method of a [[Tiddler]] object converts newline characters in the tiddler's text property into literal \n strings, and converts typed backslashes into double backslashes (\\). It takes no parameters and returns the parsed text.
{{Review}}
TiddlyWiki 2.1 allows you to save arbitrary custom fields with your tiddlers. Here is a short introduction to using these fields.
==Basics==
Edit your ViewTemplate tiddler. Paste in the following after the stuff that's already there:
<div>My Thing: <span macro="view mything"></span></div>
Edit your EditTemplate tiddler. Paste in the following after the stuff that's already there:
<div>My Thing: <span macro="edit mything"></span></div>
That's all you need to do -- you've created a custom field. When you view a tiddler you should see the My Thing field, and when you edit a tiddler you can enter stuff in there and it will save with the tiddler.
Tiddler fields are [http://trac.tiddlywiki.org/ticket/356 currently] case-sensitive and '''must be all lower case''' for their values to be stored/retrieved sucessfully.
If you want the field contents wikified when viewing (eg. so links works) add a space and the
word 'wikified' after 'mything' (ViewTemplate only, not EditTemplate):
<div>My Thing: <span macro="view mything wikified"></span></div>
==Changing the appearance of the field value==
You can use styles to make the field appear however you want. Eg:
<div style="font-size:200%;">My Thing: <span macro="view mything"></span></div>
A more manageable way is to use a class and put the styles in your StyleSheet tiddler. For example:
<div class="mything">My Thing: <span macro="view mything"></span></div>
The put something like this your StyleSheet tiddler:
.mything {
color:white;
background: red;
border:3px solid green;
}
If you are storing wiki format text in your field, you can ask for the wikifier to render the field contents by adding "wikified" to the macro call:
<span macro="view myThing wikified"></span>
Conditional display of field values can be achieved using the [[HideWhenPlugin]].
==Changing the size of the edit box==
By default the edit boxes come out a certain size. If you need to change the size you can do it with CSS. For example:
<div><span class="smallerEdit" macro="edit mything"></span></div>
Then put something like this your StyleSheet tiddler:
.smallerEdit input {
width:5em;
}
==Using HideWhen to do conditional fields based on tags==
(This requires that you install HideWhenPlugin from [http://mptw.tiddlyspot.com/#HideWhenPlugin MonkeyPirateTiddlyWiki].)
Let suppose you have a some tiddlers tagged with "friend" and you'd like to use some custom field to maintain some information about them, say their favourite food.
In ViewTemplate:
<div macro="showWhen tiddler.tags.contains('friend')">
Favourite food: <span macro="view favouritefood"></span>
</div>
In EditTemplate:
<div macro="showWhen tiddler.tags.contains('friend')">
Favourite food: <span macro="edit favouritefood"></span>
</div>
Now you can view and edit your friends' favourite food.
==Using forEachTiddler with tiddler fields==
(This requires you install ForEachTiddlerPlugin from [http://tiddlywiki.abego-software.de Abego Software].)
To list all your friends whose favourite food is pizza you can do this:
<<forEachTiddler
where
' store.getValue(tiddler,"favouritefood") == "pizza" '
>>
This will display a bulleted list of tiddlers that match the test: <tt>store.getValue(tiddler,"favouritefood") == "pizza"</tt> (if you don't want a bulleted list, refer to the ForEachTiddler documentation for how to customise the output).
== Using readBracketedList to make a list ==
Suppose you want to be able to store more than one favourite food. The best way to do this is to use the same format that TiddlyWiki uses for tags, ie, <nowiki>"this that [[the other]]"</nowiki> means three items, "this", "that" and "the other". There is a TiddlyWiki method called readBracketedList that we can use for this.
Now if we have at least one food in the custom field we can do this to see who likes pizza:
<<forEachTiddler
where
' store.getValue(tiddler,"favouritefood") &&
store.getValue(tiddler,"favouritefood").readBracketedList().contains("pizza") '
>>
Who likes chicken ''or'' fish?
<<forEachTiddler
where
' store.getValue(tiddler,"favouritefood") &&
store.getValue(tiddler,"favouritefood").readBracketedList().containsAny(["chicken","fish"]) '
>>
Who likes olives ''and'' anchovies?
<<forEachTiddler
where
' store.getValue(tiddler,"favouritefood") &&
store.getValue(tiddler,"favouritefood").readBracketedList().containsAll(["olives","anchovies"]) '
>>
The methods "contains", "containsAny" and "containsAll" are handy Array methods defined by TiddlyWiki. And "readBracketedList" is a String method that is used to convert a tags string into an Array. The reason we have to first test if the value is defined is that otherwise readBracketedList will fail on tiddlers that don't have a favouriteFood field.
Please note that the above 3 contain Array methods are case sensitive, and will only find entire words: in the above example, "olives" will be found, "olive", "OLIVE", or "Olives" will return no results. An asterisk (*) used as a wildcard does not work for these contain methods.
==Filtering based on more than one field==
Provided you had set up another custom field "favouritedrink" per the above instructions, the following should allow you to further filter based on more than one field:
Who likes olives ''and'' coffee?
<<forEachTiddler
where
' store.getValue(tiddler,"favouritefood") &&
store.getValue(tiddler,"favouritefood").readBracketedList().contains("olives") &&
store.getValue(tiddler,"favoritedrink") &&
store.getValue(tiddler,"favouritedrink").readBracketedList().contains("coffee")'
>>
== See Also ==
* [[Extended Fields]]
The <tt>fetchTiddler</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns a [[Tiddler class|Tiddler]] that the TiddlyWiki contains. It expects one parameter: the name of the tiddler. If the tiddler does not exist, then an undefined value is returned. (This is different from a null value.) This is a primitive method used by many other methods in the TiddlyWiki class.
The <tt>filterTiddlers</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object retrieves a filtered list of its tiddlers. It returns an array of Tiddler() objects that match the filter expression. The method takes one parameter:
* <tt>filter</tt> - the filter string (returns an empty array if not provided)
A filter follows a grammar that can be described in [http://en.wikipedia.org/wiki/EBNF EBNF]:
filter := filterStep {'|' filterStep }
filterStep := '[' filterElements ']', {'[' filterElements ']'}
filterElements := filterElement, {filterElement}
filterElement := '['TiddlerName']' | tag'['TagName']' | sort'['SortField']' | limit'['NoOfResults']'
Another way of expressing this, without the (in this case) slightly confusing EBNF syntax, might be:
A filter string is of the form:
filterStep | filterStep ... // only one filterStep is required
where filterStep is of the form:
[filterElements] [filterElements] ... // only one filterElements is required
where filterElements is one or more of the following:
[TiddlerName]
tag[TagName]
sort[SortField]
limit[NoOfResults]
Example:
store.filterTiddlers("[tag[blog post]sort[-modified]limit[10]]")
returns any tiddler tagged "blog post", limited to the 10 most recently edited tiddlers.
== Allowed types of filterElement ==
<tab class="wikitable" sep="comma" head="top">
filter,meaning
[text],refers to a single tiddler with title "text"
<nowiki>[text text]</nowiki>,refers to a single tiddler with title "text text"
tag[tag],refers to any tiddler tagged with "tag"
tag[a tag],refers to any tiddler tagged with "<nowiki>[[a tag]]</nowiki>"
sort[+field] or [sort[field]],sorts any results already collected in ascending order by "field"
sort[-field],sorts and results already collected in descending order by "field"
limit[integer],limits the results array to contain no more than "integer" tiddlers
</tab>
== Multiple filterElements and filterSteps ==
Multiple <tt>filterElements</tt> are combined by concatenating the results from each <tt>filterElements</tt>.
Multiple <tt>filterStep</tt>, on the other hand, are evaluated in succession, with the results of each <tt>filterStep</tt> used as the tiddler set for the next <tt>filterStep</tt>.
Examples:
store.filterTiddlers("[tag[mish]] [tag[mash]]") // two filter elements
returns any tiddler tagged with "mish" or "mash".
store.filterTiddlers("[tag[mish]] | [tag[mash]]") // two filter steps
returns any tiddler tagged with "mish" and of these, any tagged with "mash".
The <tt>findContainingTiddler</tt> method of the [[Story]] class returns the DOM element corresponding to an entire tiddler on the page that encloses the DOM element passed to the method. If the passed element is not inside a tiddler, a null value is returned.
The global function <tt>findPosX</tt> returns the horizontal position of a DOM element on the page. It takes one parameter, the element to examine.
The global function <tt>findPosY</tt> returns the vertical position of a DOM element on the page. It takes one parameter, the element to examine.
The <tt>findScrollX</tt> function returns the current horizontal scroll position of the page. It takes no parameters.
The <tt>findScrollY</tt> function returns the current vertical scroll position of the page. It takes no parameters.
The <tt>findWindowHeight</tt> global function returns the height of the browser window. It takes no parameters.
The <tt>findWindowWidth</tt> global function returns the width of the browser window. It takes no parameters.
The <tt>focusTiddler</tt> method of the [[Story]] class gives a text field belonging to a tiddler focus. It takes two parameters:
* the title of the tiddler
* a string corresponding to the type of field to focus. If a null value is passed here, the first edit field in the tiddler (typically the title) is focused.
This method returns no value.
The <tt>forEachTiddler</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object executes a function on all tiddlers in the TiddlyWiki. It expects exactly one parameter: the function to execute. The function is repeatedly invoked, each time passed these three parameters in order:
* the parent TiddlyWiki
* the title of the tiddler
* the [[Tiddler class|Tiddler]] object itself
Formatters are stored as an array of objects, defined in
<code>config.formatters[]</code>.
The TiddlyWiki core's <code>wikify()</code> process loops through this
array ''in order'', testing the <code>match</code> pattern of each formatter, one
at a time, until a match is found.
At startup, a formatter object is created that contains <code>config.formatters</code>, which is a set of rules about how to turn wiki syntax into HTML.
The <code>Formatter</code> constructor collects these into <code>formatter.formatters</code> and their regular expression match strings into one long RegEx called <code>formatter.formatterRegExp</code>.
The <code>formatter</code> object is returned by <code>getParser()</code> by default.
Some functions always use the global formatter object when creating their wikifiers: <code>wikifyPlainText()</code>, <code>highlightify()</code>.
A particular <code>formatter</code> has a variable called <code>termRegExp</code>, which is the RegEx to match the end of a type of expression.
In the table formatter, there are two terminators: <code>rowTermRegExp</code> and <code>cellTermRegExp</code>.
The <tt>gatherSaveFields</tt> method of the [[Story]] class gathers up all saveable edit fields from tiddlers on the page. It takes two parameters:
* the element to search for edit fields
* an object reference which will be filled with data found.
This is mostly used by the [[hasChanges]] method, but is available for use.
The <tt>generateFingerprint</tt> method of a [[Tiddler]] object returns a SHA-1 hash of the tiddler's text. Currently this isn't used by the TiddlyWiki code, but it is available to macros.
The global function <tt>generateRSS</tt> returns a string of a full RSS feed with the most recently changed tiddlers. This function takes no parameters; the number of items generated is governed by the variable <tt>config.numRssItems</tt>.
The <tt>getLinks</tt> method of a [[Tiddler]] object returns an array of strings, each corresponding to a link present in the tiddler's source code. Using this method is preferable to examining the <tt>links</tt> property directly. There are no parameters to this method.
The <tt>getMissingLinks</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array of strings, each entry a link that a tiddler refers to but doesn't have a matching tiddler defined. This array is sorted by name. This method does not take any parameters.
Tiddlers tagged <tt>systemConfig</tt> or <tt>excludeMissing</tt> will not be checked for missing links.
The global function <tt>getNodeText</tt> returns the text inside a DOM element. This takes one parameter, the element to examine.
The <tt>getOrphaned</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array of strings, each the name of an orphaned tiddler -- that is, a tiddler with no links to it. This array is sorted by name. This method takes no parameters.
The <tt>getPlainText</tt> returns a plain text version of the text inside a DOM element. It takes one parameter, the element to examine.
The <tt>getRecursiveTiddlerText</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns the source code of a tiddler with links replaced with their contents. That is, if two tiddlers are defined like so:
'''tiddler1'''
Hello, <nowiki>[[tiddler2]]</nowiki>.
'''tiddler2'''
world
executing <tt><nowiki>getRecursiveTiddlerText('tiddler1', '', 1)</nowiki></tt> returns <tt>Hello world</tt>. This method expects three parameters in order:
* the title of the tiddler to retrieve
* default text to return if the tiddler does not exist
* the maximum depth of links to follow
The <tt>getReferringTiddlers</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns all tiddlers that link to the tiddler passed. Unlike [[getOrphans]] or [[getMissingLinks]], this returns actual [[Tiddler]] objects, not titles. However, it expects these parameters in order:
* the title of a tiddler
* ''unused parameter — pass a null value''
* the field to sort results on. If not specified, results are sorted by title.
The <tt>getShadowed</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array of all [[Shadow Tiddlers|shadow tiddlers]] in the TiddlyWiki. Unlike the [[Dev:getMissingLinks|getMissingLinks]] and [[Dev:GetOrphans|getOrphans]] methods, it returns [[Tiddler]] objects, not titles. This method takes no parameters.
The <tt>getSubtitle</tt> method of a [[Tiddler]] object returns a string with text suitable for display as a tiddler's subtitle, incorporating the tiddler's author and modification date. It takes no parameters.
The <tt>getTemplateForTiddler</tt> method of the [[Story]] class returns HTML source code ready to be used as a template that is stored inside a tiddler. This method takes three parameters:
* the title of the tiddler that is about to be displayed
* the title of the tiddler containing the template source code. Note that the constants <tt>DEFAULT_VIEW_TEMPLATE</tt> and <tt>DEFAULT_EDIT_TEMPLATE</tt> do not work here.
* a reference to the Tiddler about to be displayed
Only the second parameter is actually used by the TiddlyWiki code, but the other information is passed for the benefit of plugin developers who override this method.
The <tt>getTiddlerText</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns the source code of a tiddler. It takes two parameters:
* the title of the tiddler
* the string to return if the tiddler does not exist (by default, the null value — not an empty string)
If a tiddler is not defined by the author but a [[Shadow Tiddlers|shadow tiddler]] exists, its source code is returned instead. If the tiddler doesn't exist, then the second parameter passed is returned.
The <tt>getTiddlers</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array of every tiddler in the TiddlyWiki, sorted by a field. Its only parameter, the name of the field to sort on, is optional. This can be any property of the [[Tiddler]] class. If the TiddlyWiki is empty, an empty array is returned.
{{Incomplete}}
Ginsu splits the tiddlers in a TiddlyWiki HTML out into separate files ready for checking in to Subversion.
cf. [http://trac.tiddlywiki.org/wiki/Ginsu Trac]
== External Resources ==
* [http://trac.tiddlywiki.org/wiki/Ginsu Ginsu]
== See Also ==
* [[Dev:Cook|Cook]]
* [[Dev:Recipe|Recipes]]
* [[Dev:Chef|Chef]]
[[Category:Developer Tools]]
The <tt>hasChanges</tt> method of the [[Story]] class returns whether there are unsaved changes for a tiddler currently on the page. It takes one parameter, the title of the tiddler to check.
The global function <tt>hasClass</tt> returns whether a DOM element belongs to a CSS class. It takes two parameters in order:
* the DOM element
* the class to check for
{{Incomplete}}
If possible, hijacking a function is preferable to overriding it, as it is less invasive and thus more future-proof.
== Example ==
Hijacking global function <code>sampleFunc</code>:
<source lang="javascript">
(function() { // closure to create local scope for backup
// backup original function
var sampleFunc_orig = sampleFunc;
// override original function
window.sampleFunc = function(foo, bar, baz) { // arguments correspond to original function signature
// perform custom operations
/* ... */
// invoke the original function
return sampleFunc_orig.apply(this, arguments);
};
})();
</source>
{{Note|
The [[Dev:Best Practices#Creating Aliases|wrapping function technique]] illustrated here is optional, but recommended to avoid clashes with other hijacks.
}}
== External Resources ==
* [http://mptw2.tiddlyspot.com/#HijackingHowto HijackingHowto]
{{Incomplete}}
Hijacking the ''onclick'' handler of a button:
{{Quote|[http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/db1dc75adc5357d7 Eric]|<pre>
var btn = document.getElementById("mybtn");
btn.old_onclick = btn.onclick;
btn.onclick = function() {
this.old_onclick.apply(this,arguments);
alert("goodbye");
}
</pre>
}}
== See Also ==
* [[Dev:Hijacking]]
[[Category:Design Patterns]]
This page is intended to keep track of yet unresolved issues regarding the roadmap for further development of the TiddlyWiki core.
__TOC__
== Enhancements ==
; standards compliance
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/de9cf00aec4cb148/ <nowiki>[twdev]</nowiki> image formatter: invalid attributes]
; DOM utilities
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/fc0a859f9e10af8f/ <nowiki>[twdev]</nowiki> DOM utilities: potentially more efficient adding/removing of class names]
== Changes ==
; namespaces
: [http://groups.google.com/group/TiddlyWiki/browse_thread/thread/e9347213b3151ddf/24ec8cc5e1c14278?#24ec8cc5e1c14278 <nowiki>[tw]</nowiki> Confused about Sync and Workspaces]
; PrettyLink syntax
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/8e4889dbc861f0ef/ <nowiki>[twdev]</nowiki> PrettyLink syntax: Creole compatibility]
: [http://groups.google.com/group/TiddlyWiki/browse_thread/thread/3a7115fb85461b3f/ <nowiki>[tw]</nowiki> Referendum on core changes]
; centralized date formats
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/2fe4e7bd3e01f8a6/ <nowiki>[twdev]</nowiki> centralized date formats]
; tiddler revision history
: cf. [http://simonmcmanus.wordpress.com/2007/08/14/enterprise-tiddlywiki/ Enterprise TiddlyWiki]
; [[Dev:TWUI 2.0|UI Redesign]]
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/48b7d64307cc9dfa/ <nowiki>[twdev]</nowiki> TiddlyWiki v2.2b5 feedback: layout/style]
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/b55011665c5e04d9/ <nowiki>[twdev]</nowiki> StyleSheetLayout: overflow technique]
[[Image:EditPortlet.png|right|thumb|150px|EditPortlet mock-up]]
; EditPortlet (non-disruptive edit mode)
: edit mode as a (temporary) portlet instead of replacing the wikified tiddler view (see mock-up image)
== Misc. ==
; embedding external files
: [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/d900d5951edd0e73 <nowiki>[twdev]</nowiki> Cross-browser Base-64 Images]
; TiddlyWiki.org skin & logo
: cf. [[User:FND/MediaWiki Skin]]
# [http://softwareas.com/tiddlywiki-internals-1-of-3-architectural-concepts Architectural Concepts]
# [http://softwareas.com/tiddlywiki-internals-2-of-3-list-of-javascript-files List of Javascript Files]
# [http://softwareas.com/tiddlywiki-internals-3-of-3-key-javascript-classes-and-files Key Javascript Classes and Files]
{{Quote|1=[http://softwareas.com/tiddlywiki-internals-1-of-3-architectural-concepts Michael Mahemoff]|2=<pre>
(This is part 1 of a 3-part series. <a href="http://softwareas.com/tiddlywiki-internals-1-of-3-architectural-concepts">Part 1</a> introduces the internals and highlights some of the key patterns and concepts. <a href="http://softwareas.com/tiddlywiki-internals-2-of-3-list-of-javascript-files">Part 2</a> introduces each Javascript file. <a href="http://softwareas.com/tiddlywiki-internals-3-of-3-key-javascript-classes-and-files">Part 3</a> focuses on the internals of the more important classes and files.)
This is the first in a 3-part series on the internal design of Tiddlywiki. The series is more or less stream of consciousness - I'm a green Tiddlywiki developer, so I've been making these notes as I trawl through the source and learn it myself. Thanks to various people at Osmosoft for explaining some of this, and special thanks to <a href="http://jermolene.com/">Jeremy</a> for some overviews and reviewing the writing here, <a href="http://about.unamesa.org/Imtiaz">Saq</a> for a great overview on many feature, and <a href="http://fnd.lewcid.org/blog/">Fred</a> for reviewing the initially published version.
<h2>Overview</h2>
A Tiddlywiki is a collection of "tiddlers", small blocks of content typically a paragaph or so in length. At any time, a subset of these tiddlers is displayed in the UI (between zero and the total number of stored tiddlers).
A special property of Tiddlywiki is that the entire application resides in a single HTML/Javascript/CSS file (it's the quintessential SPA - <a href="code.google.com/p/trimpath/wiki/SinglePageApplications">Single-Page Application</a>). This is why you can save a Tiddlywiki locally and run it off a file:// URL and stick it on your iPod or novelty hamburger USB stick.
In the file, all the tiddlers are stored inside invisible DIVs, which are read on startup into a "TiddlyWiki" data structure. When you invoke the save feature, for example by hitting the "save changes" control, the invisible DIVs are refreshed with latest content from memory, and the entire file is written out to the hard drive.
TiddlyWiki is much more than a specialised wiki - due to its flexible architecture and the possibility of plugins, it is more like a platform. <a href="http://osmosoft.com/#Products">Examples of apps built on Tiddlywiki.</a>
<a href="http://www.tiddlywiki.org/wiki/TiddlyWeb">TiddlyWeb</a>, though not discussed specifically here, marks an important step in the future of TiddlyWiki development. It's a RESTful server of Tiddlers which would allow for great flexibility in the kinds of UIs you end up with, as well as allowing non-UI clients.
<h2>Anatomy of a Tiddlywiki</h2>
The image below shows an Tiddlywiki in editable mode. As for the UI, you can see it consists of a main area with two sidebars. The main area is a "Story" - a story is a sequence of visible tiddlers.
<a href="http://skitch.com/mahemoff/ubw2/tiddlywiki-a-reusable-non-linear-personal-web-notebook"><img style="width: 400px; height: 600px;" src="http://img.skitch.com/20080811-kkjw7cg9brkrigncj4epec43gx.jpg"></a>
A lot of this is configurable by changing special tiddlers. In particular, the tiddler called "PageTemplate" provides the overall structure, with references to other tiddlers, and "Stylesheet" the CSS styles.
<h2>Object-Oriented Concepts in Tiddlywiki</h2>
There are many ways to deal with classes, objects, and prototypes in Javascript - see <a href="http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742/ref=pd_sim_b_1/102-7069230-4404147">"Javascript: The Good Parts"</a> by Doug Crockford and <a href="http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X">"Pro Javascript Design Patterns"</a> by Ross Harmes and Dustin Diaz.
Tiddlywiki's OO relies on the constructor function pattern, where you create new objects using the new keyword.
[javascript]
var tiddler = new Tiddler();
[/javascript]
In Javascript, <tt>new Fn()</tt> will magically does a couple of things that let us use the familiar (from C++, Java, etc.) idiom above. It sparks the creation of a blank object, then it conducts a special execution of Fn() in which <tt>this</tt> is superfrajalistically tied to the new-born object. This leads us to an idiom which is called a "constructor function" because it is a function that is both called and implemented as if it were, for the most part, a constructor in OO languages like C++ and Java. The <tt>Tiddler</tt> constructor function is defined as follows:
[javascript]
function Tiddler(title)
{
this.title = title;
this.text = "";
...
return this;
}
[/javascript]
In addition, the new Tiddler has a number of standard Tiddler methods associated with it, so I can call them in typical OO fashion, e.g. <tt>tiddler.isTagged("happy")</tt>. The implementations refer to the specific instance using the <tt>this</tt> keyword. In Javascript, this can easily be achieved via prototypes. Therefore, subsequent to the constructor definition, we encounter in Tiddler.js a menagerie of method definitions like:
[javascript]
Tiddler.prototype.isTagged = function(tag)
{
return this.tags.indexOf(tag) != -1;
}
[/javascript]
All of the attributes above are public, but Tiddlywiki also uses closures to ensure some attributes are only available externally via declared methods. For example, the tiddlers of a Tiddlywiki is a declared as a local variable, so there's no direct reference to it outside the methods declared in the same scope.
[javascript]
function TiddlyWiki()
{
var tiddlers = {}; // Hashmap by name of tiddlers
this.tiddlersUpdated = false;
...
this.fetchTiddler = function(title) {
var t = tiddlers[title];
return t instanceof Tiddler ? t : null;
};
}
[/javascript]
The above methods will also be available on each instance created with <tt>new</tt>, just as with those declared using the prototype assignment. They are used in exactly the same way. The only difference is that all these functions are re-created with each new instance, so they will consume more memory. That's the price we pay for the encapsulation.
You will also find static methods present (i.e. global functions attached to a constructor purely for the sake of namespacing them). For example:
[javascript]
TiddlyWiki.isStandardField = function(name)
{
return TiddlyWiki.standardFieldAccess[name] != undefined;
}
[/javascript]
Typically, a class will be contained in a single, dedicated, Javascript file (within the source code from which a Tiddlywiki is built). However, the previous example was actually contained in TiddlerFields.js rather than Tiddlywiki.js, so it seems that class definitions may be distributed across multiple files in some limited cases.
And that's how Tiddlywiki handles basic OO.
You'll also see some parts of TiddlyWiki enhancing built-in Javascript types by extending their prototype - for example, BasicTypes.js endows all Arrays with a <tt>contains()</tt> method and Dates.js sticks a <tt>getAmPm()</tt> method onto each Date that's created. Number, Array, and Date receive a dozen or so new methods.
Last but not least, there's also a healthy dose of <a href="http://en.wikipedia.org/wiki/Inheritance_(computer_science)">inheritance</a> in Tiddlywiki. Javascript inheritance is a whole new can of worms. We see an example in AdaptorBase, which serves as the base class for server adaptor subclasses. AdaptorBase looks very normal, like Tiddler above. FileAdaptor, a subclass, looks like this:
[javascript]
function FileAdaptor() {
}
FileAdaptor.prototype = new AdaptorBase();
[/javascript]
Basically, Javascript has a concept of prototype chains. The assignment means that any instance of FileAdaptor will now have all methods present in a new instance of AdaptorBase. FileAdaptor goes on to define its own methods, using the standard prototype pattern. If so inclined, it can override AdaptorBase's methods by defining them on its own prototype method. (This is why we say "new AdaptorBase()" - if we had assigned FileAdaptor.prototype to AdaptorBase.prototype, anything we set on FileAdaptor would also be set on AdaptorBase.)
<h2>URL Arguments</h2>
Tiddlywiki uses the fragment identifier pattern (<a href="http://AjaxPatterns.org/Unique_URLs">described here</a>) to provide flexible loading strategies.
Normally, the "DefaultTiddlers" shadow tiddler is used to specify which tiddlers are shown on startup. However, this can be overridden via URL params. For example, use <a href="http://www.tiddlywiki.com/#Examples">http://www.tiddlywiki.com/#Examples</a> to load with just the Examples tiddler showing. Or, for multiple tiddlers, just separate with a space (%20 in URL-5peak) <a href="http://www.tiddlywiki.com/#Examples%20Plugins">http://www.tiddlywiki.com/#Examples%20Plugins</a>. (An interesting possibility would be for Tiddlywiki to keep updating the URL to ensure its sync'd with the state of the app, so you could bookmark it at any time to save that configuration.)
But maybe you don't want to manually list all the tiddlers - instead, you might want to show all tiddlers matching some criteria. Then you'd want an automated mechanism for auto-selecting those criteria (think iTunes Smart Playlist for dramatic effect.) This would make the URL shorter, easier to understand the true purpose of the configuation, and future-proof it against any changes to the set of tiddlers we're interested in.
In Tiddlywiki, that mechanism is achieved with a URL "filter" prefix. For example, show all tiddlers with "systemConfig" tag - <tt>http://tiddlywiki.com/#filter:[tag[systemConfig]]</tt>.
Other things you can do -
http://tiddlywiki.com/#newTiddler:tiddlername - create a new tiddler, specifying the name
The URL is modelled as a map, i.e. key-value pairs. In the case of <tt>http://www.tiddlywiki.com/#Examples%20Plugins</tt>, that's just an alias for the canonical map form, <tt>http://www.tiddlywiki.com/#open:Examples%20open:Plugins</tt>. All this is managed by the Paramifiers class.
*********************************************************************
*********************************************************************
*********************************************************************
(This is part 2 of a 3-part series. <a href="http://softwareas.com/tiddlywiki-internals-1-of-3-architectural-concepts">Part 1</a> introduces the internals and highlights some of the key patterns and concepts. <a href="http://softwareas.com/tiddlywiki-internals-2-of-3-list-of-javascript-files">Part 2</a> introduces each Javascript file. <a href="http://softwareas.com/tiddlywiki-internals-3-of-3-key-javascript-classes-and-files">Part 3</a> focuses on the internals of the more important classes and files.)
Continuing the series, below is a list of all core Javascript files, organised into functional groups.
<h3>Initialisation</h3>
<ul>
<li><strong>main.js</strong> Runs the initialisation sequence.</li>
<li><strong>Paramifiers.js</strong> Handles URL params.</li>
</ul>
<h3>Generic (Non-Animation)</h3>
<ul>
<li><strong>BasicTypes.js</strong> Augments built-in Javascript Number and Array.</li>
<li><strong>Crypto.js</strong> Crypto functions. (Tiddlers can generate fingerprints.)</li>
<li><strong>Dates.js</strong> Augments built-in Javascript Date class.</li>
<li><strong>Dom.js</strong> Supports DOM manipulation.</li>
<li><strong>FileSystem.js</strong> </li>
<li><strong>Strings.js</strong> Augments built-in Javascript Number and Array.</li>
<li><strong>Http.js</strong> Supports XmlHttpRequest based remoting.</li>
<li><strong>RGB.js</strong> CSS colour manipulation.</li>
</ul>
<h3>Generic (Specifically Animation)</h3>
See also <a href="http://ajaxpatterns.org/One-Second_Mutation#TiddlyWiki_2">(2005) TiddlyWiki animation write-up.</a>
<ul>
<li><strong>Animator.js</strong> Runs the dynamic flow of stepping through an animation, delegating to specific strategies.</li>
<li><strong>Morpher.js</strong> Morphing animation strategy. Cool - smoothly animates between two CSS styles.</li>
<li><strong>Scroller.js</strong> Scroller animation strategy. Scrolls window to show an element. (The way the page smoothly scrolls to show a tiddler when you click its link).</li>
<li><strong>Slider.js</strong> Slider animation strategy. Slides elements opening and closed (e.g. Closing tiddlers or the Options box on right sidebar.).</li>
<li><strong>Zoomer.js</strong> Zoomer animation strategy (the way a tiddler "jumps out" from its link).</li>
</ul>
<h3>Tiddlywiki-Specific Utilities</h3>
<ul>
<li><strong>FormatterHelpers.js</strong> Utilities specifically for Formatters.</li>
<li><strong>Refresh.js</strong> Mechanism for notifying and updating elements based on changes, e.g. if stylesheet shadow tiddler is updated.</li>
<li><strong>Utilities.js</strong> Miscellaneous TiddlyWiki-specific utility functions.</li>
</ul>
<h3>Data Structures</h3>
<ul>
<li><strong>Tiddler.js</strong> Data structure representing a tiddler, i.e. a block of text with a title.</li>
<li><strong>TiddlerFields.js</strong> Augments TiddlyWiki to manage tiddler fields.</li>
<li><strong>TiddlyWiki.js</strong> Data structure representing a collection of tiddlers.</li>
</ul>
<h3>Data Import/Export</h3>
<ul>
<li><strong>AdaptorBase.js</strong> Adaptors convert from various wiki formats (e.g. Mediawiki) to TiddlyWiki. This is the base class for Adaptors.</li>
<li><strong>FileAdaptor.js</strong> Subclass of AdaptorBase which reads the default/standard Tiddlywiki format.</li>
<li><strong>Import.js</strong> Macro to import tiddlers from another Tiddlywiki.</li>
<li><strong>LoaderSaver.js</strong> Converts between HTML and a list of tiddlers. (I think the main purpose is to get a clean HTML list of tiddlers.)</li>
<li><strong>Saving.js</strong> Saves the Tiddlywiki - main case is serialising everything to DOM elements and saving to local file system.</li>
<li><strong>SavingRSS.js</strong> Serves Tiddlywiki as RSS format (e.g. <a href="http://www.tiddlywiki.com/index.xml">TiddlyWiki.com RSS feed</a>) showing time-sorted list of recently updated tiddlers.</li>
<li><strong>Sync.js</strong> Syncs </li>
<li><strong>TW21Loader.js</strong> Standard implementation of LoaderBase (defined in LoaderSaver.js).</li>
<li><strong>TW21Saver.js</strong> Standard implementation of SaverBase (defined in LoaderSaver.js).</li>
</ul>
<h3>Strategies</h3>
This is a broad category of options and control-type functions. The control-type functions are here because they are designed using flexible mechanisms which make them easily overrideable by plugin developers.
<ul>
<li><strong>Config.js</strong> General Tiddlywiki config - controls capacities, names of shadow tiddlers, which options can be set, other stuff.</li>
<li><strong>Commands.js</strong> Handlers for menus and toolbar.</li>
<li><strong>Macros.js</strong> Defines built-in macros.</li>
<li><strong>Formatter.js</strong> Formatters are strategies for locating regexp patterns in the wiki text (wiki words, image URLs, etc.) and rendering them.</li>
<li><strong>Options.js</strong> Options are cookie-based preferences. The user can generally set them directly on the Tiddlywiki UI. This is in contrast to Config.js settings, which are fixed unless the uswer cares to dive into the source code.</li>
<li><strong>Wikifier.js</strong> </li>
</ul>
<h3>UI Elements</h3>
<ul>
<li><strong>Backstage.js</strong> The backstage space at the top of the page, with access to advanced features and acting as an escape route after over-enthusiastic bouts of customisation.</li>
<li><strong>ListView.js</strong> A table-like list, e.g. shows all options when you hit Backstage|Tweak.</li>
<li><strong>Manager.js</strong> Plugin manager (accessible from Backstage|Plugins)</li>
<li><strong>Messages.js</strong> Simple status notifications.</li>
<li><strong>NewTiddler.js</strong> Macro for a new tiddler, e.g. when user hits "New Tiddler" menu option, and also new journal.</li>
<li><strong>Popup.js</strong> Popup menu (e.g. when you click on the name of a tiddler in the list of shadow tiddlers).</li>
<li><strong>Search.js</strong> Search implementation - allows user to search for a term.</li>
<li><strong>Sparkline.js</strong> Generates CSS based <a href="en.wikipedia.org/wiki/Sparkline">sparklines</a> graphic.</li>
<li><strong>Story.js</strong> Manages the container of all visible tiddler UI elements.</li>
<li><strong>Tabs.js</strong> A UI element for handling tabs.</li>
<li><strong>Toolbar.js</strong> The toolbar shown in the top of a tiddler (with "close", "close others" etc controls - or "done"-"cancel"-"delete" if open).</li>
<li><strong>Wizard.js</strong> Multi-step wizard UI framework.</li>
</ul>
<h3>Miscellaneous</h3>
<ul>
<li><strong>Deprecated.js</strong> Deprecated functions.</li>
<li><strong>Guide.js</strong> A short readme file.</li>
<li><strong>Lingo.js</strong> internationalisation-localisation support - contains string keys and their English values.</li>
<li><strong>Upgrade.js</strong> Support for upgrading Tiddlywiki vgersion.</li>
<li><strong>Version.js</strong> Short file with info about this version of Tiddlywiki.</li>
</ul>
*******************************************************************
********************************************************************
(This is part 3 of a 3-part series. <a href="http://softwareas.com/tiddlywiki-internals-1-of-3-architectural-concepts">Part 1</a> introduces the internals and highlights some of the key patterns and concepts. <a href="http://softwareas.com/tiddlywiki-internals-2-of-3-list-of-javascript-files">Part 2</a> introduces each Javascript file. <a href="http://softwareas.com/tiddlywiki-internals-3-of-3-key-javascript-classes-and-files">Part 3</a> focuses on the internals of the more important classes and files.)
Concluding this series, below is a list of all core Javascript files, organised into functional groups.
<h2>main.js</h2>
main() is the function that runs onload.
Key functions:
* creates a new tiddlywiki data store (new TiddlyWiki()) - this is the collection of tiddlers users are exposed to. The store is populated using TiddlyWiki.prototype.loadFromDiv(), which loads all the tiddlers from the "storeArea" div, which is an invisible block on the page (and rendered back in nice - and visible - manner later on).
* creates a second TiddlyWiki data store to hold "shadow tiddlers" - these are "meta"/config tiddlers holding data such as CSS styling. Populated from invisible "shadowArea" div (which at compile time is defined in the Shadow/ directory).
* creates a new "Story div", a div which will show tiddlers to the user, and themes it according to config.options.txtTheme
* sets up Popup.onDocumentClick (removes popup menus when user clicks outside of the menu)
* sets up event propagation - certain tiddlers are notified when certain actions occur. The mappings are defined in refresh.js (e.g. {name: "StyleSheetLayout", notify: refreshStyles})
* sets up and renders backstage
* loads plugins (plugins are evidently supposed to set a global "plugin problem" value if a problem occurs)
General:
* calls several lifecycle event handlers as it loads - the wiki config can provide hook functions which run upon particular lifetime events
* benchmarks most of the above (the benchmarking was possibly a quick fix - relies on variables t1,t2...t10 -> this code could be optimised for conciseness using function wrappers, but maybe startup would be too slow that way).
* After initial setup ensures tiddlywiki data structures and other initialisation/config pieces are in place, it blats and shows the display with restart() and refreshDisplay().
<h2>Plugins</h2>
Tiddlywiki has a strong plugin architecture. Each plugin is included as a regular (non-shadow) tiddler, one that must be tagged "systemConfig". (For all intents and purposes, "systemConfig" is synonymous with "plugin".) There's an example shipping with the default tiddlywiki instance on tiddlywiki.com (and a more detailed example in the source code - association/plugins/SparklinePlugin/SparklinePlugin.js). (Also of interest, <a href="http://www.tiddlywiki.org/wiki/Plugin_specs#Template">the latest plugin template at the tiddlywiki.org wiki</a>.)
[html]
<div title="ExamplePlugin" modifier="JeremyRuston" created="200607271914" modified="200609212329" tags="systemConfig">
<pre>/***
|''Name:''|ExamplePlugin|
|''Description:''|To demonstrate how to write TiddlyWiki plugins|
|''Version:''|2.0.3|
|''Date:''|Sep 22, 2006|
|''Source:''|http://www.tiddlywiki.com/#ExamplePlugin|
|''Author:''|JeremyRuston (jeremy (at) osmosoft (dot) com)|
|''License:''|[[BSD open source license]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
***/
//{{{
// Uncomment the following line to see how the PluginManager deals with errors in plugins
// deliberateError();
// Log a message
pluginInfo.log.push("This is a test message from " + tiddler.title);
//}}}<\/pre>
</div>
[/html]
A plugin is essentially just a Javascript block which gets executed on page load. All the biosketch info is optional (although in some cases, it does effect processing, e.g. there is a check against the required TiddlyWiki version). "Just some Javascript" did you say? <a href="http://ejohn.org/blog/jquery-plugins-size-and-storage/">This post on JQuery plugins</a> by JQuery daddy John Resig is instructive. His point is that a plugin architecture needs explicit points for plugins to hook into - i.e. an API - and the existence of a plugin catalogue. Tiddlywiki doesn't have a plugin API <em>per se</em>, but is structured with plenty of extension points to naturally hook into. As for the catalogue, there's also a <a href="http://www.tiddlywiki.org/wiki/Plugins">plugin wiki area</a>, with a grander-scale plugin repo project in progress.
Incidentally, note that you don't have to register the Javascript block as you might do in some other frameworks (e.g. runOnInit(myPlugin); ). It executes automatically when plugins are loaded.
Okay, so about those plugin extension points. I'm still learning that. In the case of sparklines, the purpose is to create a new macro (e.g. <tt><<sparkline 100 200 300>></tt>), so it defines <tt>config.macros.sparkline.handler(place,macroName,params)</tt>, and its "output" is to populate the <tt>place</tt> element with sparkline content.
Another popular pasttime for plugin developers is szhushing the global Formatter object to shape how stuff gets rendered. e.g. if your formatter locates the built-in formatter named "heading", it could easily overwrite its handler method to MAKE ALL THE HEADINGS SHOUT AT UNSUSPECTING READERS.
To install a plugin, users usually use the Import dialog, accessible from Backstage. It's also possible to manually include plugins <a href="http://mnteractive.com/archive/how-to-install-a-tiddlywiki-plugin/">via cut-and-paste</a> into Tiddlywiki.
There's much more to be said about plugins. The bottom line is that Tiddlywiki's architecture lets you bend the core product into many things. (By "architecture", I refer to both the plugin mechanism and the flexible nature in which the overall architecture is structured.)
<h2>Tiddlers</h2>
Tiddlers are the atomic content blocks that make up a Tiddlywiki, typically about a paragraph in length. A Tiddler is simply a block of text, with extra info like a title, a list of tags, and timestamp data. There's also a <tt>fields</tt> hash where you could store any arbitrary properties. (This seems suitable for plugins, but the core also makes use of it, and I don't really get that. Even for plugins, why can't they just make new fields dynamically?)
<tt>Tiddler</tt> is a Javascript class, so you get a new instance with <tt>new Tiddler()</tt>. Internally, it uses a publish-subscribe mechanism, where a changed() method is called after any mutation. This basically ensures the <tt>links</tt> property is up to date, as <tt>links</tt> is a redundant (and presumably there for performance) collection of links inside the tiddler.
A Tiddler also has a collection of "slices", though the collection is managed by <tt>TiddlyWiki</tt> rather than <tt>Tiddler</tt>. (This relates to the fact that shadow tiddlers are mere text blocks - using Tiddlywiki to extract slices ensures shadow tiddlers can also be sliced up....and slices are a major feature of most shadow tiddlers, since they are config-related.)
There's a string->string map from name to slice. This is similar to the fields hash, insofar as it's a free-form map. In this case, though, it's something that can easily be changed by the user in real time, as the slice collection is sync'd to the tiddler content. For example: |''slicename:''|''some slice content''|. Slices allow for easily edited meta-data, e.g. a stylesheet tiddler can have a slice called "backgroundColour". Users then edit the backgroundColor slice content to set the background colour.
A Tiddler also has a set of notification handlers - this is also managed by TiddlyWiki rather than the Tiddlers themselves (again, this ensures the mechanism works for shadow tiddlers). These are listeners/observers that are notified each time tiddler is changed.
A file closely related to Tiddler is TiddlerFields.js. It actually alters the TiddlyWiki definition rather than the Tiddler definition, but in any event it deals with accessing the Tiddler's fields map.
<h2>Shadow Tiddlers</h2>
Shadow tiddlers are a particular type of tiddler. There's no separate "ShadowTiddler" class, but they are held in a separate store and treated in special ways. Indeed, shadow tiddlers aren't actually of class Tiddler (which is slightly confusing). They are simply a title-text pairing; the data structure is a map from title to text. In contrast, regular Tiddlers are mapped from title to Tiddler.
In particular, TiddlyWiki has a fallback mechanism when asked to return a tiddler - if the tiddler doesn't exist, it will attempt to revert to a shadow tiddler of the same name. Shadow tiddlers are immutable (unless you hack source code), whereas tiddlers are of course easily edited. You can override shasow tiddlers with regular tiddlers of the same name, but the original shadow still lurks (in a good way) in the background.
To see this, open an editable Tiddlywiki, choose a shadow tiddler from the right sidebar Contents menu (e.g. SiteUrl), edit it, and save it. Then re-open it to verify your changes were affected. Then delete it, and notice that it's still in the list of shadow tiddlers. When you open it yet again, you'll see it now contains the original content. (The shadow tiddler itself never changed.)
Shadow tiddlers are used for config stuff like stylesheets. The fail-safe mechanism ensures you can easily "restore factory defaults" at any time.
<h2>TiddlyWiki</h2>
A Tiddlywiki is essentially a hash of Tiddlers, keyed on their title. More precisely, it's a wrapper around this hash. Here's a (slightly refactored) look at the relevant code for managing tiddlers, which looks like any other hash wrapper:
[javascript]
function TiddlyWiki()
{
var tiddlers = {}; // Hashmap by name of tiddlers
...
this.clear = function() { tiddlers = {}; };
this.fetchTiddler = function(title) { return tiddlers[title]; };
this.deleteTiddler = function(title) { delete tiddlers[title]; };
this.addTiddler = function(tiddler) { tiddlers[tiddler.title] = tiddler; };
}
[/javascript]
There is also a set of similar methods which wrap around these to provide more intelligent behaviour. e.g. createTiddler() wraps addTiddler() to provide "Add or retrieve if exists" functionality. getTiddler() wraps fetchTiddler() to ensure null is returned if no such tiddler exists. removeTiddler() wraps deleteTiddler() to delete only if the tiddler exists, and also notifies the tiddler's listeners. Most other methods also do "general stuff" with the tiddlers hash. A lot of them also run operations on behalf of Tiddlers themselves (this is mostly so it can endow shadow tiddlers - which are just strings - with certain behaviour, as mentioned in the previous section.)
<h2>Story</h2>
Story is the sovereign UI element in TiddlyWiki - its the container of all visible Tiddlers which you'll usually see occupying the main, middle, column. Theoretically, there could be more than one Story instance on the page, but I'm told that there are some hard coding shenanigans that rule it out in the project's current state. (Specifically, direct references to the "story" instance that main.js creates.) So Story is a singleton class in practice.
One gotcha here with the nomenclature - a "tiddler" inside Story.js is conceptually a DOM element, whereas in most other places its a data structure. Obviously, the tiddler UI element is a rendering of the tiddler data structure. However, the implementation isn't entirely symmetrical because the data structure has a dedicated class (Tiddler), while the UI element doesn't; tiddler rendering is handled purely by the Story class. In one case (<tt>displayTiddler()</tt>), either form is valid as the "tiddler" argument, similar to $() functions that accept either the element or the ID (<tt>title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler</tt>.)
Story's key properties are a container ID, which points to the container DOM element, and an idPrefix, the prefix for all tiddler IDs. The container already exists on the page when a Story object is created to manage it.
[javascript]
function Story(containerId,idPrefix)
{
this.container = containerId;
this.idPrefix = idPrefix;
...
}
[/javascript]
Each tiddler's ID is simply <tt>idPrefix + title</tt>. You might expect an array of tiddler DOM elements, but Story doesn't need it, as it can use the DOM itself to keep track of them; the direct descendents of the Story container are the Tiddler elements. It simply uses DOM traversal techniques to iterate through all such elements, when it needs to. (There's a generic forEachTiddler function too; I could imagine there might be some value in other <a href="http://martinfowler.com/bliki/CollectionClosureMethod.html">collection closure methods</a>.)
Story contains the logic to display a tiddler. <tt>displayTiddler()</tt> decides if the tiddler is already being shown, and if not, creates a new child element with the tiddler content. It delegates to the animation engine for display.
There is also <tt>refreshTiddler()</tt> - the logic for rendering the tiddler - which is called from <tt>displayTiddler()</tt>. For flexibility, tiddlers are rendered using a template, a template which is generally contained in a shadow tiddler. There's a ViewTemplate shadow tiddler and an EditTemplate shadow tiddler - it depends on whether the tiddler is being edited.
Furthermore, there is the concept of themes, which means you can use different templates. This is handled by <tt>switchTheme()</tt>. An example of different templates is <a href="http://tiddlythemes.com/empties/TiddlyPedia.html#ViewTemplate%20EditTemplate">illustrated here in the TiddlyPedia theme</a>.
<hr>
And that concludes the three-part series. Thanks again to those who helped me gather this info (see credits in <a href="http://softwareas.com/tiddlywiki-internals-1-of-3-architectural-concepts">first article</a>). I've learned a lot about Tiddlywiki in writing it, but I still have a long way to go. There wil be more.
</pre>
}}
The <tt>isDirty</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns whether there are unsaved changes to it. This method expects no parameters.
See also: [[Dev:SetDirty|setDirty]]
The <tt>isReadOnly</tt> method of a [[Tiddler]] object returns whether the tiddler is eligible for editing. Currently this is a TiddlyWiki-wide setting, so all tiddlers will return the same value. If the user is viewing the TiddlyWiki over anything but a local file connection, then this returns true. This method takes no parameters.
The <tt>isShadowTiddler</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns whether a tiddler is shadowed. It takes one parameter, the title of the tiddler, and returns a boolean value.
The <tt>isTagged</tt> method of a [[Tiddler]] object returns whether the author has given the tiddler the passed tag.
{{WIP}}
suggestions for plugins taking advantage of jQuery, e.g. by implementing existing jQuery plugins in TiddlyWiki context
* sortable/filterable tables (cf. [http://tw.lewcid.org/#TableSortingPlugin TableSortingPlugin])
* table of contents (cf. [http://devpad.tiddlyspot.com/#TiddlerToCPlugin TiddlerToCPlugin], [http://devpad.tiddlyspot.com/#DcTableOfContentsPlugin DcTableOfContentsPlugin])
* logging console (cf. [[LoggingConsolePlugin]])
* accordion effects (cf. [http://tw.lewcid.org/#AccordionMenuPlugin AccordionMenuPlugin], [http://docs.jquery.com/UI/Accordion UI/Accordion])
{{DISPLAYTITLE:jQuery Plugins}}
JavaScript is a scripting language generally used for client-side web development.
{{Note|
Despite the name, JavaScript is essentially unrelated to the Java programming language.
}}
== Syntax ==
* statements
* assignments
* line termination
* blocks
== Variables ==
* types
** boolean
** integer, float
** string
** array
** object
* scope (local vs. global)
* <code>this</code>
== Functions ==
* arguments
* return values
== Flow Control ==
=== Conditionals ===
* <code>if ... else</code>
* <code>switch ... case</code>
* exceptions
=== Loops ===
* <code>for</code>
* <code>for ... in</code>
* <code>for each ... in</code>
== Document Object Model ==
object representation of hierarchical document structure (HTML/XML)
== Embedding and Executing JavaScript Code ==
* HTML <code>script</code> tags
* [[TiddlyWiki]] [[plugin]] [[tiddlers]]
* [http://www.billyreisinger.com/jash/ Jash] (bookmarklet)
* [http://getfirebug.com Firebug] console
== External Resources ==
* [http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference Core JavaScript 1.5 Reference]
* [http://www.selfhtml.org SelfHTML] (German / French)
JigglyWiki is the project name for an experiment into implementing much of the TiddlyWiki functionality with [http://jquery.com jQuery].
In the short term, there is a project to include the jQuery core into the core of TiddlyWiki and refactor the internals of many of the TiddlyWiki functions to both offset the increased file size and also reduce complexity in the code.
With jQuery included in the core of TiddlyWiki, it would be available to plugin authors.
In tandem with this effort to efficiently integrate jQuery into the core, there are also more radical experiments into how we might further TiddlyWiki with a more radical restructure such that it embraces approaches such as [http://en.wikipedia.org/wiki/Unobtrusive_JavaScript unobtrusive JavaScript], [http://en.wikipedia.org/wiki/Progressive_enhancement progressive enhancement] and various UI paradigms.
Some of this work may surface in a future version of TiddlyWiki.
Some examples of such experiments can be found here:
* [http://jigglywiki.com/examples/jigglywiki.0.0.3.html proof of concept v0.0.3] (simple demonstration of content) - this version shows the content initial approach and should elegantly degrade if rendered with JavaScript disabled
* [http://jigglywiki.com/examples/pagemap.0.1.html Pagemap example] - exploring the concept of vertical tabs to represent to various sections of the rendered page (requires Firefox or Safari)
{{WIP}}
== Reasoning ==
* a single plugin can provide multiple macros
* plugin documentation is different from macro documentation
* machine-readable format for macro documentation would offer a wide array of possibilities (automatic aggregation, contextual help etc.)
== Implementation ==
<pre>
config.macros.foo.doc = {
desc: "lorem ipsum",
usage: "<<foo [bar]>>",
params: [ // optional
{
desc: "lorem ipsum",
optional: true,
defaultValue: null
}, {
type: "string",
name: "foo",
desc: "lorem ipsum",
optional: true,
defaultValue: "bar"
}
],
examples: [ // optional
"<<foo>>",
"<<foo 'bar baz'>>"
]
};
</pre>
=== Examples ===
<pre>
config.macros.tabs.doc = {
desc: "creates a pane to display one of several tiddlers alternately, using a tabbed interface",
usage: "<<tabs cookieName label1 tooltip1 tiddler1 label2 tooltip2 tiddler2 [label3 ...]>>",
params: [
{
desc: "cookie name",
optional: false
}, {
desc: "label for first tab",
optional: false
}, {
desc: "tooltip for first tab",
optional: false
}, {
desc: "name of first tiddler",
optional: false
}, {
desc: "label for second tab",
optional: true
}, {
desc: "tooltip for second tab",
optional: true
}, {
desc: "name of second tiddler",
optional: true
}, {
desc: "name of third tiddler",
optional: true
}, {
desc: "...",
optional: true
}
],
examples: [
"<<tabs 'txtShadowTiddlers'\n\t'PageTemplate' 'page layout' [[PageTemplate]]\n\t'ViewTemplate' 'tiddler layout' [[ViewTemplate]]\n\t'StyleSheet' 'custom styling' [[StyleSheet]]\n>>"
]
};
config.macros.timeline.doc = {
desc: "creates a list of tiddlers sorted by date",
usage: "<<timeline [date] [length] [format]>>",
params: [
{
name: 0,
desc: "date to sort by (\"modified\" or \"created\")",
optional: true,
defaultValue: "modified"
}, {
name: 1,
desc: "maximum length (amount of tiddlers to show)",
optional: true,
defaultValue: "0 (all)"
}, {
name: 2,
desc: "[[date format|http://www.tiddlywiki.org/wiki/Timestamps]] to use",
optional: true,
defaultValue: "default timeline date format (\"DD MMM YYYY\")"
}
],
examples: [
"<<timeline>>",
"<<timeline 'created' '99' 'YYYY-0MM-0DD 0hh:0mm'>>"
]
};
config.macros.today.doc = {
desc: "displays the current date and time",
usage: "<<today [dateFormat]>>",
params: [
{
name: 0,
desc: "[[date format|http://www.tiddlywiki.org/wiki/Timestamps]] to use",
optional: true,
defaultValue: "default locale format"
}
],
examples: [
"<<today>>",
"<<today 'YYYY-0MM-0DD 0hh:0mm'>>"
]
};
config.macros.version.doc = {
desc: "displays the version number of the current TiddlyWiki document",
usage: "<<version>>",
examples: [
"<<version>>"
]
};
</pre>
{{Incomplete}}
== Unnamed Parameters ==
<pre>
<<foo value1 value2 value3>>
</pre>
With the example macro call above, the <code>params</code> variable passed to the macro's handler is an array, created by parsing the space-separated list of strings. For multi-word parameters, use <code>[[a param]]</code>.
Example:
<pre>
config.macros.myMacro.handler = function(place,macroName,params,...) {
if(params[0])
var length = params[0];
if(params[1])
var width = params[1];
...
</pre>
== Named Parameters ==
{{Quote|[http://trac.tiddlywiki.org/ticket/538#comment:4 Jeremy]|2=
The semantics of <code>parseParams()</code> are that either <code>defaultName</code> or <code>defaultValue</code> can be provided. If <code>defaultName</code> is provided, then any single token parameters are taken to be values, with the name being specified in <code>defaultName</code>. If instead <code>defaultValue</code> is defined, then any single token parameters are taken to be names, with the value being specified in <code>defaultValue</code>.
There is a potential error condition if neither <code>defaultName</code> or <code>defaultValue</code> is provided, but this only occurs if the parameter string in question includes any single token values. The current patch incorrectly treats a missing <code>defaultName</code> as an error.
}}
Code for parsing named parameters:
<pre>
config.macros.foo.handler = function(place, macroName, params, wikifier, paramString) {
var prms = paramString.parseParams(null, null, true);
var value = getParam(prms, "key");
};
</pre>
To be used like so:
<pre>
<<foo key:value>>
<<foo key:"value">>
<<foo key:[[value]]>>
</pre>
=== Default Names ===
The first (required) parameter of <code>parseParams()</code> will be used as the default name for unnamed parameters:
<pre>
p = paramString.parseParams("anon", null, true);
</pre>
Usage:
<pre>
<<foo value1 value2 value3>>
</pre>
In this example, ''anon'' is used as the default name when processing parameters.
=== Default Values ===
If you set the second parameter of <code>parseParams</code> but '''not''' the first, that will be taken as the default value for named parameters without values:
<pre>
p = paramString.parseParams(null, "val", true);
</pre>
Usage:
<pre>
<<foo p1:A p2:B p3: p4:D>>
</pre>
In this example, ''val'' is used as the default value when processing parameters.
A macro is a specialized form of TiddlyWiki plugin that is invoked in a particular tiddler. See [[Plugin Development]] for more information on developing them.
== Macro Execution ==
Each macro has a handler (<code>config.macros.*.handler</code>) as well as an optional initialization (<code>config.macros.*.init</code>) method.
The <code>init</code> function is executed on document startup and takes no arguments.
Macro handlers are invoked when the respective macro is being rendered and are passed the following arguments:
* '''place:''' the respective macro call's containing DOM object
* '''macroName:''' the respective macro's name
* '''params:''' parameters retrieved from the macro call, as an array
* '''wikifier:''' the wikifier object being used
* '''paramString:''' parameters retrieved from the macro call, as a string
* '''tiddler:''' the respective macro call's containing tiddler
== See Also ==
* [[Dev:Plugin Development]]
* [[Dev:Plugin Specifications]]
* [[Dev:Custom Macros]]
Mother of All Server Sides (aka [[TiddlyWeb]])
According to [[Jeremy Ruston]] must be spoken aloud with a thrusting of the chin, a lowering of the voice, and an imposing inflection.
{{Review}}
The latest TiddlyWiki development build can be obtained from http://nightly.tiddlywiki.org.
Nightly builds may have bugs or be unstable and therefore should not to be relied on for regular use. They are intended for testing purposes only.
[[Category:FAQ]]
The <tt>notify</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object runs all notification functions registered to a tiddler via [[Dev:AddNotification|addNotification]]. It takes two parameters:
* the title of the tiddler
* a boolean value signalling whether to include blanket notifications — that is, notifier functions that were registered via <tt>addNotification(null, function)</tt>
This is typically called as a matter of course by the TiddlyWiki code, but you may force a notification via this method.
The <tt>notifyAll</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object runs all notification function currently registered to it via [[Dev:AddNotification|addNotification]]. If a notification function was tied to a particular tiddler, it is invoked with that tiddler, as usual, and blanket notifications (registered to the null value) are run as well. This method takes no parameters.
This method is called by the TiddlyWiki code starts up as the page loads, but this is an easy way to force a reload of page title, subtitle, stylesheet, and so on.
{{Incomplete}}
rjbs has created a [http://use.perl.org/~rjbs/journal/37882 Perl script] for manipulating TiddlyWiki files.
== See Also ==
* [[Dev:r4tw]]
* [[Dev:PyTiddlyWiki]]
[[Category:Tools]]
The <tt>permaView</tt> method of the [[Story]] class changes the browser's address bar so that it is a permalink to all open tiddlers. This method takes no parameters.
{{Merge|[[Dev:Developing and Testing a Plugin]]}}
In TiddlyWiki, almost everything is a [[tiddler]] - including
plugin/macro code.
Plugins are marked as such by tagging them with "[[systemConfig]]".
Development-related questions are discussed on the [http://groups.google.com/group/TiddlyWikiDev TiddlyWiki Dev Group].
{{WIP}}
potential changes in the plugin API
== Goals ==
* provide explicit contract for plugins (rectifying possible legacy issues)
* allow jQuery-plugins to be used unchanged in TiddlyWiki
* maintain backwards-compatibility with existing plugins
== Guidelines ==
* generic functionality should use the jQuery namespace
* TiddlyWiki-specific functionality uses a dedicated sub-namespace:
** <code>$.tw</code> corresponds to pre-2.5 <code>config.extensions</code>
** <code>$.tw.fn</code> corresponds to pre-2.5 <code>config.macros</code>
<source lang="javascript">
(function($) { //# set up local scope
// plugin namespace
$.tw.myPlugin = {
myMethod: function() {
/* ... */
}
};
// macro
$.tw.fn.extend({
myMacro: function(args) {
var place = this[0]; // "this" is a jQuery object
var macroName = args.tw.macro;
var tiddler = args.tw.tiddler;
var wikifier = args.tw.wikifier;
/* ... */
return this;
}
});
})(jQuery); //# end of local scope
</source>
== Issues ==
* TiddlyWiki namespace; <code>tw</code> might be too short to avoid clashes - <code>tiddlywiki</code> might be a better choice
* differentiation between plugins and macros (<code>$.tw</code> vs. <code>$.tw.fn</code>) might be too subtle, thus warranting a separate sub-namespace for macros (e.g. <code>$.tw_macros</code>)
* unclear whether plugins should use the <code>extend</code> method when claiming their namespace (this might depend on the respective use case)
* macros have an <code>init</code> method - should this be mapped to a method of the macro object?
{{Note|1=
The [http://plugins.tiddlywiki.org TiddlyWiki Plugin Library] has now reached alpha stage.
Both [{{fullurl:{{FULLPAGENAME}}|oldid=5019}} previous] [{{fullurl:{{FULLPAGENAME}}|oldid=5380}} concepts] are available via the page history.
}}
== Goal ==
* central index of [[TiddlyWiki]] [[plugins]] for improved discoverability of third-party functionality
{{Note|1=
The term "Plugin ''Library''" is potentially misleading because the system harnesses the decentralized landscape of plugin repositories rather than acting as a central (re)distribution system.
A variety of alternative names [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/31595edc4522f769 have been suggested] - e.g. Plugin Index, Plugin Directory or Plugin Hub.
}}
== Aggregation ==
* periodical updates [''not yet implemented'']
* manually maintained [http://svn.tiddlywiki.org/Trunk/association/services/pluginLibrary/aggregator/repos.lst list of plugin authors' repositories]
** hosted TiddlyWiki documents
** Subversion repositories
* ''[[systemConfig]]'' tiddlers / <code>.js</code> files (<code>systemConfig</code>; actual execution can be prevented with <code>systemConfigDisable</code>)
* black- and whitelisting
** Subversion directories: [http://svn.tiddlywiki.org/Trunk/association/services/pluginLibrary/aggregator/test/foo/excludeLibrary.txt excludeLibrary.txt] or [http://svn.tiddlywiki.org/Trunk/association/services/pluginLibrary/aggregator/test/bar/includeLibrary.txt includeLibrary.txt] (supports [http://www.python.org/doc/2.5.2/lib/module-fnmatch.html wildcards])
** TiddlyWiki documents: blacklisting via tag "systemPrivate"
* [[#Community Features|user augmentation]]
* stored are tiddler fields, meta-slices, and documentation sections
== Metadata Evaluation ==
[TBD]
== Advanced Features ==
[''not yet implemented'']
* timeline of plugin releases and updates
* dependency notification
* one-click install for plugins
** client sends request via URL parameter (e.g. <code>import?foo&bar</code>)
** server fetches respective plugins from their original locations
** server returns plugins wrapped in pure-store format
* instant plugin demonstration
* popularity index (hits/usage counter)
== Community Features ==
[''not yet implemented'']
* commenting
* tagging
* rating
* trusted plugins
* suggestions for sites/plugins not yet indexed
{{Note|
'''N.B.:''' This page is now obsolete.
}}
__TOC__
== Retrieval ==
[[Image:PluginLibrary retrieval process.png]]
== Integration ==
[[Image:PluginLibrary integration process.png]]
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/ec7ebb5433ca3364 <nowiki>[twdev]</nowiki> licensing plugins]
This page provides guidelines for structuring [[plugin]] tiddlers.
A standardized, consistent structure simplifies automated aggregation (cf. [[Plugin Library]]), and also provides easy-to-follow guidelines for new TiddlyWiki developers.
Generally, a plugin consists of two basic sections: The metadata (i.e. description) and the actual JavaScript code.
== Metadata ==
The metadata section is divided into two parts:
The machine-readable meta fields and the documentation.
=== Meta-Slices ===
{{:{{NAMESPACE}}:{{PAGENAME}}/Meta-Slices}}
=== Documentation Sections ===
{{:{{NAMESPACE}}:{{PAGENAME}}/Documentation Sections}}
{{Note|
Extensive documentation content can be moved to a separate tiddler (e.g. ''<pluginName>Documentation'').
}}
== Code ==
Ideally the plugin's JavaScript code is presented as a [[Escaping#Code_Comments|preformatted block]]:
<pre>
//{{{
<...>
//}}}
</pre>
Alternatively, the source code can be hidden by wrapping it in the respective [[Escaping#Code_Comments|comment markers]]:
<pre>
///%
<...>
//%/
</pre>
== Template ==
{{:{{NAMESPACE}}:{{PAGENAME}}/Template}}
== See Also ==
* [[Dev:Best Practices]]
* [http://trac.tiddlywiki.org/browser/Trunk/contributors/MartinBudden/plugins/ExamplePlugin.js ExamplePlugin]
{| class="wikitable"
! Title
! Purpose
! Notes
|-
| '''Description'''
| detailed description
|
|-
| '''Notes'''
| general remarks (e.g. limitations, alternative implementations)
|
|-
| '''Usage'''
| usage instructions (e.g. syntax)
|
|-
| '''Parameters'''
| list of macro parameters (sequential or named)
| sub-section of Usage
|-
| '''Examples'''
| usage examples
| sub-section of Usage
|-
| '''Configuration'''
| plugin-specific settings
|
|-
| '''Revision History'''
| version history
| one sub-section per version
|-
| '''To Do'''
| further development tasks
|
|}
{| class="wikitable"
! Field
! Used By
! Description
! Notes
|-
| '''Name'''
| TiddlyWiki core
| plugin designation
| usually corresponds to the respective tiddler title
|-
| '''Description'''
| TiddlyWiki core
| concise description of the plugin's functionality
|
|-
| '''Icon'''
| [[Plugin Library]]
| URL to image identifying the author(s) and/or the plugin
| recommended image dimensions: 100x100 px
|-
| '''Author'''
| [[Plugin Library]]
| main developers' and/or maintainers' names
| multiple names separated by semicolon; can contain PrettyLinks (e.g. e-mail address)
|-
| '''Contributors'''
| convention only
| contributors' names
| multiple names separated by semicolon; can contain PrettyLinks (e.g. e-mail address)
|-
| '''Version'''
| convention only
| plugin version number
|
|-
| '''Date'''
| convention only
| latest release date
| ideally ISO 8601 format
|-
| '''Status'''
| [[Plugin Library]]
| development status (stability)
| supported values: //unknown//; @@experimental@@; @@beta@@; //obsolete//; stable
|-
| '''Source'''
| [[Plugin Library]]
| plugin origin (direct TiddlyWiki permalink)
| can contain PrettyLink
|-
| '''CodeRepository'''
| convention only
| developers' development repository
| can contain PrettyLink
|-
| '''Copyright'''
| convention only
| copyright holder
|
|-
| '''License'''
| convention only
| plugin license
| '''N.B.:''' if unspecified, regular copyright applies
|-
| '''CoreVersion'''
| TiddlyWiki core
| required TiddlyWiki core version
| can contain PrettyLink
|-
| '''Requires'''
| TiddlyWiki core
| dependencies; list of plugin names
| bracketed list
|-
| '''Overrides'''
| convention only
| core functions replaced by this plugin
|
|-
| '''Feedback'''
| convention only
| location to submit feedback
|
|-
| '''Documentation'''
| [[Plugin Library]]
| location of the plugin documentation (e.g. <code>##Usage</code> to reference the respective [[#Documentation Sections|documentation section]])
|
|-
| '''Demonstration'''
| [TBD]
| location of the plugin demonstration (e.g. <code>##Examples</code> to reference the respective [[#Documentation Sections|documentation section]])
|
|-
| '''Keywords'''
| [TBD]
| plugin categorization
| bracketed list
|}
The latest version of this [http://svn.tiddlywiki.org/Trunk/association/plugins/SamplePlugin.js SamplePlugin] is available in the TiddlyWiki Subversion repository.
<source lang="javascript">
/***
|''Name''|SamplePlugin|
|''Description''|<...>|
|''Icon''|<...>|
|''Author''|<...>|
|''Contributors''|<...>|
|''Version''|<...>|
|''Date''|<...>|
|''Status''|<//unknown//; @@experimental@@; @@beta@@; //obsolete//; stable>|
|''Source''|<...>|
|''CodeRepository''|<...>|
|''Copyright''|<...>|
|''License''|<...>|
|''CoreVersion''|<...>|
|''Requires''|<...>|
|''Overrides''|<...>|
|''Feedback''|<...>|
|''Documentation''|<...>|
|''Keywords''|<...>|
!Description
<...>
!Notes
<...>
!Usage
{{{
<<sampleMacro>>
}}}
!!Parameters
<...>
!!Examples
<<sampleMacro>>
!Configuration Options
<...>
!Revision History
!!v<#.#> (<yyyy-mm-dd>)
* <...>
!To Do
<...>
!Code
***/
//{{{
if(!version.extensions.SamplePlugin) { //# ensure that the plugin is only installed once
version.extensions.SamplePlugin = { installed: true };
if(!config.extensions) { config.extensions = {}; } //# obsolete from v2.4.2
config.extensions.SamplePlugin = {
sampleFunction: function() {
/* ... */
}
};
config.macros.SampleMacro = {
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
/* ... */
}
};
} //# end of "install only once"
//}}}
</source>
Some plugins require custom styling for certain elements (e.g when adding elements to the UI).
Ideally, the respective CSS rule sets should be inserted using the built-in style sheets mechanism.
This can be accomplished in two different ways:
# adding a shadow tiddler<br>This is especially useful when the user should be able to customize the appearance.
# adding the styles as an invisible style sheet<br>This is useful for basic styles that are essential for the plugin and should not be changed.
{{Note|
When using the second approach, [[slices]] cannot be used.
}}
== Examples ==
{{Note|In both examples, ''Foo'' represents the name of the plugin, ''bar'' is the ID of the respective element.}}
=== Shadow Style Sheet ===
Creating a new [[Dev:Shadow Tiddlers|shadow tiddler]] (useful for easy customization by users):
<pre>
config.shadowTiddlers.StyleSheetFoo = "/*{{{*/\n"
+ "#bar {\n"
+ "\tcolor: #f00;\n"
+ "}\n"
+ "/*}}}*/";
store.addNotification("StyleSheetFoo", refreshStyles);
</pre>
=== Invisible Style Sheet ===
Using [[Dev:SetStylesheet|setStylesheet]]:
<pre>
var styles = "#bar { color: #f00; }";
setStylesheet(styles, "StyleSheetFoo");
</pre>
== See Also ==
* [http://mptw-beta.tiddlyspot.com/#PackageAsShadowPlugin PackageAsShadowPlugin]: tool for creating shadow style sheets from regular CSS rule sets
Another way to include style sheet with a plugin is to use the following method at the bottom of your plugin:
config.shadowTiddlers.StyleSheetGoogleButton = store.getTiddlerText(tiddler.title + "##StyleSheet");
store.addNotification("StyleSheetGoogleButton", refreshStyles);
/***
!StyleSheet
Your CSS HERE
****/
* [[Getting started with custom macros]]
* [[Hijacking onClick Methods]]
* [[Developing and Testing a Plugin]]
== See Also ==
* [[Dev:Best Practices]]
* [[Dev:Plugin Specifications]]
[http://crackpod.bplaced.net/?page_id=125 pyTiddlyWiki] is a Python class for manipulating TiddlyWiki files.
== See Also ==
* [[Dev:r4tw]]
[[Category:Tools]]
{{DISPLAYTITLE:Dev:pyTiddlyWiki}}
[http://simonbaird.com/r4tw/ r4tw] is a collection of Ruby classes for manipulating [[TiddlyWiki]] files.
[http://rubyforge.org/projects/tiddlywikicp/ tiddlywiki_cp] is a Ruby gem based on ''r4tw'', providing a library and command-line interface to create tiddlers from files and vice versa.
== See Also ==
* [[Dev:PyTiddlyWiki]]
[[Category:Tools]]
{{DISPLAYTITLE:Dev:r4tw}}
{{Incomplete}}
The TiddlyWiki core code is stored and developed as a collection of individual files, which makes it more manageable.
A recipe file determines which code files, tiddlers and HTML components to put together - somewhat like a jigsaw puzzle.
[[Dev:Cook|Cook]] then reads the recipe file and generates a new TiddlyWiki document from it.
Recipes can reference one another, making it straightforward to create a multiple variants of TiddlyWiki at one time.
== Example ==
TiddlyWiki's [http://svn.tiddlywiki.org/Trunk/core/tiddlywiki.html.recipe default recipe file]:
<pre>
template: html/tiddlywiki.template.html
recipe: tiddlers/split.recipe
recipe: js/split.recipe
copy: java/TiddlySaver.jar
</pre>
== External Resources ==
* [http://trac.tiddlywiki.org/wiki/RecipeFiles Recipe Files]
== See Also ==
* [[Dev:Cook|Cook]]
* [[Dev:Ginsu|Ginsu]]
* [[Dev:Chef|Chef]]
[[Category:Developer Tools]]
The <tt>refreshAllTiddlers</tt> method of the [[Story]] class re-renders all tiddlers currently visible on the page. It does not take any parameters nor does it return any value. Tiddlers with unsaved changes are forced to be re-rendered.
The <tt>refreshTiddler</tt> method of the [[Story]] class re-renders a tiddler's source code on the page. It takes three parameters:
* the title of the tiddler to display
* the template to use for display. You should use either the constant <tt>DEFAULT_VIEW_TEMPLATE</tt> or <tt>DEFAULT_EDIT_TEMPLATE</tt> in most cases.
* a boolean representing whether to force re-rendering even if the tiddler and template has not changed, or if there are unsaved changes to the tiddler.
This method returns an DOM element reference to the tiddler on the page.
The global function <tt>refreshTiddlyLink</tt> refreshes the status of a link already on the page. You would want to do this to update an individual link to a tiddler if that tiddler has been either created or deleted. This function takes two parameters:
* a DOM element reference to the link on the page
* the title of the tiddler it references
This function does not return anything.
The <tt>removeChildren</tt> global function removes all child nodes from a DOM element. It takes one parameter, the element to remove children from, and returns no value.
The global function <tt>removeClass</tt> removes a CSS class from a DOM element. It takes two parameters:
* the DOM element
* the class to remove
The global function <tt>removeEvent</tt> removes an event listener from a DOM element. It takes three parameters:
* the element
* the event type to stop listening to
* the function to remove
The <tt>removeTiddler</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object removes a tiddler from the TiddlyWiki. It takes one parameter: the name of the tiddler to delete. It notifies functions set as listeners with [[TiddlyWiki.addNotification|addNotification]], sets the dirty flag, and returns silently if the tiddler does not exist.
The global function <tt>resolveTarget</tt> returns the real target of a DOM event. This function takes one parameter, the event in question, and returns the element.
The <tt>resumeNotifications</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object resumes issuing notifications after [[Dev:SuspendNotifications|suspendNotifications]] is called. If [[Dev:SuspendNotifications|suspendNotifications]] was called multiple times, this method must be called an equal number of times in order for notifications to occur. This method does not expect any parameters.
The <tt>reverseLookup</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array of [[Tiddler]] objects that match a certain criteria. It expects four parameters in order:
* the field to filter by. This can be any property of a Tiddler.
* the value of the field to filter by.
* a boolean value:
** ''true:'' return tiddlers that match the value passed
** ''false:'' return all tiddlers except those that match the value passed
* what field to sort results by.
If no matches were found, an empty array is returned.
A Java based implementation of javascript that can be useful for server-side Javascript use.
http://www.mozilla.org/rhino/
{{WIP}}
This page explains the rationale and plan for integrating [http://jquery.com jQuery] into [[TiddlyWiki]].
== History ==
TiddlyWiki includes big swathes of code that smooth out the differences between different browsers.
For example, the <code>doHttp</code> function neatly wraps up the browsers native <code>XMLHttpRequest</code>, hiding some of the peculiar incompatibilities.
Similarly, the <code>setStyleSheet</code> function that applies a CSS stylesheet contains separate code paths for different browsers.
Much of this code is completely generic, but because it's TiddlyWiki-specific it's also rather brittle.
In the time since TiddlyWiki was first published, we've seen the rise of several standardised JavaScript libraries that smooth out these browser quirks, and typically also include features to make the DOM easier and more efficient to work with.
Leading examples are jQuery, Dojo, Prototype and Mootools.
At various times in the past, the TiddlyWiki community has considered adopting one of these libraries to replace TiddlyWiki's homegrown code.
Now that these libraries have stabilised and become pretty much commoditised, we believe that it is appropriate to revisit the issue.
== Why Use a Standard JavaScript Library? ==
* allows us to reuse existing work on the intricacies of browser quirks (TiddlyWiki's own code has far fewer eyeballs on it)
* makes it easier for developers familiar with that library to work on TiddlyWiki
* allows us to benefit from more sophisticated libraries of UI widgets that come with each library
== Why Choose jQuery (and not Dojo/Prototype/Mootools etc.)? ==
* jQuery has a clear, disciplined scope (unlike say Dojo) which means it's stayed small and lean
* the jQuery chaining idiom has turned out to be cool and productive
* the jQuery community is booming, and has generated an impressive library of plugins and themes etc
== jQuery Integration Steps ==
* make jQuery available via the window.jq global variable
* replace calls in the core to certain TiddlyWiki DOM-related functions with the equivalent calls to jQuery<br>For example, find all calls to doHttp and replace them with ajax. equivalent from jQuery
* re-implement shorter versions of certain TiddlyWiki functions to utilise jQuery<br>For example, story.createTiddler does a bunch of DOM manipulation that would be better done with jQuery
* re-implement all event handlers in the core to use jQuery
* re-implement certain TiddlyWiki functions as jQuery plugins<br>For example, setStylesheet doesn't exist in jQuery, but could be packaged as a jQuery plugin for wider consumption/testing etc
* provide a mechanism for invoking compatible jQuery plugins directly from wikitext<br>For example, a new <<jQuery ...>> macro that allows any jQuery plugin to be invoked
== What Else? ==
It would also be extremely cool to copy the chaining paradigm from jQuery. Specifically, there might be a <code>window.tw</code> object such that:
<source lang="javascript">
tw("HelloThere", "MyTiddler") // returns a map of the two named tiddlers
tw({ tag: "systemConfig" }) // returns a map of tiddlers tagged "systemConfig"
</source>
And then there would be chaining functions like:
<source lang="javascript">
.display() // display a tiddler
.close() // close a tiddler if it is displayed
.meta({ modified: "13 Dec 2008", modifier: "Simon" }) // modify the metadata of all the matching tiddlers
</source>
Further possibilities:
* use custom events to avoid the need for [http://trac.tiddlywiki.org/ticket/484 hijacking core functions]
* [http://trac.tiddlywiki.org/ticket/946 improve] the plugin contract
* unify commands and macros
* etc.
== What about jQuery UI? ==
jQuery UI is a library of themeable UI components built on top of jQuery, including tabs, accordions, date pickers etc.
TiddlyWiki has its own implementation of some of these things at the moment, but it remains to be seen whether its usage is high enough to warrant the inclusion of jQuery UI in every instance of TiddlyWiki.
TiddlyWiki 2.5.0 will be a simplistic release without any core changes apart from the jQuery integration - the actual refactoring (along with the usual fixes/enhancements) will begin with release 2.5.1.
The <tt>saveAsRss</tt> method of a [[Tiddler]] object returns a string with XML suitable for use as part of an RSS feed. This method takes no parameters.
The global function <tt>saveChanges</tt> saves the TiddlyWiki to disk. This function takes a boolean value as its parameter; if it is true, then the TiddlyWiki is only saved if its dirty flag is set.
The global function <tt>saveOptionCookie</tt> saves user settings in config to a cookie. You can add your own settings if you like; see the config passage for details. This function takes no parameters and returns nothing.
The <tt>saveTiddler</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object is typically called in response to the author clicking the '''done''' link after editing a tiddler. This method takes six parameters: the old title of the tiddler, the new title of the tiddler, the tiddler's source code, the name of the author, the modification date, and the tags (as an array of strings). This feeds directly into [[Tiddler]]'s [[set]] method, and does not save changes to the document on disk.
The <tt>saveToDiv</tt> method of a [[Tiddler]] object returns HTML for saving to a file on disk. This doesn't actually do any saving in itself; it just returns HTML.
The <tt>search</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array of [[Tiddler]] objects whose source code contains a string. This method takes three parameters:
* a regular expression to search for
* the field to sort results by. This can be any property of a [[Tiddler]] object.
* a tag to exclude from search results.
If no tiddlers match the search, an empty array is returned.
A testing tool for web applications that runs directly in a remote controlled web browser.
http://www.openqa.org/selenium/
{{Review}}
{{Merge|[[Dev:ServerAdaptorMechanism]]}}
{{Note|
As of TiddlyWiki v2.4.1, there is a BaseAdaptor class which provides much of the basic functionality.
The current documentation has not been updated to reflect this yet.
}}
Server adaptors are designed to use a familiar model akin to a device driver, adapting a standardized interface to whatever is required by the underlying server: [http://en.wikipedia.org/wiki/Webdav WebDAV], ZiddlyWiki, [http://en.wikipedia.org/wiki/MediaWiki MediaWiki], [http://www.socialtext.com/ Socialtext] or HTML scraping.
Many server architectures are [http://en.wikipedia.org/wiki/REST REST] based, and TiddlyWiki server adaptors are particularly easy to create for common REST patterns. It is also possible to create TiddlyWiki server adaptors for servers with an [http://en.wikipedia.org/wiki/XML-RPC XML-RPC] interface.
Server adaptors are registered by name in the global object <code>config.adaptors</code>. Each entry is a reference to an object constructor for that type of server adaptor. The adaptor object must implement the following methods:
* [[Dev:Adaptor.openHost|Adaptor.openHost]]
* [[Dev:Adaptor.openWorkspace|Adaptor.openWorkspace]]
* [[Dev:Adaptor.getTiddler|Adaptor.getTiddler]]
* [[Dev:Adaptor.close|Adaptor.close]]
Additionally the adaptor may implement the following methods:
* [[Dev:Adaptor.getWorkspaceList|Adaptor.getWorkspaceList]] (required to support the Import Tiddlers UseCase)
* [[Dev:Adaptor.getTiddlerList|Adaptor.getTiddlerList]] (required to support the Import Tiddlers UseCase)
* [[Dev:Adaptor.putTiddler|Adaptor.putTiddler]] (required to support the Sync UseCase)
* [[Dev:Adaptor.getTiddlerRevision|Adaptor.getTiddlerRevision]]
* [[Dev:Adaptor.getTiddlerRevisionList|Adaptor.getTiddlerRevisionList]]
The adaptor object is used through the life of a connection to a server; see ServerAdaptorConcepts for details. Many of the methods use an AsynchronousPattern of callbacks to deliver their results.
Custom formatters for the WikifierFormatterMechanism are often used alongside server adaptors to allow TiddlyWiki to display content in the native format of a server.
== See Also ==
* [[Server Adaptor Mechanism]]
* [[Dev:Server Adaptor Mechanism/Adaptor Structure|Adaptor Structure]]
[[Category:Server Adaptors]]
{{Incomplete}}
When writing an adaptor for a specific server, the first step is to identify the respective HTTP interface. The adaptor then creates a mapping to translate the server's content elements to tiddlers.
Adaptors are usually derived from the built-in <code>AdaptorBase</code> class, extending it with the required server-specific functionality.
== Mechanism ==
Typically, adaptors work in three steps:
# ''adaptor interface'' is called, performing an HTTP request
# ''adaptor callback'' function is called, interpreting the HTTP response
# ''user callback'' is called, processing the result
'''Parameters'''
''Adaptor Interface''
* <code>context</code> object containing parameters to be passed to the adaptor callback function
* <code>userParams</code> object containing parameters to be passed to the user callback function (unaltered by the adaptor)
* <code>callback</code> user callback function
''Adaptor Callback''
* <code>status</code> HTTP status (<code>true</code> or <code>false</code>)
* <code>context</code>
* <code>responseText</code> HTTP response text
* <code>uri</code> request URI
* <code>xhr</code> XMLHttpRequest object
''User Callback''
* <code>context</code>
* <code>userParams</code>
== Typical Components ==
=== Adaptor Properties ===
* <code>prototype</code> (usually <code>new AdaptorBase()</code>)
* <code>serverType</code> (technical designation; must be lowercase)
* <code>serverLabel</code> (human-readable designation)
* <code>mimeType</code> (optional)
=== Generic Context Properties ===
''Adaptor Interface'' (passed from user)
* <code>host</code>
''Adaptor Callback'' (passed from adaptor interface)
* <code>callback</code>
* <code>userParams</code>
''User Callback'' (passed from adaptor callback)
* <code>status</code>
* <code>statusText</code>
* <code>httpStatus</code>
=== getWorkspaceList ===
Retrieve a list of workspaces available on the server.
Workspaces are objects with a single property, <code>title</code>.
For servers not supporting this, a single default workspace should be added to <code>context.workspaces</code>.
==== Context Properties ====
[''TBD'']
=== getTiddlerList ===
Retrieve a list of tiddlers available in the respective workspace.
==== Context Properties ====
''Adaptor Interface''
* <code>workspace</code>
''Adaptor Callback''
* [''TBD'']
''User Callback''
* <code>tiddlers</code>
=== getTiddler ===
Retrieve an individual tiddler.
==== Context Properties ====
''Adaptor Interface''
* <code>workspace</code>
''Adaptor Callback''
* <code>workspace</code>
* <code>title</code>
''User Callback''
* <code>tiddler</code>
==== Tiddler Properties ====
* <code>fields["server.type"]</code> (usually <code>adaptor.serverType</code>)
* <code>fields["server.host"]</code> (usually <code>AdaptorBase.minHostName(context.host)</code>)
* <code>fields["server.workspace"]</code> (optional; usually <code>context.workspace</code>)
=== putTiddler ===
Add or modify an individual tiddler on the server.
==== Context Properties ====
''Adaptor Interface''
* [''TBD'']
''Adaptor Callback''
* [''TBD'']
''User Callback''
* [''TBD'']
== Template ==
The latest version of this [http://svn.tiddlywiki.org/Trunk/association/adaptors/SampleAdaptor.js SampleAdaptor] is available in the TiddlyWiki Subversion repository.
<source lang="javascript">
/***
|''Name''|SampleAdaptor|
|''Description''|<...>|
|''Icon''|<...>|
|''Author''|<...>|
|''Contributors''|<...>|
|''Version''|<...>|
|''Date''|<...>|
|''Status''|<//unknown//; @@experimental@@; @@beta@@; //obsolete//; stable>|
|''Source''|<...>|
|''CodeRepository''|<...>|
|''Copyright''|<...>|
|''License''|<...>|
|''CoreVersion''|2.4.1|
|''Requires''|<...>|
|''Overrides''|<...>|
|''Feedback''|<...>|
|''Documentation''|<...>|
|''Keywords''|<...>|
!Code
***/
//{{{
if(!version.extensions.SampleAdaptor) { //# ensure that the plugin is only installed once
version.extensions.SampleAdaptor = { installed: true };
config.adaptors.sampletype = function() {};
(function(adaptor) { //# set up alias
adaptor.prototype = new AdaptorBase();
adaptor.serverType = "sampletype"; //# corresponds to config.adaptors entry
adaptor.serverLabel = "Sample Type";
adaptor.mimeType = "application/json";
// retrieve a list of workspaces
adaptor.prototype.getWorkspaceList = function(context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/workspaces";
var uri = uriTemplate.format([context.host]);
var req = httpReq("GET", uri, adaptor.getWorkspaceListCallback,
context, { accept: adaptor.mimeType });
return typeof req == "string" ? req : true;
};
adaptor.getWorkspaceListCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
context.workspaces = [];
/* ... */ // parse responseText, determining workspaces
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// retrieve a list of tiddlers
adaptor.prototype.getTiddlerList = function(context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/tiddlers";
var req = httpReq("GET", uri, adaptor.getTiddlerListCallback,
context, null, null, { accept: adaptor.mimeType });
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerListCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
context.tiddlers = [];
/* ... */ // parse responseText, determining tiddlers
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// retrieve an individual tiddler
adaptor.prototype.getTiddler = function(title, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = title;
context.tiddler = new Tiddler(title);
context.tiddler.fields = {
"server.type": adaptor.serverType,
"server.host": AdaptorBase.minHostName(context.host),
"server.workspace": context.workspace
};
var uriTemplate = "%0/tiddlers/%1";
var uri = uriTemplate.format([context.host, title]);
var req = httpReq("GET", uri, adaptor.getTiddlerCallback,
context, null, null, { accept: adaptor.mimeType });
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
var tiddler = new Tiddler();
/* ... */ // parse responseText, determining tiddler properties
tiddler.fields = merge(context.tiddler.fields, tiddler.fields, true);
context.tiddler = tiddler;
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
})(config.adaptors.sampletype); //# end of alias
} //# end of "install only once"
//}}}
</source>
With the [[Dev:ServerAdaptorMechanism|ServerAdaptorMechanism]], TiddlyWiki adopts a simple model for interacting with servers:
* ''Server adaptors'' are plugins that provide a standardised interface to a particular server architecture. As standard, TiddlyWiki provides the FileAdaptor for talking to static TiddlyWiki files but others are available for talking to popular wiki servers
* A ''host'' string identifies a particular server, usually by it's URL
* A ''workspace'' string identifies a particular compartment or storage area within a server, for instance the name of a wiki on a shared MediaWiki server
* A ''title'' identifies a tiddler within a particular workspace
Via the [[Dev:ServerAdaptorExtendedFields|ServerAdaptorExtendedFields]], particular tiddlers can have a connection to a particular server.
[[Category:Adaptors]]
The [[Dev:ServerAdaptorMechanism|ServerAdaptorMechanism]] uses extended fields to store additional data with tiddlers that are associated with a server host. The standard fields are listed below, but particular adaptors will often add their own custom fields for their own purposes.
{| border="1"
|-
!Field!!Description!!Status!!Example
|-
|server.type||The type of server adaptor associated with this tiddler||mandatory||eg "file", "socialtext"
|-
|server.host||The host server URL||mandatory||eg "http://www.socialtext.net/"
|-
|server.workspace||The workspace associated with this tiddler||optional||eg "stoss"
|-
|server.page.name||The full (human readable) name of the tiddler's page on the server||optional||eg "Socialtext Open Source Wiki"
|-
|server.page.id||The normalized form of the tiddler's page name on the server||optional||eg "socialtext_open_source_wiki"
|-
|server.page.revision||The revision of the page. Host dependent, but normally a number. Guaranteed to lexigraphically sort into chronological order||optional||eg "20070222050650"
|-
|wikiformat||The wikiformat of the tiddler||optional||eg "socialtext"
|}
See [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]] for the meanings of terms like host, workspace and adaptor.
[[Category:Adaptors]]
{{Merge|[[Dev:Server Adaptor Mechanism]]}}
TiddlyWiki's architecture for interacting with servers allows it to be plugged into a wide variety of servers. This is done through the addition of plugins containing custom server adaptors. Server adaptors are designed to allow for use cases like importing tiddlers, loading missing tiddlers on the fly and synchronising changes with a server. Separate macros and core extensions implement such features by accessing the facilities provided by server adaptors.
Server adaptors are designed to use a familiar model akin to a device driver, adapting a standardised interface to whatever is required by the underlying server: [http://en.wikipedia.org/wiki/Webdav WebDAV], ZiddlyWiki, [http://en.wikipedia.org/wiki/MediaWiki MediaWiki], [http://www.socialtext.com/ Socialtext] or HTML scraping.
Many server architectures are [http://en.wikipedia.org/wiki/REST REST] based, and TiddlyWiki server adaptors are particularly easy to create for common REST patterns. It is also possible to create TiddlyWiki server adaptors for severs with an [http://en.wikipedia.org/wiki/XML-RPC XML-RPC] interface.
Server adaptors are registered by name in the global object <code>config.adaptors</code>. Each entry is a reference to an object constructor for that type of server adaptor. The adaptor object must implement the following methods:
* [[Dev:Adaptor.openHost|Adaptor.openHost]]
* [[Dev:Adaptor.openWorkspace|Adaptor.openWorkspace]]
* [[Dev:Adaptor.getTiddler|Adaptor.getTiddler]]
* [[Dev:Adaptor.close|Adaptor.close]]
Additionally the adaptor may implement the following methods:
* [[Dev:Adaptor.getWorkspaceList|Adaptor.getWorkspaceList]] (required to support the Import Tiddlers UseCase)
* [[Dev:Adaptor.getTiddlerList|Adaptor.getTiddlerList]] (required to support the Import Tiddlers UseCase)
* [[Dev:Adaptor.putTiddler|Adaptor.putTiddler]] (required to support the Sync UseCase)
* [[Dev:Adaptor.getTiddlerRevision|Adaptor.getTiddlerRevision]]
* [[Dev:Adaptor.getTiddlerRevisionList|Adaptor.getTiddlerRevisionList]]
The adaptor object is used through the life of a connection to a server; see [[Dev:ServerAdaptorConcepts|ServerAdaptorConcepts]] for details. Many of the methods use an AsynchronousPattern of callbacks to deliver their results.
Custom formatters for the WikifierFormatterMechanism are often used alongside server adaptors to allow TiddlyWiki to display content in the native format of a server.
[[Category:Adaptors]]
The <tt>set</tt> method of a [[Tiddler]] object modifies the tiddler's properties. This is preferable to changing the tiddler's properties directly, as it does some recalculation in the background. This method takes seven parameters, all of them optional, but they must be specified in this order:
* <tt>title</tt>: the new title of the tiddler
* <tt>text</tt>: the source code of the tiddler
* <tt>modifier</tt>: the name of the author who last modified the tiddler
* <tt>modified</tt>: a Date object corresponding to the last time the tiddler was changed
* <tt>tags</tt>: an array of strings, each with a name of a tag applied to the tiddler
* <tt>created</tt>: a Date object corresponding to when the tiddler was created
* <tt>fields</tt>: an object containing extended fields in the tiddler, primarily used with TiddlyWiki.forEachField.
The <tt>setDirty</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object sets its dirty flag to the value passed.
The global function <tt>setStylesheet</tt> adds a custom stylesheet to the document. This replaces any stylesheet already set with the same DOM id with this function. There are two parameters to this function:
* a string containing CSS source code
* a DOM id to assign to the new stylesheet
This function does not return any value.
The <tt>setTiddlerTag</tt> method of the [[Story]] class modifies a tiddler's tags. It takes three parameters:
* the title of the tiddler to change
* the tag to add or remove
* an integer specifying what to do. -1 indicates to remove the tag, 1 indicates to add the tag, and 0 toggles the state of the tag.
This method does not return anything.
Adding a shadow tiddler by extending the <code>config.shadowTiddlers</code> object:
<pre>
config.shadowTiddlers.StyleSheetFooBar = "/*{{{*/\n"
+ "#foo .bar {\n"
+ "\tfont-weight: bold;\n"
+ "}\n"
+ "/*}}}*/";
store.addNotification("StyleSheetFooBar", refreshStyles);
</pre>
Note: This example adds a shadow tiddler containing CSS code and thus includes a [[http://www.tiddlywiki.org/wiki/Dev:AddNotification|store notification]].
== See Also ==
* [http://mptw-beta.tiddlyspot.com/#PackageAsShadowPlugin PackageAsShadowPlugin]
{{Incomplete}}
There are four basic stages in TiddlyWiki's startup process:
* <code>oninit</code>: when <code>main()</code> starts
* <code>onload</code>: after tiddlers (including [[shadow tiddlers]]) have been loaded
* <code>onconfig</code>: right after plugins are loaded
* <code>onstart</code>: when [[Dev:Restart|restarting]]
These are defined within the [[Dev:Paramifiers|paramifiers mechanism]].
The Story class governs what is visible to the user on the page, and is instantiated by the TiddlyWiki code when the page loads as the global object story.
== Properties ==
* <tt>container</tt>: the DOM id of the Element that tiddlers are being displayed in
* <tt>highlightRegExp</tt>: the current search string, if any
* <tt>idPrefix</tt>: the string prepended to the DOM ids of tiddlers being displayed
== Methods ==
* [[Dev:blurTiddler|blurTiddler]]: remove focus from a tiddler
* [[Dev:chooseTemplateForTiddler|chooseTemplateForTiddler]]: find the template name for a tiddler
* [[Dev:closeAllTiddlers|closeAllTiddlers]]: close all tiddlers on the page
* [[Dev:Story.CloseTiddler|closeTiddler]]: close a single tiddler
* [[Dev:Story.createTiddler|createTiddler]]: show a tiddler on the page
* [[Dev:displayTiddler|displayTiddler]]: animate showing a tiddler on the page
* [[Dev:displayTiddlers|displayTiddlers]]: display many tiddlers simultaneously
* [[Dev:findContainingTiddler|findContainingTiddler]]: find the tiddler containing an Element
* [[Dev:focusTiddler|focusTiddler]]: give a tiddler's edit field keyboard focus
* [[Dev:Story.forEachTiddler|forEachTiddler]]: execute a function on every visible tiddler
* [[Dev:gatherSaveFields|gatherSaveFields]]: gather all saveable edit fields from tiddlers on the page
* [[Dev:getTemplateForTiddler|getTemplateForTiddler]]: return template source code for a tiddler
* [[Dev:hasChanges|hasChanges]]: returns whether there are unsaved changes to a tiddler
* [[Dev:permaView|permaView]]: change the address bar to a permalink to all visible tiddlers
* [[Dev:refreshAllTiddlers|refreshAllTiddlers]]: re-display all tiddlers on the page
* [[Dev:refreshTiddler|refreshTiddler]]: re-display a tiddler
* [[Dev:Story.saveTiddler|saveTiddler]]: save user changes to a tiddler
* [[Dev:Story.scrubTiddler|scrubTiddler]]: remove all DOM ids from a tiddler on the page
* [[Dev:Story.search|search]]: start a user-visible search
* [[Dev:Story.setDirty|setDirty]]: set the dirty flag of a single tiddler
* [[Dev:setTiddlerTag|setTiddlerTag]]: specify tags for a tiddler
The <tt>closeTiddler</tt> method of the [[Story]] class closes a single tiddler from the page. It takes three parameters in order:
* the title of the tiddler
* a boolean value indicating whether to animate closing the tiddler
* a boolean value indicating whether to animate slowly. To see this in action, hold down the Shift, Option, or Alt key while clicking the '''close''' button for a tiddler.
This function does not return any value.
The <tt>createTiddler</tt> method of the [[Story]] class opens a tiddler on the page. If a tiddler by the requested name doesn't exist, it appears ready to edit. This method takes four parameters:
* the overall parent DOM element to display the tiddler inside.
* the DOM element inside the first parameter to display directly after. A null value places it at the end of the entire element.
* the title of the tiddler to display.
* the template to use to display the tiddler. It should be either the constant <tt>DEFAULT_VIEW_TEMPLATE</tt> or <tt>DEFAULT_EDIT_TEMPLATE</tt>.
This method returns the newly created DOM element.
The <tt>forEachTiddler</tt> method of the [[Story]] class executes a function on every tiddler on the page. It takes a single parameter, which is a reference to a function. This function is executed repeatedly and is passed the following two parameters:
* a reference to a Tiddler object
* a reference to the DOM element on the page for the tiddler
This method returns no value.
The <tt>saveTiddler</tt> method of the [[Story]] class saves changes made by the user to a tiddler. It takes two parameters:
* the title of the tiddler
* a boolean value indicating whether to consider this a minor update
Minor updates do not affect the modification date of a tiddler. This method returns the title of the tiddler; if it was changed by the user, this change is reflected here.
The <tt>scrubTiddler</tt> method of the [[Story]] class removes all DOM ids from a tiddler on the page. It takes one parameter, a reference to an DOM element on the page, and returns nothing.
The <tt>search</tt> method of the [[Story]] class starts a user-visible search. It's the exact equivalent to the user typing in a search query on the page, although this method does not change what the user typed into the search field. This method takes three parameters:
* the text to search for
* a boolean value indicating whether the search should be case-sensitive
* a boolean value indicating whether the search term should be treated as a regular expression
This method does not return anything. If no tiddlers matched the search, a message is displayed to the user.
The <tt>setDirty</tt> method of the [[Story]] class sets the dirty flag of a single tiddler, which indicates whether it contains unsaved changes. It takes two parameters:
* the title of the tiddler
* the boolean value to set the dirty flag to
This method does not return any value.
=== right(n) ===
Returns n right-most chars from a string.
Example:
"foo".right(1) // returns "0"
=== trim() ===
Trims whitespace from the front and end of a string
Example:
" foo ".trim() // returns "foo"
=== format(subStrings) ===
Very handy for macros and plugins. A little bit like sprintf in C. subStrings should be an array.
Examples:
alert("Today I feel %0 because %1".format(['happy','my cat likes me']));
var t = store.getTiddler("MyTiddler");
var template = "Tiddler '%0' is %1 chars long and was last modified by '%3'";
alert(template.format([t.title,t.text.length,t.modifier]);
=== escapeRegExp() ===
Escapes any special RegExp characters with that character preceded by a backslash
=== readBrackettedList() ===
TBC...
String.prototype.right = function(n)
String.prototype.trim = function()
String.prototype.unDash = function()
String.prototype.format = function(substrings)
String.prototype.escapeRegExp = function()
String.prototype.escapeLineBreaks = function()
String.prototype.unescapeLineBreaks = function()
String.prototype.htmlEncode = function()
String.prototype.htmlDecode = function()
String.prototype.toJSONString = function()
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
String.prototype.readMacroParams = function()
String.prototype.readBracketedList = function(unique)
String.prototype.getChunkRange = function(start,end)
String.prototype.replaceChunk = function(start,end,sub)
String.prototype.getChunk = function(start,end)
String.prototype.decodeHashMap = function()
String.prototype.startsWith = function(prefix)
The <tt>suspendNotifications</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object suspends notifications from being issued by the [[Dev:Notify|notify]] and [[Dev:NotifyAll|notifyAll]] methods. The [[Dev:ResumeNotifications|resumeNotifications]] method must be called in order to resume notifications.
This method takes no parameters. It may be called multiple times; [[Dev:ResumeNotifications|resumeNotifications]] must be called an equal number of times in order for notifications to be issued.
{{Dablink|working title}}
exploring a potential redesign of the TiddlyWiki user interface
== Issues ==
* rename tiddler toolbar's ''Jump'' button (ambiguous/unclear)
* change backstage button's label (very opaque to regular users)
* jump button as global element (rather than within the tiddler toolbar)
== Suggestions ==
* re-organize UI components, grouping thematically related elements
** "listings" (index, timeline, tags, missing, orphans, shadowed)
** options
** controls
* custom context menus (optional) for application-like look and feel
* console to complement the message area (cf. [http://twconsole.tiddlyspot.com proof of concept])
=== Page Layout ===
* [http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/b55011665c5e04d9/ overflow technique] ([http://cleanlayout.tiddlyspot.com sample]) for increased flexibility
** percentage widths for sidebars
* smaller header
* search bar in the top right of the header
* message area in the bottom right, or as transparent bar at the bottom of the screen (cf. [http://webrunnerlayout.tiddlyspot.com sample])
* option to choose between vertical MainMenu and horizontal TopMenu
* footer fixed to the bottom of the viewport
* horizontal control bar (cf. [http://www.hawksworx.com/journal/2007/08/30/richer-ui-experiences-in-tiddlywiki/ sample]) and/or overhauled sidebar ([http://hawksworx.com/playground/tiddlywiki_sidebar/ concepts])
* backstage button (<code>#backstageShow</code>) styled as an actual button
* gradient in menu bar (cf. [http://webrunnerlayout.tiddlyspot.com sample]); using [http://trac.tiddlywiki.org/ticket/466 enhanced gradient macro] ([http://www.mailstore.com/images/mailstore-home/screenshots/en_Import.jpg example])
=== Tiddler Layout ===
* unobtrusive border for tiddlers
* clear separation of tiddler header and contents (e.g. using a subtle horizontal line)
* tiddler subtitle positioned at the right edge (below the tiddler toolbar; [http://paste.css-standards.org/32557/view draft])
* increased contrast between active and inactive tiddler toolbars
* visually more discrete heading styles
* overflow setting for embedding large tables or images (<code>.tiddler .viewer { overflow: auto; }</code>)
== See Also ==
* [[Dev:Hot Issues]]
* [[User Interface#Interface Components|Interface Components]]
[[Category:Concepts]]
This page summarizes suggestions for a possible redesign of the basic TiddlyWiki layout (cf. [http://groups.google.com/group/TiddlyWikiDev/t/48b7d64307cc9dfa <nowiki>[twdev]</nowiki> TiddlyWiki v2.2b5 feedback: layout/style]).
In order not to alienate users, the new design should mostly consist of subtle changes while retaining the overall look and feel of the current theme.
== Page Layout ==
{| border="1" cellpadding="5" style="border-collapse: collapse;"
|-
! Status
! Description
! Discussion
|-
| {{StatusOK}}
| the ''#backstageShow'' button should look more like an actual button
| Jeremy has agreed to fix this for TW v2.2
|-
| {{StatusOK}}
| the search bar should be moved to the top right in the header
| Jeremy has agreed to this
|-
| {{StatusOK}}
| the header should be less tall
| Jeremy has agreed to this
|-
| {{StatusPending}}
| the ''#messageArea'' box should be moved to the bottom right
| an alternative design for the message area would be welcome
: one possibility would be a horizontal bar at the bottom, resembling the browser's status bar
on a related note, maybe the ''#messageArea'' box should automatically disappear after a few seconds
|-
| {{StatusPending}}
| it should be considered replacing the left-hand MainMenu with a horizontal TopMenu (possibly with dropdown sub-menus)
| this is a contentious issue
: a rather complex solution would be including both layouts in the core, with an option to switch the layout
: alternatively, shadow tiddlers (and styling) for both MainMenu and TopMenu could be provided with the core code (risk of bloating?)
|-
| {{StatusPending}}
| a footer (fixed to the bottom of the screen or page, whichever is taller) might be a nice addition
| Jeremy would very much like to have a footer, but maintaining cross-browser compatibility proved to be a problem so far
: another issue is what exactly to use this footer for
|-
| {{StatusOpen}}
| the sidebar could be replaced by a horizontal control bar (cf. [http://www.hawksworx.com/journal/2007/08/30/richer-ui-experiences-in-tiddlywiki/ sample])
| this issue has not been discussed yet
|-
| {{StatusOpen}}
| tabbed interface for open tiddlers (cf. [http://visualtw.ouvaton.org/VisualTW.html#TiddlersBarPlugin TiddlersBarPlugin])
| this issue has not been discussed yet
|}
== Tiddler Layout ==
{| border="1" cellpadding="5" style="border-collapse: collapse;"
|-
! Status
! Description
! Discussion
|-
| {{StatusPending}}
| tiddlers should have an unobtrusive border
| Jeremy has generally agreed; what's missing is a specific style for this
|-
| {{StatusPending}}
| the tiddler's header should be separated from its contents with a subtle line
| at least two people agree, no objections so far
|-
| {{StatusPending}}
| the subtitle should be moved (floated) to the right of the title, right below the tiddler toolbar
| no feedback so far
|-
| {{StatusPending}}
| the tiddler toolbar needs more contrast between its inactive and active state (as of TW v2.2b5)
| no feedback so far
|-
| {{StatusPending}}
| it should be considered moving the ''Jump'' button from the tiddler toolbar to a more "global" position
| an additional, global tiddler(s) toolbar would generally be welcome – but it would have to be fixed to the screen (''not'' to the page) to allow for quick navigation without scrolling to the top
: the buttons ''Jump'', ''Close All'' and ''PermaView'' could be moved to a newly-created floating toolbar at the top of the screen
|-
| {{StatusPending}}
| the tiddler toolbar's ''Jump'' button should be renamed, as "jump" is very ambiguous/unclear
| this is a contentious issue
: one possible label would be ''GoTo''
:: "Jump" might actually be a less-technical term than "GoTo" – plus it is also "a nice iconic single word"
|-
| {{StatusPending}}
| the heading styles all look too similar
| alternative suggestions are welcome
: one possibility would be using a "shaded" style for H1, underlined styles for H2 and H3, and simply bold (but gradually smaller) styles for H4-H6 – see [[{{TALKPAGENAME}}|Discussion Page]] for examples)
|}
== Related Issues ==
* the core needs to have proper support for switchable themes packed into a single tiddler
* it would be desirable to add a new higher-level customisation layer that allows users to make basic layout and colour changes interactively, without editing templates or CSS
* custom context (right-click) menus could enhance the application-like look and feel (though they must be optional)
See http://trac.tiddlywiki.org/
[[Category:Incomplete]]
The Tiddler class represents a single tiddler. Instances of this class are created as the user opens tiddlers; they are not deleted if an user closes the corresponding tiddler. Changing an instantiated object does not affect the tiddler as saved by the author; to make changes to the tiddler that will be saved the next time the TiddlyWiki is saved, call <tt>store.saveTiddler</tt>.
== Properties ==
Instead of setting these manually, use the [[Dev:set|set]] method.
* title: a string with the tiddler's name
* text: the source code of the tiddler
* modifier: a string with the creator of the tiddler's name
* modified: a Date object corresponding to when the tiddler was last changed
* links: an array of strings, one per link in the tiddler's source code
* tags: an array of strings, one per tag assigned to the tiddler
* created: a Date object corresponding to when the tiddler was created
* linksUpdated: a boolean corresponding to whether the links property is up-to-date
== Methods ==
* [[Dev:autoLinkWikiWords|autoLinkWikiWords]]: returns whether the tiddler should have WikiWords linked.
* [[Dev:changed|changed]]: a hook to be called after the Tiddler object has been modified.
* [[Dev:escapeLineBreaks|escapeLineBreaks]] and [[Dev:unescapeLineBreaks|unescapeLineBreaks]]: converts line breaks from textual and HTML representations.
* [[Dev:generateFingerprint|generateFingerprint]]: return a SHA-1 hash of the tiddler's text.
* [[Dev:getLinks|getLinks]]: returns an array of links contained in the tiddler. Should be used instead of accessing the links property directly.
* [[Dev:getSubtitle|getSubtitle]]: returns a subtitle with author name and modification date.
* [[Dev:Tiddler.getTags|getTags]]: returns a single string of the tiddler's tags.
* [[Dev:isTagged|isTagged]]: returns whether the tiddler has been assigned a specific tag.
* [[Dev:isReadOnly|isReadOnly]]: returns whether the tiddler can be edited.
* [[Dev:Tiddler.LoadFromDiv|loadFromDiv]]: instantiate a Tiddler from HTML.
* [[Dev:saveAsRss|saveAsRss]]: render XML for the tiddler for use in an RSS feed.
* [[Dev:saveToDiv|saveToDiv]]: render HTML for storing the tiddler.
* [[Dev:set|set]]: changes many properties of the tiddler. Should be used instead of changing tiddler properties like text directly. TW 2+
== Modifying Tiddler Contents ==
{{Quote|1=[http://groups.google.com/group/TiddlyWiki/browse_thread/thread/44edde8c58e99892/ Eric]|2=
assigning directly to the "tiddler.text" property of a retrieved
tiddler object *can* be tricky:
Normally, the TW core functions, <code>store.saveTiddler()</code> or
<code>tiddler.set()</code> (for low-level internal operations) are used to
change the value in the tiddler data, and these functions trigger side-
effect processing, such as refreshing the display or even auto-saving
the file in response to changes in tiddler content.
In addition, lots of plugins hijack these functions in order to add
system-level extensions that are triggered whenever a tiddler is
changed. Directly setting the value of <code>tiddler.text</code> completely
bypasses these functions, preventing all add-on ''and'' core side-effects
behaviors from being triggered.
}}
== Storage ==
Within TiddlyWiki's store area, each tiddler is recorded in the following format:
<pre>
<div title="tiddler title" modifier="author/editor" modified="YYYY0MM0DD0hh0mm" created="YYYY0MM0DD0hh0mm" tags="foo bar [[lorem ipsum]]">
<pre>
Body of the tiddler
</pre>
</div>
</pre>
The <tt>loadFromDiv</tt> method of a [[Tiddler]] object loads a newly-instantiated Tiddler with information from a div (presumably saved by the author). It takes two parameters: the first is a reference to the div itself. This must be an Element, not a string id. The second is the tiddler's title. This method returns nothing; it only changes values in the Tiddler itself.
The <tt>getTags</tt> method of a [[Tiddler]] object returns a string representing all tags that the author has assigned to it. Spaces separate tags, and double brackets indicate tags with spaces in them. This method takes no parameters.
The <tt>tiddlerExists</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns whether a tiddler exists in the TiddlyWiki. If a shadow tiddler exists for the title, it will return true. This method expects one parameter: the title of the tiddler.
This is an abstract project used as an umbrella to represent the overlapping needs of three active Osmosoft projects - TiddlyGeo, TiddlyDocs and TiddlyGuv.
To begin with, this is a working page used to figure out who is making progress on which bits.
== Suggested Features ==
Key:
* blank = not required
* x = required
* y = implemented
{| class="wikitable"
|-
! feature
! generic client
! Geo
! Docs
! Guv
! TiddlyWeb Notes
|-
| access control (permission assignment)
| x
| x
| y
| y
| built in (bags, extractors)
|-
| login
| x
| x
| y
| x
| built in (challengers), client side support minimal
|-
| section editing
|
|
|
| y
| client dependent
|-
| commenting
| y
| y
| y
| y
| client dependent
|-
| tiddler draft/published/etc status
|
| y
| y
| x
| client dependent
|-
| owner assignment for a tiddler (such as for a task)
|
|
| y
| x
| client dependent
|-
| user groups
|
| x
| x
| x
| built in (user roles)
|-
| bag/recipe management
| x
|
|
| x
| API support bag recipe edits, needs client support
|-
| revision control
| x
| x
| x
| x
| built in (for tiddlers, not for bags and recipes)
|-
| file uploading
|
| x
| x
|
| any type of content can be stored in a tiddler if PUT with the correct mime-type
|-
| filtered RSS feeds
|
| x
| x
| x
| part of the atom tiddlyweb plugin (any collection of tiddlers can have a feed, that is filtered)
|-
| static file publishing
|
| y
| x
|
| part of the twstatic tiddlyweb plugin
|-
| SMS/email pub/sub
|
|
| x
|
| a plugin could be created to do this
|-
| WYSIWYG editor
|
| y
| y
| x
| client dependent
|-
| PDF generation
|
|
| y
|
| can probably host Paul's code in some fashion, need more info
|-
| image handling (copy & paste)
|
|
|
| x
| client dependent
|-
| business diagrams
|
|
|
| x
| client dependent
|-
| spreadsheets
|
|
|
| x
| client dependent (see also wikicalc/socialcalc)
|}
== Notes ==
=== Access Control ===
permission assignment similar to *nix file permissions
=== Login ===
login should be TW macro that presenting a login form (Simon haz it)
=== Section Editing ===
section editing is to be implemented as tiddler transclusion
''not enough interest''
=== Commenting ===
Everyone has used Mike's [http://svn.tiddlywiki.org/Trunk/contributors/MichaelMahemoff/plugins/CommentsPlugin/ CommentsPlugin].
=== Tiddler Status ===
=== Tiddler Assignment ===
=== User Groups ===
=== Bag/Recipe Management ===
=== Revision Control ===
revision control includes listing individual revisions, but diff'ing is not absolutely necessary (although very desirable!)
=== File Uploading ===
=== Filtered RSS Feeds ===
Simon will look into filtered RSS feeds tomorrow (Tuesday 10th Feb '09)
=== Static File Publishing ===
Geo has implemented this using a combination of [[TiddlyTemplating]] and a custom plugin for [[TiddlyWeb]].
In addition to simply publishing static files, JRL has been experimenting with how to use TiddlyWeb as a full-on content templating system and has been working with a relatively simple TiddlyWeb plug-in to achieve this. As an example of what is trying to be achieved, now you could visit e.g. "/articles/anArticle" and the "anArticle" tiddler will be rendered through the "articles" template.
=== Pub/Sub ===
SMS/e-mail
''not enough interest''
=== WYSIWYG Editor ===
implemented using [http://visualtw.ouvaton.org/VisualTW.html#FCKeditorPlugin FCKEditor] from Pascal Collin
=== PDF Generation ===
''not enough interest''
=== Image Handling ===
(copy & paste)
''not enough interest''
=== Business Diagrams ===
''not enough interest''
=== Spreadsheets ===
''not enough interest''
== Overview ==
TiddlyWeb is a project to create a reference implementation of the mother of all [[server-side implementations]] ([[möäss]]) for [[TiddlyWiki]], particularly a reference web API for accessing TiddlyWiki content located on the internet.
== Demo ==
A demonstration site with a growing collection of documentation and plans is sometimes up at http://peermore.com:8080/recipes/AutoTiddlyWeb/tiddlers.wiki
That URL is a working alpha of the TiddlyWeb service, configured for autosave (meaning tiddlers are automatically saved back to the server when 'done' is clicked). Please visit there for the most up to date information on TiddlyWeb. Contribute questions, comments and additional documentation there.
== Goals ==
The goals of TiddlyWeb include:
* Readable code reference version
* Set of rigorous tests that establish a point of reference (the ideal tests would be usable against other implementations)
* Documented API
* GET and PUT 1 tiddler
* GET full TiddlyWiki -> acquire *.html document for writing to disk as offline tiddlywiki
* Implementation is plugin-based extension(s) to core code
* Strong attention to javascript/plugin security policy
* Authentication modularized
* Authorization modularized
* Improved performance of save to server
* Easy creation of dynamically constructed TiddlyWikis for use by multiple people
* Rigorous attention to the standards and practices of HTTP.
== Source, Distribution and Discussion ==
The source of TiddlyWeb is kept in the TiddlyWiki subversion repository at http://svn.tiddlywiki.org/Trunk/association/serversides/tiddlyweb/core
Regular python-egg tarballs are built and distributed from http://peermore.com/tiddlyweb/dist
Discussion about TiddlyWeb development can be found in the TiddlyWikiDev google group: http://groups.google.com/group/TiddlyWikiDev
* [[{{TALKPAGENAME}}]]
== Ongoing development ==
[[Dev:TiddlyWeb development]]
{{WIP}}
This page is used to track why people involved in [[TiddlyWeb]] development are interested and what they would like it to be able to do.
We'll also use this to indicate what we're building next and who's doing it.
== Needs ==
=== FND ===
Goal: provide a collaborative platform with TiddlyWiki as the front-end
missing client-side components:
* renaming (adaptor)
* diff'ing
* unit tests (esp. adaptor)
=== Jonathan Lister ===
I would like a hosted TiddlyWeb as easy to use as [[Tiddlyspot]].
I have heard that client-side diff'ing is interesting to the other people here and would like that too.
=== General Needs ===
* Bag and Recipe editing plugins (client and server (?) side).
* Client side login handling (see http://svn.tiddlywiki.org/Trunk/association/serversides/tiddlyweb/client/plugins/TiddlyWebLoginPlugin.js )
* Client side tools for managing recipe and bag defaults and editing current fields.
* (forgetting something)
== Potential New Features ==
* TiddlyWeb hosting
* one-click provisioning of new TiddlyWeb instances
* interface for editing permissions on a TiddlyWeb
* client-side diff'ing
This is a guide for setting up TiddlyWeb and the accompanying server.
original source: [http://svn.tiddlywiki.org/Trunk/contributors/ChrisDent/experimental/TiddlyWeb/COOKBOOK TiddlyWiki Subversion repository]
<pre>
Version 0.1 A draft. Improvement suggestions most welcome.
This document attempts to provide a cookbook for people
who wish to start up a TiddlyWeb server with their own
content. These instructions assume you've read README
and RUN and been able to get a test server with test
content operating correctly. If you've not yet done that,
please read those files first and confirm that your system
will run TiddlyWeb.
Once you have a running TiddlyWeb, we're going to shut it
down, remove its data store, create a new store, put data
in it and then start up a new server. These instructions
assume you are working from a unix-like shell. Where you see
'$' starting a line, that's a line for you to type into the
shell. Other shells like the Windows command line will work
similarly, perhaps with slight changes to the syntax. Before
each block of $ lines will be an explanation of what you're
about to do. Each block is followed by what should have happened.
When you are to fill in a piece of information yourself, it is
bracketed like this:
$ mkdir <directory name>
That means you should type 'mkdir', a space, the name of the
directory you are creating and then press return.
Let's begin.
1. Get Where You Need to Be
The first step is to locate yourself in the directory
where you find this COOKBOOK file. It should also contain
a file called 'manager'. We'll be using this file a lot in
the next steps. If you run it without any arguments you should
get a list of possible commands. Try it:
$ ./manager
You should see something like this:
adduser Add or update a user to the database: <username> <password>
bag Create or update a bag with the json text on stdin: <bag name>
help List this help
imwiki Import a Tiddlywiki html file into a bag: <filename> <bag name>
recipe Create or update a recipe with the recipe text on stdin: <recipe name>
server Start the server: <hostname or ip number> <port>
tiddler Import a single tiddler into an existing bag from stdin: <tiddler_name> <bag name>
If you get something else that looks like an error try running
this instead:
$ python manager
If that does work, in the instructions below wherever you see
'./manager' use 'python manager' instead. If it doesn't work
then you are in an unsual situation. First make sure the stuff
in README and RUN checks out okay. If not, contact TiddlyWikiDev
(http://groups.google.com/group/TiddlyWikiDev/) for assistance.
2. Create Your Base Store
By default, TiddlyWeb uses a text based storage system with its
root in a directory named 'store' in the same directory from which
the server starts. That's the directory we are in right now.
First we need to remove any remains from the store used in earlier
experiments:
$ rm -r store
We remove the store directory itself, not just its contents, because in
some cases store may be symlink.
Then we need to remake the store directory (now empty) and three
required directories within:
$ mkdir store
$ mkdir store/bags
$ mkdir store/recipes
$ mkdir store/users
3. Create Your First Bag
>>> [FND] basic concepts; to be explained elsewhere?
TiddlyWeb tiddler content is stored in bags. Each bag gets a name.
If you have two tiddlers with the same title, but in two differently
named bags, they are two different tiddlers. In TiddlyWeb when we
refer to a tiddler, we refer to it using the combination of its
bag's name and its tiddler title.
To put content into a TiddlyWeb server, we first have to create a bag.
Bags have permission structures which can be set. For now we will ignore
those and use the default, which is that everyone can read or write
content from and to a bag.
The first bag we'll create is one that contains the two plugins that
make TiddlyWeb automatically synchronize content back to the server
after it has been edited in a TiddlyWiki. We'll call this bag
'TiddlyWeb'. Where the below shows ^D, type ctrl-d.
$ ./manager bag TiddlyWeb
^D
If you get no response it worked.
Now we'll add two tiddlers that are included with the distribution
to the bag. These are located in the lib directory. They are
specially formatted for use with the TiddlyWeb text store.
$ ./manager tiddler TiddlyWebAdaptorPlugin TiddlyWeb < lib/TiddlyWebAdaptorPlugin
$ ./manager tiddler TiddlyWebAutoSavePlugin TiddlyWeb < lib/TiddlyWebAutoSavePlugin
This stores the two named plugins into the TiddlyWeb bag.
4. Import A TiddlyWiki
Now, to bootstrap the collection of content on your TiddlyWeb server,
we're going to import one or more existing TiddlyWiki files into the
server, making a bag for each one. The first thing you need to do is
locate the TiddlyWiki HTML files and note their path on the filesystem.
If they are not on your system, download them using whatever method you
prefer (your browser, wget, curl, lwp_request, whatever it takes).
Create a bag for each TiddlyWiki you wish to import, replace <bag name>
with a unique name for each TiddlyWiki. You can use spaces if you want.
Remember which name goes with which TiddlyWiki.
$ ./manager bag <bag name>
^D
Once you have created the bags, import the wikis. The following
command will import one wiki into one bag.
$ ./manager imwiki <path to wiki file> <bag name>
If there are no errors from this, the tiddlers were imported. You can
confirm this by doing a directory list of the bag:
$ ls store/bags/<bag name>/tiddlers
If your system has the 'ls' command that should return a list of the
tiddlers that were in the TiddlyWiki you have imported.
5. Create A Recipe
A recipe is a way to generate a collection of tiddlers by using tiddlers
from multiple bags. The recipe itself is a list of bags with optional
filter instructions. A filter selects only some of the tiddlers in a
bag, or sorts them. If there is no filter, all the tiddlers from the
bag are included. If there are tiddlers with duplicate names, the tiddler
from the last bag to have it is the one that is included in the collection
(the recipes are processed in order).
Your recipe gets a name. This name will be used in URLs that you publish,
so make it meaningful to you.
For this cookbook we'll ignore filters for now, and create a simple
recipe that allows the content you imported from one of your TiddlyWikis
to be presented through TiddlyWeb and be edited. For this you'll need
the name of the bag in which the tiddlers you want can be found. Use
that name in the instructions below. The two lines that follow the line
beginning with $ are lines for you to type (substituting the bag's name
for <bag name>). Make sure you type them correctly:
$ ./manager recipe <recipe name>
/bags/TiddlyWeb/tiddlers
/bags/<bag name>/tiddlers
^D
If there are no errors, you should now have a recipe. It's time to
start the server.
6. Start The Server
TiddlyWeb comes with its own built in web server. It can be run with
any sever you like, with some scripting, but for these current purposes
the built in server should work fine. You need to decide the hostname
or ip number and port on which your server is going to run. If you're just
experimenting on your local machine, for now you can use 0.0.0.0 for the
ip number and 8080 for the port number. If you are running TiddlyWeb on
a remote server you need to choose the hostname of that server, and an
available port.
Once you have figured these bits out you can start the server:
$ ./manager server <ip number> <port>
You should see 'Starting CherryPy' and the $ prompt will not come
back. It's time to go to your web browser. For the examples below
we'll use 0.0.0.0 and 8080 for ip number and port. Replace these
with the information you chose. Go to:
http://0.0.0.0:8080/
You should see a list containing 'recipes' and 'bags'. Click
around and see what you can discover. Going to
http://0.0.0.0:8080/recipes/<recipe name>/tiddlers
will give you a list (in HTML) of tiddlers in the recipe.
http://0.0.0.0:8080/recipes/<recipe name>/tiddlers.txt
will get you the same list as text.
http://0.0.0.0:8080/recipes/<recipe name>/tiddlers.wiki
will get you a TiddlyWiki containing the tiddlers. Editing
one of those tiddlers should result in the tiddler being saved
back to the server. You can look at a tiddler from the recipe
on the server, as HTML, by going to:
http://0.0.0.0:8080/recipes/<recipe name>/tiddlers/<tiddler title>
If you have wikklytext installed, the wikitext will be rendered to
HTML, with clickable links. If you are not using wikklytext, you'll
see plain text.
7. What Next?
Now you have a working TiddlyWeb server. You can stop and start it
whenever you like. If you want to start it and keep a record of the log
you may, depending on operating system and shell choices, do something
like this:
$ ./manager server <ip number> <port> &> server.log &
If you encounter difficulties using TiddlyWeb contact the TiddlyWikiDev
Google Group, or contact the first author Chris Dent, <cdent@peermore.com>.
You can create bags and recipes as you need them for whatever functionality
you might need. With filters it is possible to create complex recipes.
TiddlyWeb is highly extensible, with a slowly growing collection of
plugins for using different storage mechanism and outputting different
types of a content. For example TiddlyWeb can now run on Google's app
engine service and it can provide Atom feeds of lists of tiddlers.
TiddlyWeb is built as a REST API so can be flexibly accessed by a variety
of clients: a web browser, TiddlyWiki, script languages, other web
servers. By looking through the code and experimenting with the URLs
given to the server you'll see that TiddlyWeb can do far more than
explained in this document. Have fun with it.
8. What's Been Left Out?
This document makes no effort to explain some concepts in TiddlyWeb
which may be important to some users. Please look elsewhere in
the distribution and on the web for additional documentation. Things
not covered include:
* Filters on bags and recipes, both in recipes and in URLs. Any
list of tiddlers may be filtered.
* User creation, authentication and authorizatin. TiddlyWeb has
a flexible user handling system. By default no user is required
and no restrictions are presented.
* TiddlyWeb is a Python WSGI application, and as such can be run
under many different web servers without much issue.
* TiddlyWeb has a flexible storage system that makes it possible to
create code which allows tiddlers, bags and recipes to be stored
in many different systems (on disk, in a database, in Google's
data system, in Amazon's S3, whatever you can think of).
</pre>
TiddlyWeb has built-in support for third-party plugins.
== Creating Plugins ==
Creating TiddlyWeb plugins is a simple matter of writing a Python module which contains an <code>init</code> function. This function is executed when the plugin is loaded on startup, and is passed the <code>config</code> dictionary as its sole argument:
<source lang="python">
def init(config):
pass # ...
# ...
</source>
The <code>init</code> function may do anything (or nothing, using <code>pass</code>).
Since the plugin code is being imported, the plugin module itself can import code from elsewhere in the system, extending and modifying the code as needed.
For example, methods or variables in other packages can be overridden or hijacked as necessary to change behavior. This might not be the best way to apply customizations though; sometimes changing the WSGI stack or adjusting the storage interfaces and serializations is a better option.
=== Example: URL Mapping ===
The following example extends the URL mapping used to dispatch URLs to methods, adding the URI <code>/hello/<name></code> to return a simple HTML page for <code>GET</code> requests to that URL:
<source lang="python">
def hello(environ, start_response):
name = environ['wsgiorg.routing_args'][1]['name']
start_response('200 OK', [('Content-Type', 'text/html')])
return "<html><head><title>Hello</title></head><body><h1>Hello %s</h1></body></html>" % name
def init(config):
config['selector'].add('/hello/{name}', GET=hello)
</source>
== Activating Plugins ==
Plugin modules are registered in the [[Dev:TiddlyWeb/configuration|configuration file]] (<code>tiddlywebconfig.py</code>):
<source lang="python">
config = {
"system_plugins": ["FooPlugin", "BarPlugin"]
}
</source>
The respective modules must be accessible via Python's <code>sys.path</code>.
For example, they could reside directly in the current working directory (the directory from where TiddlyWeb is launched). Alternatively, a sub-directory can be created (e.g. <code>TiddlyWeb/extensions/</code>) - this must then contain an <code>__init__.py</code> module (which can just be an empty file). (Note: There might be a [http://trac.tiddlywiki.org/ticket/785 default location] for plugins eventually.)
At http://trac.tiddlywiki.org/browser/Trunk/association/serversides/tiddlyweb/core there is a developing version of [[TiddlyWeb]].
The concepts and entities used in TiddlyWeb are described in a TiddlyWeb server running at http://peermore.com:8080/recipes/TiddlyWeb/tiddlers/TiddlyWeb
== Try It ==
If you are feeling curious you can try the following if you have python 2.5 or 2.4:
* ''svn co http://svn.tiddlywiki.org/Trunk/association/serversides/tiddlyweb/core''
* ''cd TiddlyWeb''
* read README, docs/RUN and docs/COOKBOOK
The TiddlyWiki class represents an entire TiddlyWiki. It is normally instantiated once, when the page is first loaded, into the global object <tt>store</tt>. It has one notable property, <tt>dirty</tt>, which is a boolean representing whether the TiddlyWiki contains unsaved changes.
== Methods ==
* [[Dev:addNotification|addNotification]]: hooks a function to changes in a tiddler
* [[Dev:clear|clear]]: empties the TiddlyWiki
* [[Dev:createTiddler|createTiddler]]: instantiates a new tiddler
* [[Dev:removeTiddler|removeTiddler]]: removes an existing tiddler
* [[Dev:fetchTiddler|fetchTiddler]]: retrieves a Tiddler object
* [[Dev:filterTiddlers|filterTiddlers]]: retrieve a filtered list of tiddlers
* [[Dev:forEachTiddler|forEachTiddler]]: executes a function on each tiddler
* [[Dev:getMissingLinks|getMissingLinks]]: retrieves a list of missing links
* [[Dev:getOrphans|getOrphans]]: retrieves a list of orphaned tiddlers
* [[Dev:getRecursiveTiddlerText|getRecursiveTiddlerText]]: retrieves tiddler source code, replacing links with source code
* [[Dev:getReferringTiddlers|getReferringTiddlers]]: finds tiddlers that refer to a particular tiddler
* [[Dev:getShadowed|getShadowed]]: returns an array of all shadow tiddlers available
* [[Dev:TiddlyWiki.GetTags|getTags]]: retrieves a list of all tags used in the TiddlyWiki
* [[Dev:getTiddlerText|getTiddlerText]]: returns the source code of a tiddler
* [[Dev:getTiddlers|getTiddlers]]: returns all tiddlers sorted by a criteria
* [[Dev:isDirty|isDirty]]: returns whether there are unsaved changes to the TiddlyWiki
* [[Dev:isShadowTiddler|isShadowTiddler]]: returns whether a tiddler is shadowed
* [[Dev:loadFromDiv|loadFromDiv]]: initializes a TiddlyWiki from saved HTML
* [[Dev:notify|notify]]: invoke notification handlers for a tiddler
* [[Dev:notifyAll|notifyAll]]: invoke all notification handlers
* [[Dev:resumeNotifications|resumeNotifications]]: resumes update notifications
* [[Dev:reverseLookup|reverseLookup]]: finds all tiddlers matching a criteria
* [[Dev:saveTiddler|saveTiddler]]: saves changes to a tiddler
* [[Dev:search|search]]: search for tiddlers whose source code contains a string
* [[Dev:setDirty|setDirty]]: sets the TiddlyWiki's dirty flag
* [[Dev:suspendNotifications|suspendNotifications]]: halts update notifications
* [[Dev:tiddlerExists|tiddlerExists]]: returns whether a tiddler exists
The <tt>getTags</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object returns an array with information about the tags used in the TiddlyWiki. Each element is an array in itself, with two items:
* the name of the tag
* the number of tiddlers with this tag
The overall array is sorted by tag name. If the TiddlyWiki is empty, an empty array is returned.
The <tt>loadfromDiv</tt> method of a [[TiddlyWiki class|TiddlyWiki]] object initializes the TiddlyWiki by examining the HTML source of the page. It takes two parameters:
* the id of the div where tiddlers are stored (by convention, 'storeArea')
* the prefix each tiddler has to its id (by convention, 'store')
You won't typically need to call this function yourself; TiddlyWiki does this when the page is loaded and stores the result in the [[Dev:store|store]] global object.
Similar to [[Dev:Macros|creating macros]], [[Tiddler Toolbar|toolbar]] commands are created by attaching an object to <code>config.commands</code>:
<source lang="JavaScript">
config.commands.sampleCommand = {
text: "label",
tooltip: "tooltip",
handler: function(event, src, title) {
// ...
return false;
}
};
</source>
Each command has a ''handler'' method (<code>config.commands.*.handler</code>), as well as <code>text</code> and <code>tooltip</code> properties for the generated command button's label and tooltip, respectively.
{{Note|
In order to use a newly-created toolbar command, this has to be added to the [[ToolbarCommands]] shadow tiddler.
The keyword for a command is the same as the name of the object defining the handler (here ''sampleCommand'').
}}
Command handlers are invoked when the respective button is clicked and are passed the following arguments:
* '''event:''' the respective event
* '''src:''' the button element
* '''title:''' the title of the respective tiddler
== Command Popups ==
Toolbar commands can also create popups:
<source lang="JavaScript">
config.commands.sampleCommand = {
type: "popup",
text: "label",
tooltip: "tooltip",
handlePopup: function(popup, title) {
// ...
}
};
</source>
== Conditional Display ==
Commands have an optional <code>isEnabled</code> method which is passed the respective tiddler object and determines whether the command button will be rendered (returning <code>true</code> or <code>false</code>).
In addition, the following optional properties are available:
* '''hideShadow:''' do not display for shadow tiddlers
* '''hideReadOnly:''' do not display in ''[[readOnly]]'' state
* '''readOnlyText:''' button label for ''[[readOnly]]'' state
* '''readOnlyTooltip:''' tooltip for ''[[readOnly]]'' state
== See Also ==
* [[Dev:Macros]]
== TiddlyWiki Assembly ==
* [[Dev:Cook|Cook]]
* [[Dev:Ginsu|Ginsu]]
* [[Dev:Recipe|Recipes]]
* [[Dev:Chef|Chef]]
* [[r4tw]]
== Debugging and Testing ==
* [http://www.getfirebug.com Firebug] (Firefox extension)
[[Category:Developer Tools]]
See http://trac.tiddlywiki.org/wiki/Translations for a current list of translations, and information on how to do new translations.
See http://trac.tiddlywiki.org/browser/Trunk/association/locales/core to view the source code of existing translations.
[[Category:Incomplete]]
twill is a tool for scripting web browsing in python.
http://twill.idyll.org/
It could be useful for testing in some cases, but it is not as tied to HTTP as some would like. It thinks more in terms of HTML and links, rather than HTTP, URLs and methods.
{{WIP}}
cf. [http://groups.google.com/group/TiddlyWiki/browse_thread/thread/bbfcc35e63096a8c <nowiki>[tw]</nowiki> TW interface annoyances - what are yours?]
* header too tall
* switching from edit to view mode does not scroll to the top of the respective tiddler
* closing the bottom tiddler does not adjust scrolling to put tiddlers in view
* links to non-existing tiddlers in the [[sidebar]]'s Options slider
The <tt>unescapeLineBreaks</tt> function of a [[Tiddler]] object is the reverse of the [[Dev:escapeLineBreaks|escapeLineBreaks]] method. It turns the literal string <tt>\n</tt> into a newline character, and the string <tt>\\</tt> into <tt>\</tt>.
[http://en.wikipedia.org/wiki/Unit_testing Unit tests] increase confidence in the quality and reliability of source code.
From release 2.5, The TiddlyWiki core uses the [http://docs.jquery.com/QUnit QUnit] framework (gradually transitioning from [http://jania.pe.kr/aw/moin.cgi/JSSpec JSSPec]).
The [http://trac.tiddlywiki.org/browser/Trunk/core/test/recipes/tests.html.recipe test suite] [[Dev:Recipe|recipe]] provides a simple way to get started.
Individual tests are located in the [http://trac.tiddlywiki.org/browser/Trunk/core/test/tests/ tests directory].
{{Review}}
The options under the options slider and in AdvancedOptions are stored in browser cookies. So if you sometimes lose your cookies or you are working on your TiddlyWiki on several computers then it can be tiresome to have to enter your options time and time again.
==Use Cases==
''There is probably a better way of expressing this, a table?. Just jotted down for now:''
===TiddlyWiki Preferences===
# options defining viewing preferences - Animate, etc
# options defining editing preferences - UserName, etc
===TiddlyWiki Users===
# single user, editing and viewing
# single user editing, several viewing
# multiple users editing and viewing
===TiddlyWiki Origin===
# local file URI, stable
# local file URI, moved or copied from another TiddlyWiki
# file URI on a network or shared drive
# http URI served by [[TiddlySpot]], [[TiddlyWeb]] or [[ccTiddly]] ''separate use-cases?''
# other URI, such as ftp ''probably downloaded as a file URI''
===Scope===
# options scoped to a specific file
# options scoped to a specific browser/computer
# options carried across multiple files
===Persistence===
# options may be cleared with browser cache clearing
# options persisting beyond browser reset/upgrade
===Precedence===
* cookie based options override options stored in a tiddler
==Issues==
* any changes to the options implementation will have to deal with being backwards compatible, or having a migration for existing [[TiddlyWiki]] users.
* cookies are not supported for file URIs in the [[http://www.google.com/chrome Google Chrome]] browser as discussed in [[http://code.google.com/p/chromium/issues/detail?id=535 Issue:535]] for reasons explained in the paper [[http://www.adambarth.com/papers/2008/jackson-barth-b.pdf Beware of Finer-Grained Origins]]
==Discussion==
This page originated from a thread on the [[TiddlyWikiDev]] forum:
http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/6945f595e6917ce5
== See Also ==
* [[Configuration_Options]]
== Global methods ==
<tt>getParser</tt> selects the correct Formatter for a tiddler.
The <tt>wikify</tt> function:
<pre>
function wikify(source,output,highlightRegExp,tiddler)
{
if(source && source != "") {
var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
wikifier.subWikifyUnterm(output);
}
}
</pre>
It creates a Wikifier object and then calls wikifier.subWikifyUnterm.
<tt>wikifyStatic</tt> returns the html directly.
<tt>wikifyPlain</tt> calls wikifyPlainText on a single tiddler.
<tt>wikifyPlainText</tt> calls wikifier.wikifyPlain on the text.
<tt>highlightify</tt> calls wikifier.outputText.
== Static methods ==
The <tt>Wikifier</tt> Object
Constructor takes a formatter. <tt>getParser</tt> figures out which one. <tt>config.parsers</tt> is examined but is empty by default, which means that in the default setup, calls to <tt>getParser</tt> return the global formatter object.
<tt>Wikifier.prototype.wikifyPlain</tt> calls subWikify on itself
<tt>Wikifier.prototype.subWikify</tt> calls either <tt>subWikifyTerm</tt> or <tt>subWikifyUnterm</tt> depending on whether it has a terminator provided or not (see below). Example uses are in the gradient macro's handler, where it is called with a terminator, and <tt>Wikifer.prototype.wikifyPlain</tt> where it is called without.
<tt>Wikifier.prototype.subWikifyUnterm</tt> is the function that gets called by <tt>wikify</tt> and <tt>wikifyStatic</tt>. <tt>wikifyPlain</tt> and <tt>wikifyPlainText</tt> calls <tt>subWikify</tt> with one argument, so that ends up calling <tt>subWikifyUnterm</tt> as well. The function finds the strings that match with the formatter, outputs whatever is before the first as ordinary text, then goes into a loop through the matches until they run out. The mechanism for figuring out which part of the formatter and therefore which handler to call depends on the result of an exec() call being an array where the elements correspond to the paranthesized parts of the regex - if there is a match, that element contains something in it, if not, it is undefined. After the matches are finished, it outputs the rest of the text. The handlers tend to wikify their contents, so the wikification process is recursive. The upshot of this is that care has to be taken to keep the regex's lastIndex property and the output pointer in check.
<tt>Wikifier.prototype.subWikifyTerm</tt> is similar to <tt>subWikifyUnTerm</tt>. It takes a terminator regex to match the end of the string in question and wikifies (again a use of recursion) whatever is in between a formatter match and a terminator match.
<tt>Wikifier.prototype.outputText</tt> outputs text without wikifying it. Copes with highlighting text (via highlightify).
The global function <tt>wikify</tt> renders TiddlyWiki source code into an Element. There are four parameters to this function:
* the source code to render
* the DOM element to render into
* a regular expression to highlight
* the [[Tiddler]] that the source belongs to
The <tt>wikifyPlain</tt> global function renders an entire tiddler as plain text. It takes one parameter, the title of the tiddler to render, and returns a string with the output.
The global function <tt>wikifyStatic</tt> works the same way as that the [[Dev:wikify|wikify]] method does, but instead of rendering directly into an DOM element, it returns HTML source code as a string. It takes three parameters:
* the source code to render
* the regular expression to highlight
* the [[Tiddler]] to use as a reference point for rendering
/***
|''Name:''|MediaWikiFormatterPlugin|
|''Description:''|Allows Tiddlers to use [[MediaWiki|http://meta.wikimedia.org/wiki/Help:Wikitext]] ([[WikiPedia|http://meta.wikipedia.org/]]) text formatting|
|''Author:''|Martin Budden (mjbudden (at) gmail (dot) com)|
|''Source:''|http://www.martinswiki.com/#MediaWikiFormatterPlugin |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/formatters/MediaWikiFormatterPlugin.js |
|''Version:''|0.4.6|
|''Date:''|Jul 27, 2007|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''~CoreVersion:''|2.1.0|
|''Display instrumentation''|<<option chkDisplayInstrumentation>>|
|''Display empty template links:''|<<option chkMediaWikiDisplayEmptyTemplateLinks>>|
|''Allow zooming of thumbnail images''|<<option chkMediaWikiDisplayEnableThumbZoom>>|
|''List references''|<<option chkMediaWikiListReferences>>|
|''Display unsupported magic words''|<<option chkDisplayMediaWikiMagicWords>>|
This is the MediaWikiFormatterPlugin, which allows you to insert MediaWiki formated text into a TiddlyWiki.
The aim is not to fully emulate MediaWiki, but to allow you to work with MediaWiki content off-line and then resync the content with your MediaWiki later on, with the expectation that only minor edits will be required.
To use MediaWiki format in a Tiddler, tag the Tiddler with MediaWikiFormat or set the tiddler's {{{wikiformat}}} extended field to {{{mediawiki}}}.
!!!Issues
There are (at least) the following known issues:
# Not all styles from http://meta.wikimedia.org/wiki/MediaWiki:Common.css incorporated
## Styles for tables don't yet match Wikipedia styles.
## Styles for image galleries don't yet match Wikipedia styles.
# Anchors not yet supported.
!!!Not supported
# Template parser functions (also called colon functions) http://meta.wikimedia.org/wiki/ParserFunctions eg {{ #functionname: argument 1 | argument 2 | argument 3... }}
# Magic words and variables http://meta.wikimedia.org/wiki/Help:Magic_words eg {{{__TOC__}}}, {{CURRENTDAY}}, {{PAGENAME}}
# {{{^''}}} (italic at start of line) indents, makes italic and quotes with guilmot quote
!!!No plans to support
# Template substitution on save http://meta.wikimedia.org/wiki/Help:Substitution eg {{ subst: templatename }}
***/
//{{{
// Ensure that the MediaWikiFormatter Plugin is only installed once.
if(!version.extensions.MediaWikiFormatterPlugin) {
version.extensions.MediaWikiFormatterPlugin = {installed:true};
if(version.major < 2 || (version.major == 2 && version.minor < 1))
{alertAndThrow('MediaWikiFormatterPlugin requires TiddlyWiki 2.1 or later.');}
if(config.options.chkDisplayInstrumentation == undefined)
{config.options.chkDisplayInstrumentation = false;}
if(config.options.chkMediaWikiDisplayEmptyTemplateLinks == undefined)
{config.options.chkMediaWikiDisplayEmptyTemplateLinks = false;}
if(config.options.chkMediaWikiDisplayEnableThumbZoom == undefined)
{config.options.chkMediaWikiDisplayEnableThumbZoom = false;}
if(config.options.chkMediaWikiListReferences == undefined)
{config.options.chkMediaWikiListReferences = false;}
if(config.options.chkDisplayMediaWikiMagicWords == undefined)
{config.options.chkDisplayMediaWikiMagicWords = false;}
//<div class='viewer' macro='view text wikified'></div>;
config.macros.include = {};
config.macros.include.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if((tiddler instanceof Tiddler) && params[0]) {
var host = store.getValue(tiddler,'server.host');
if(host && host.indexOf('wikipedia')!=-1) {
var t = store.fetchTiddler(params[0]);
var text = store.getValue(t,'text');
wikify(text,place,highlightHack,tiddler);
}
}
};
MediaWikiFormatter = {}; // 'namespace' for local functions
mwDebug = function(out,str)
{
createTiddlyText(out,str.replace(/\n/mg,'\\n').replace(/\r/mg,'RR'));
createTiddlyElement2(out,'br');
};
MediaWikiFormatter.Tiddler_changed = Tiddler.prototype.changed;
Tiddler.prototype.changed = function()
{
if((this.fields.wikiformat==config.parsers.mediawikiFormatter.format) || this.isTagged(config.parsers.mediawikiFormatter.formatTag)) {
this.links = [];
var tiddlerLinkRegExp = /\[\[(?::?([A-Za-z]{2,}:)?)(#?)([^\|\]]*?)(?:(\]\])|(\|(.*?)\]\]))/mg;
tiddlerLinkRegExp.lastIndex = 0;
var match = tiddlerLinkRegExp.exec(this.text);
while(match) {
if(!match[1] && !match[2])
this.links.pushUnique(match[3]);
match = tiddlerLinkRegExp.exec(this.text);
}
} else if(!this.isTagged('systemConfig')) {
MediaWikiFormatter.Tiddler_changed.apply(this,arguments);
return;
}
this.linksUpdated = true;
};
TiddlyWiki.prototype.getMediaWikiPagesInNamespace = function(namespace)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(tiddler.title.indexOf(namespace)==0)
results.push(tiddler);
});
results.sort(function(a,b) {return a.title < b.title ? -1 : +1;});
return results;
};
TiddlyWiki.prototype.getMediaWikiPages = function()
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(!tiddler.isTagged('excludeLists') && tiddler.title.indexOf(':')==-1)
results.push(tiddler);
});
results.sort(function(a,b) {return a.title < b.title ? -1 : +1;});
return results;
};
TiddlyWiki.prototype.getMediaWikiOtherPages = function()
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(!tiddler.isTagged('excludeLists') && tiddler.title.indexOf(':')!=-1)
results.push(tiddler);
});
results.sort(function(a,b) {return a.title < b.title ? -1 : +1;});
return results;
};
config.macros.list.otherpages = {};
config.macros.list.otherpages.handler = function(params)
{
return store.getMediaWikiOtherPages();
};
config.macros.list.templates = {};
config.macros.list.templates.handler = function(params)
{
return store.getMediaWikiPagesInNamespace('Template:');
};
config.macros.list.categories = {};
config.macros.list.categories.handler = function(params)
{
return store.getMediaWikiPagesInNamespace('Category:');
};
function createTiddlyElement2(parent,element)
{
return parent.appendChild(document.createElement(element));
}
config.formatterHelpers.createElementAndWikify = function(w)
{
w.subWikifyTerm(createTiddlyElement2(w.output,this.element),this.termRegExp);
};
MediaWikiFormatter.hijackListAll = function ()
{
MediaWikiFormatter.oldListAll = config.macros.list.all.handler;
config.macros.list.all.handler = function(params) {
return store.getMediaWikiPages();
};
};
MediaWikiFormatter.hijackListAll();
MediaWikiFormatter.normalizedTitle = function(title)
{
title = title.trim();
var n = title.charAt(0).toUpperCase() + title.substr(1);
return n.replace(/\s/g,'_');
};
MediaWikiFormatter.expandVariable = function(w,variable)
{
switch(variable) {
case 'PAGENAME':
createTiddlyText(w.output,w.tiddler.title);
break;
case 'PAGENAMEE':
createTiddlyText(w.output,MediaWikiFormatter.normalizedTitle(w.tiddler.title));
break;
case 'REVISIONID':
var text = w.tiddler.fields['server.revision'];
if(text)
createTiddlyText(w.output,text);
break;
default:
return false;
}
return true;
};
MediaWikiFormatter.getParserFunctionParams = function(text)
{
var params = [];
// #if: foo | do if true | do if false
text += '|';
//var fRegExp = / ? # ?([a-z]*) ?:/mg;
//var fRegExp = /#(if) : (foo) :/mg;
var fRegExp = /(#([a-z]*) *: ([^\|]*))\|/mg;
fRegExp.lastIndex = 0;
var match = fRegExp.exec(text);
var pRegExp = /([^\|]*)\|/mg;
if(match) {
pRegExp.lastIndex = fRegExp.lastIndex;
match = pRegExp.exec(text);
}
var i = 1;
while(match) {
params[i] = match[1].trim();
i++;
match = pRegExp.exec(text);
}
return params;
};
MediaWikiFormatter.getTemplateParams = function(text)
{
var params = {};
text += '|';
var pRegExp = /(?:([^\|]*)=)?([^\|]*)\|/mg;
var match = pRegExp.exec(text);
if(match) {
match = pRegExp.exec(text);
}
var i = 1;
while(match) {
if(match[1]) {
params[match[1]] = match[2];
} else {
params[i] = match[2];
i++;
}
match = pRegExp.exec(text);
}
return params;
};
MediaWikiFormatter.expandParserFunction = function(w,text,expr,params)
{
var fnRegExp = / *#(if) */mg;
var t = '';//params[0];
fnRegExp.lastIndex = 0;
var match = fnRegExp.exec(text);
if(match) {
switch(match[1]) {
case 'if':
t = expr.trim()=='' ? params[2] : params[1];
break;
default:
break;
}
}
return t;
};
MediaWikiFormatter.expandTemplate = function(w,templateText,params)
{
var text = templateText;
text = text.replace(/<noinclude>((?:.|\n)*?)<\/noinclude>/mg,'');// remove text between noinclude tags
var includeOnlyRegExp = /<includeonly>((?:.|\n)*?)<\/includeonly>/mg;
var t = '';
var match = includeOnlyRegExp.exec(text);
while(match) {
t += match[1];
match = includeOnlyRegExp.exec(text);
}
text = t == '' ? text : t;
var paramsRegExp = /\{\{\{(.*?)(?:\|(.*?))?\}\}\}/mg;
t = '';
var pi = 0;
match = paramsRegExp.exec(text);
while(match) {
var name = match[1];
var val = params[name];
if(!val) {
val = match[2];
}
if(!val) {
val = '';//val = match[0];
}
t += text.substring(pi,match.index) + val;
pi = paramsRegExp.lastIndex;
match = paramsRegExp.exec(text);
}
t += text.substring(pi);
return t;
//return t == '' ? text : t;
/* //displayMessage("ss:"+text.substring(pi));
t += text.substring(pi);
t = MediaWikiFormatter.evaluateTemplateParserFunctions(t);
//{{#if: {{{perihelion|}}} | <tr><th>[[Perihelion|Perihelion distance]]:</th><td>{{{perihelion}}}</td></tr>}}
//{{#if:{{{symbol|}}} | {{{symbol}}} | }}
text = t == '' ? text : t;
displayMessage("t2:"+text);
return text;
*/
};
MediaWikiFormatter.endOfParams = function(w,text)
{
var p = 0;
var i = text.indexOf('|');
if(i==-1) {return -1;}
var n = text.indexOf('\n');
if(n!=-1 && n<i) {return -1;}
var b = text.indexOf('[[');
if(b!=-1 && b<i) {return -1;}
b = text.indexOf('{{');
while(b!=-1 && b<i) {
p += b;
text = text.substr(b);
var c = text.indexOf('}}');
p += c;
text = text.substr(c);
i = text.indexOf('|');
if(i==-1) {return -1;}
n = text.indexOf('\n');
if(n!=-1 && n<i) {return -1;}
b = text.indexOf('{{');
i = -1;
}
return i;
};
MediaWikiFormatter.readToDelim = function(w)
//!!! this is a bit rubish, needs doing properly.
{
var dRegExp = /\|/mg;
var sRegExp = /\[\[/mg;
var tRegExp = /\]\]/mg;
dRegExp.lastIndex = w.startMatch;
var dMatch = dRegExp.exec(w.source);
sRegExp.lastIndex = w.startMatch;
var sMatch = sRegExp.exec(w.source);
tRegExp.lastIndex = w.startMatch;
var tMatch = tRegExp.exec(w.source);
if(!tMatch) {
return false;
}
while(sMatch && sMatch.index<tMatch.index) {
if(dMatch && dMatch.index<sMatch.index) {
w.nextMatch = dRegExp.lastIndex;
w.matchLength = dMatch.index - w.startMatch;
return true;
}
tRegExp.lastIndex = sRegExp.lastIndex;
tMatch = tRegExp.exec(w.source);
w.nextMatch = tRegExp.lastIndex;
dRegExp.lastIndex = w.nextMatch;
dMatch = dRegExp.exec(w.source);
sRegExp.lastIndex = w.nextMatch;
sMatch = sRegExp.exec(w.source);
tRegExp.lastIndex = w.nextMatch;
tMatch = tRegExp.exec(w.source);
}
if(dMatch && dMatch.index<tMatch.index) {
w.nextMatch = dRegExp.lastIndex;
w.matchLength = dMatch.index - w.startMatch;
return true;
}
if(tMatch) {
w.nextMatch = tRegExp.lastIndex;
w.matchLength = tMatch.index - w.startMatch;
return false;
}
w.nextMatch = tRegExp.lastIndex;
w.matchLength = -1;
return false;
};
MediaWikiFormatter.getParams = function(w)
{
var params = [];
var i = 1;
w.startMatch = w.nextMatch;
var read = MediaWikiFormatter.readToDelim(w);
if(w.matchLength!=-1) {
params[i] = w.source.substr(w.startMatch,w.matchLength);
}
while(read) {
i++;
w.startMatch = w.nextMatch;
read = MediaWikiFormatter.readToDelim(w);
if(w.matchLength!=-1) {
params[i] = w.source.substr(w.startMatch,w.matchLength);
}
}
return params;
};
MediaWikiFormatter.setFromParams = function(w,p)
{
var r = {};
var re = /\s*(.*?)=(?:(?:"(.*?)")|(?:'(.*?)')|((?:\w|%|#)*))/mg;
var match = re.exec(p);
while(match)
{
var s = match[1].unDash();
if(match[2]) {
r[s] = match[2];
} else if(match[3]) {
r[s] = match[3];
} else {
r[s] = match[4];
}
match = re.exec(p);
}
return r;
};
MediaWikiFormatter.setAttributesFromParams = function(e,p)
{
var re = /\s*(.*?)=(?:(?:"(.*?)")|(?:'(.*?)')|((?:\w|%|#)*))/mg;
var match = re.exec(p);
while(match) {
var s = match[1].unDash();
if(s == 'bgcolor') {
s = 'backgroundColor';
}
try {
if(match[2]) {
e.setAttribute(s,match[2]);
} else if(match[3]) {
e.setAttribute(s,match[3]);
} else {
e.setAttribute(s,match[4]);
}
}
catch(ex) {}
match = re.exec(p);
}
};
config.mediawiki = {};
config.mediawiki.formatters = [
{
name: 'mediaWikiHeading',
match: '^={1,6}(?!=)\\n?',
termRegExp: /(={1,6}\n?)/mg,
handler: function(w)
{
var output = w.output;
var e = createTiddlyElement2(output,'h' + w.matchLength);
var a = createTiddlyElement2(e,'a');
var t = w.tiddler ? MediaWikiFormatter.normalizedTitle(w.tiddler.title) + ':' : '';
var len = w.source.substr(w.nextMatch).indexOf('=');
a.setAttribute('name',t+MediaWikiFormatter.normalizedTitle(w.source.substr(w.nextMatch,len)));
w.subWikifyTerm(e,this.termRegExp);
}
},
{
name: 'mediaWikiTable',
// see http://www.mediawiki.org/wiki/Help:Tables, http://meta.wikimedia.org/wiki/Help:Table
match: '^\\{\\|', // ^{|
tableTerm: '\\n\\|\\}', // |}
rowStart: '\\n\\|\\-', // \n|-
cellStart: '\\n!|!!|\\|\\||\\n\\|', //\n! or !! or || or \n|
caption: '\\n\\|\\+',
rowTerm: null,
cellTerm: null,
inCellTerm: null,
tt: 0,
debug: null,
rowTermRegExp: null,
handler: function(w)
{
if(!this.rowTermRegExp) {
this.rowTerm = '(' + this.tableTerm +')|(' + this.rowStart + ')';
this.cellTerm = this.rowTerm + '|(' + this.cellStart + ')';
this.inCellTerm = '(' + this.match + ')|' + this.rowTerm + '|(' + this.cellStart + ')';
this.caption = '(' + this.caption + ')|' + this.cellTerm;
this.rowTermRegExp = new RegExp(this.rowTerm,'mg');
this.cellTermRegExp = new RegExp(this.cellTerm,'mg');
this.inCellTermRegExp = new RegExp(this.inCellTerm,'mg');
this.captionRegExp = new RegExp(this.caption,'mg');
}
this.captionRegExp.lastIndex = w.nextMatch;
var match = this.captionRegExp.exec(w.source);
if(!match) {return;}
var output = w.output;
var table = createTiddlyElement2(output,'table');
var rowContainer = table;
var i = w.source.indexOf('\n',w.nextMatch);
if(i>w.nextMatch) {
MediaWikiFormatter.setAttributesFromParams(table,w.source.substring(w.nextMatch,i));
w.nextMatch = i;
}
var rowCount = 0;
var eot = false;
if(match[1]) {
var caption = createTiddlyElement2(table,'caption');
w.nextMatch = this.captionRegExp.lastIndex;
var captionText = w.source.substring(w.nextMatch);
var n = captionText.indexOf('\n');
captionText = captionText.substr(0,n);
i = MediaWikiFormatter.endOfParams(w,captionText);
if(i!=-1) {
captionText = w.source.substr(w.nextMatch,i);
w.nextMatch += i+1;
}
if(caption != table.firstChild) {
table.insertBefore(caption,table.firstChild);
}
w.subWikify(caption,this.cellTerm);
w.nextMatch -= w.matchLength;
this.cellTermRegExp.lastIndex = w.nextMatch;
var match2 = this.cellTermRegExp.exec(w.source);
if(match2) {
if(match2[3]) {
eot = this.rowHandler(w,createTiddlyElement2(rowContainer,'tr'));
rowCount++;
}
}
} else if(match[3]) {
w.nextMatch = this.captionRegExp.lastIndex-match[3].length;
} else if(match[4]) {
w.nextMatch = this.captionRegExp.lastIndex-match[4].length;
eot = this.rowHandler(w,createTiddlyElement2(rowContainer,'tr'));
rowCount++;
}
this.rowTermRegExp.lastIndex = w.nextMatch;
match = this.rowTermRegExp.exec(w.source);
while(match && eot==false) {
if(match[1]) {
w.nextMatch = this.rowTermRegExp.lastIndex;
if(w.tableDepth==0) {
return;
}
} else if(match[2]) {
var rowElement = createTiddlyElement2(rowContainer,'tr');
w.nextMatch += match[2].length;
i = w.source.indexOf('\n',w.nextMatch);
if(i>w.nextMatch) {
MediaWikiFormatter.setAttributesFromParams(rowElement,w.source.substring(w.nextMatch,i));
w.nextMatch = i;
}
eot = this.rowHandler(w,rowElement);
}
rowCount++;
this.rowTermRegExp.lastIndex = w.nextMatch;
match = this.rowTermRegExp.exec(w.source);
}//# end while
if(w.tableDepth==0) {
w.nextMatch +=3;
}
},//# end handler
rowHandler: function(w,e)
{
var cell;
this.inCellTermRegExp.lastIndex = w.nextMatch;
var match = this.inCellTermRegExp.exec(w.source);
while(match) {
if(match[1]) {
w.tableDepth++;
w.subWikify(cell,this.tableTerm);
w.nextMatch = this.tt;
w.tableDepth--;
return false;
} else if(match[2]) {
this.tt = this.inCellTermRegExp.lastIndex;
return true;
} else if(match[3]) {
return false;
} else if(match[4]) {
var len = match[4].length;
cell = createTiddlyElement2(e,match[4].substr(len-1)=='!'?'th':'td');
w.nextMatch += len;
this.inCellTermRegExp.lastIndex = w.nextMatch;
var lookahead = this.inCellTermRegExp.exec(w.source);
if(!lookahead) {
return false;
}
var cellText = w.source.substr(w.nextMatch,lookahead.index-w.nextMatch);
var oldSource = w.source;
var i = MediaWikiFormatter.endOfParams(w,cellText);//cellText.indexOf('|');
if(i!=-1) {
cellText = cellText.replace(/^\+/mg,''); //!!hack until I fix this properly
MediaWikiFormatter.setAttributesFromParams(cell,cellText.substr(0,i-1));
cellText = cellText.substring(i+1);
}
cellText = cellText.replace(/^\s*/mg,'');
w.source = cellText;
w.nextMatch = 0;
w.subWikifyUnterm(cell);
w.source = oldSource;
w.nextMatch = lookahead.index;
}
this.inCellTermRegExp.lastIndex = w.nextMatch;
match = this.inCellTermRegExp.exec(w.source);
}//# end while
return false;
}//# end rowHandler
},
{
name: 'mediaWikiList',
match: '^[\\*#;:]+',
lookaheadRegExp: /(?:(?:(\*)|(#)|(;)|(:))+)(?: ?)/mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var stack = [w.output];
var currLevel = 0, currType = null;
var listType, itemType;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
if(lookaheadMatch[1]) {
listType = 'ul';
itemType = 'li';
} else if(lookaheadMatch[2]) {
listType = 'ol';
itemType = 'li';
} else if(lookaheadMatch[3]) {
listType = 'dl';
itemType = 'dt';
} else if(lookaheadMatch[4]) {
listType = 'dl';
itemType = 'dd';
}
var listLevel = lookaheadMatch[0].length;
w.nextMatch += listLevel;
if(listLevel > currLevel) {
for(var i=currLevel; i<listLevel; i++) {
stack.push(createTiddlyElement2(stack[stack.length-1],listType));
}
} else if(listLevel < currLevel) {
for(i=currLevel; i>listLevel; i--) {
stack.pop();
}
} else if(listLevel == currLevel && listType != currType) {
stack.pop();
stack.push(createTiddlyElement2(stack[stack.length-1],listType));
}
currLevel = listLevel;
currType = listType;
var e = createTiddlyElement2(stack[stack.length-1],itemType);
var ci = w.source.indexOf(':',w.nextMatch);
var ni = w.source.indexOf('\n',w.nextMatch);
if(itemType=='dt' && (ni==-1 || (ci!=-1 && ci<ni))) {
w.subWikifyTerm(e,/(:)/mg);
w.nextMatch--;
} else {
w.subWikifyTerm(e,this.termRegExp);
}
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
}
},
{
name: 'mediaWikiRule',
match: '^----+$\\n?',
handler: function(w)
{
createTiddlyElement2(w.output,'hr');
}
},
{
name: 'mediaWikiLeadingSpaces',
match: '^ ',
lookaheadRegExp: /^ /mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var e = createTiddlyElement2(w.output,'pre');
while(true) {
w.subWikifyTerm(e,this.termRegExp);
createTiddlyElement2(e,'br');
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
w.nextMatch += lookaheadMatch[0].length;
} else {
break;
}
}
}
},
{
name: 'mediaWikiImage',
match: '\\[\\[(?:[Ii]mage|Bild):',
lookaheadRegExp: /\[\[(?:[Ii]mage|Bild):/mg,
defaultPx: 180,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var params = MediaWikiFormatter.getParams(w);
var src = params[1];
src = src.trim().replace(/ /mg,'_');
src = src.substr(0,1).toUpperCase() + src.substring(1);
var palign = null;
var ptitle = null;
var psrc = false;
var px = null;
var pthumb = false;
var pframed = false;
for(var i=2;i<params.length;i++) {
var p = params[i];
if(p=='right'||p=='left'||p=='center'||p=='none') {
palign = p;
} else if(p=='thumbnail'||p=='thumb') {
pthumb = true;
} else if(p=='framed') {
pframed = true;
} else if(/\d{1,4} ?px/.exec(p)) {
px = p.substr(0,p.length-2).trim();
} else {
ptitle = p;
}
}//#end for
if(pthumb) {
var output = w.output;
if(!palign) {
palign = 'right';
}
if(!px) {
px = 180;
}
psrc = px + 'px-' + src;
var t = createTiddlyElement(output,'div',null,'thumb'+(palign?' t'+palign:''));
var s = createTiddlyElement2(t,'div');
s.style['width'] = Number(px) + 2 + 'px';
var a = createTiddlyElement(s,'a',null,'internal');
if(config.options.chkMediaWikiDisplayEnableThumbZoom) {
a.href = src;
}
a.title = ptitle;
var img = createTiddlyElement2(a,'img');
img.src = 'images/' + psrc;
img.width = px;
img.longdesc = 'Image:' + src;
img.alt = ptitle;
var tc = createTiddlyElement(s,'div',null,'thumbcaption');
var oldSource = w.source; var oldMatch = w.nextMatch;
w.source = ptitle; w.nextMatch = 0;
w.subWikifyUnterm(tc);
w.source = oldSource; w.nextMatch = oldMatch;
if(config.options.chkMediaWikiDisplayEnableThumbZoom) {
var tm = createTiddlyElement(tc,'div',null,'magnify');
tm.style['float'] = 'right';
var ta = createTiddlyElement(tm,'a',null,'internal');
ta.title = 'Enlarge';
timg = createTiddlyElement2(ta,'img'); timg.src = 'magnify-clip.png'; timg.alt = 'Enlarge'; timg.width = '15'; timg.height = '11';
ta.href = src;
}
} else {
a = createTiddlyElement(w.output,'a',null,'image');
a.title = ptitle;
img = createTiddlyElement2(a,'img');
if(palign) {img.align = palign;}
img.src = px ? 'images/' + px + 'px-' + src : 'images/' + src;
if(px) {img.width = px;}
img.longdesc = 'Image:' + src;
img.alt = ptitle;
}
}
}//#end image handler
},
{
name: 'mediaWikiExplicitLink',
match: '\\[\\[',
lookaheadRegExp: /\[\[(?:([a-z]{2,3}:)?)(#?)([^\|\]]*?)(?:(\]\](\w*))|(\|(.*?)\]\]))/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
if(!lookaheadMatch[1]) {
var e;
var link = lookaheadMatch[3];
var text = link;
link = link.substr(0,1).toUpperCase() + link.substring(1);
if(lookaheadMatch[4]) {
if(lookaheadMatch[2]) {
var a = createTiddlyElement(w.output,'a');
var t = w.tiddler ? MediaWikiFormatter.normalizedTitle(w.tiddler.title) + ':' : '';
t = '#' + t + MediaWikiFormatter.normalizedTitle(link);
a.setAttribute('href',t);
a.title = '#' + MediaWikiFormatter.normalizedTitle(link);
createTiddlyText(a,'#'+link);
} else {
e = createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
if(lookaheadMatch[5]) {
text += lookaheadMatch[5];
}
createTiddlyText(e,text);
}
} else if(lookaheadMatch[6]) {
if(link.charAt(0)==':')
link = link.substring(1);
e = createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
var oldSource = w.source; var oldMatch = w.nextMatch;
w.source = lookaheadMatch[7].trim(); w.nextMatch = 0;
w.subWikifyUnterm(e);
w.source = oldSource; w.nextMatch = oldMatch;
}
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiTemplate',
match: '\\{\\{[^\\{]',
lookaheadRegExp: /\{\{((?:.|\n)*?)\}\}/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var lastIndex = this.lookaheadRegExp.lastIndex;
var contents = lookaheadMatch[1];
if(MediaWikiFormatter.expandVariable(w,contents)) {
w.nextMatch = lastIndex;
return;
}
var i = contents.indexOf('|');
var title = i==-1 ? contents : contents.substr(0,i);
title = title.trim().replace(/_/mg,' ');
if(title.substr(0,1)=='#') {
var parserFn = true;
var j = contents.indexOf(':');
var expr = contents.substring(j+1,i);
i = title.indexOf(':');
title = title.substr(0,i);
} else {
title = 'Template:' + title.substr(0,1).toUpperCase() + title.substring(1);
var tiddler = store.fetchTiddler(title);
}
var oldSource = w.source;
if(tiddler) {
var params = {};
if(i!=-1) {
params = MediaWikiFormatter.getTemplateParams(lookaheadMatch[1]);
}
w.source = MediaWikiFormatter.expandTemplate(w,tiddler.text,params);
w.nextMatch = 0;
w.subWikifyUnterm(w.output);
} else if(parserFn) {
if(i!=-1) {
params = MediaWikiFormatter.getParserFunctionParams(lookaheadMatch[1]);
}
w.source = MediaWikiFormatter.expandParserFunction(w,title,expr,params);
w.nextMatch = 0;
w.subWikifyUnterm(w.output);
} else {
if(config.options.chkMediaWikiDisplayEmptyTemplateLinks) {
w.source = '[['+title+']]';
w.nextMatch = 0;
w.subWikifyUnterm(w.output);
}
}
w.source = oldSource;
w.nextMatch = lastIndex;
}
}
},
{
name: 'mediaWikiParagraph',
match: '\\n{2,}',
handler: function(w)
{
w.output = createTiddlyElement2(w.output,'p');
}
},
{
name: 'mediaWikiExplicitLineBreak',
match: '<br ?/?>',
handler: function(w)
{
createTiddlyElement2(w.output,'br');
}
},
{
name: 'mediaWikiExplicitLineBreakWithParams',
match: "<br(?:\\s*(?:(?:.*?)=[\"']?(?:.*?)[\"']?))*?\\s*/?>",
lookaheadRegExp: /<br((?:\s+(?:.*?)=["']?(?:.*?)["']?)*?)?\s*\/?>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e =createTiddlyElement2(w.output,'br');
if(lookaheadMatch[1]) {
MediaWikiFormatter.setAttributesFromParams(e,lookaheadMatch[1]);
}
w.nextMatch = this.lookaheadRegExp.lastIndex;// empty tag
}
}
},
{
name: 'mediaWikiTitledUrlLink',
match: '\\[' + config.textPrimitives.urlPattern + '(?:\\s+[^\\]]+)?' + '\\]',
handler: function(w)
{
var lookaheadRegExp = new RegExp('\\[(' + config.textPrimitives.urlPattern + ')(?:\\s+([^\[]+))?' + '\\]','mg');
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index==w.matchStart) {
var link = lookaheadMatch[1];
if(lookaheadMatch[2]) {
var e = createExternalLink(w.output,link);
var oldSource = w.source; var oldMatch = w.nextMatch;
w.source = lookaheadMatch[2].trim(); w.nextMatch = 0;
w.subWikifyUnterm(e);
w.source = oldSource; w.nextMatch = oldMatch;
} else {
e = createExternalLink(createTiddlyElement2(w.output,'sup'),link);
w.linkCount++;
createTiddlyText(e,'['+w.linkCount+']');
}
w.nextMatch = lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiUrlLink',
match: config.textPrimitives.urlPattern,
handler: function(w)
{
w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
}
},
{
name: "mediaWikiCharacterFormat",
match: "'{2,5}|(?:<[usbi]>)",
handler: function(w)
{
switch(w.matchText) {
case "'''''":
var e = createTiddlyElement(w.output,'strong');
w.subWikifyTerm(createTiddlyElement(e,'em'),/('''''|(?=\n))/mg);
break;
case "'''":
w.subWikifyTerm(createTiddlyElement(w.output,'strong'),/('''|(?=\n))/mg);
break;
case "''":
w.subWikifyTerm(createTiddlyElement(w.output,'em'),/((?:''(?!'))|(?=\n))/mg);
break;
case '<u>':
w.subWikifyTerm(createTiddlyElement(w.output,'u'),/(<\/u>|(?=\n))/mg);
break;
case '<s>':
w.subWikifyTerm(createTiddlyElement(w.output,'del'),/(<\/s>|(?=\n))/mg);
break;
case '<b>':
w.subWikifyTerm(createTiddlyElement(w.output,'b'),/(<\/b>|(?=\n))/mg);
break;
case '<i>':
w.subWikifyTerm(createTiddlyElement(w.output,'i'),/(<\/i>|(?=\n))/mg);
break;
}
}
},
{
name: 'mediaWikiTemplateParam',
match: '\\{\\{\\{',
lookaheadRegExp: /(\{\{\{(?:.|\n)*?\}\}\})/mg,
element: 'span',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiInsertReference',
match: '<ref[^/]*>',
lookaheadRegExp: /<ref(\s+(?:.*?)=["']?(?:.*?)["']?)?>([^<]*?)<\/ref>/mg,
handler: function(w)
{
if(config.browser.isIE) {
refRegExp = /<ref[^\/]*>((?:.|\n)*?)<\/ref>/mg;
refRegExp.lastIndex = w.matchStart;
var refMatch = refRegExp.exec(w.source);
if(refMatch && refMatch.index == w.matchStart) {
w.nextMatch = refRegExp.lastIndex;
return;
}
}
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var x = {id:'',value:''};
w.nextMatch = this.lookaheadRegExp.lastIndex;
if(!w.referenceCount) {
w.referenceCount = 0;
w.references = {};
}
var s = createTiddlyElement(w.output,'sup',null,'reference');
var a = createTiddlyElement2(s,'a');
var prefix = w.tiddler ? w.tiddler.title + ':' : '';
var name;
if(lookaheadMatch[1]) {
var r = MediaWikiFormatter.setFromParams(w,lookaheadMatch[1]);
name = r.name ? r.name.trim() : '';
name = name.replace(/ /g,'_');
s.id = prefix + '_ref-' + name;// + '_' + nameCount;(w.referenceCount+1);
if(!w.references[name]) {
w.references[name] = x;
w.references[name].id = w.referenceCount;
w.references[name].value = lookaheadMatch[2].trim();
}
} else {
w.references[w.referenceCount] = x;
w.references[w.referenceCount].id = w.referenceCount;
w.references[w.referenceCount].value = lookaheadMatch[2].trim();
name = w.referenceCount;
s.id = prefix + '_ref-' + w.referenceCount;
}
w.referenceCount++;
a.title = lookaheadMatch[2].trim();//mb, extra to wikipedia
a.href = '#' + prefix + '_note-' + name;
a.innerHTML = '['+w.referenceCount+']';
}
}
},
{
name: 'mediaWikiListReferences',
match: '<references ?/>',
lookaheadRegExp: /<references ?\/>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(config.options.chkMediaWikiListReferences && w.referenceCount) {
var ol = createTiddlyElement(w.output,'ol',null,'references');
var oldSource = w.source;
if(w.referenceCount>0) {
for(var i in w.references) {
var li = createTiddlyElement2(ol,'li');
var prefix = w.tiddler ? w.tiddler.title + ':' : '';
var b = createTiddlyElement2(li,'b');
var a = createTiddlyElement2(b,'a');
li.id = prefix + '_note-' + i;
a.href = '#' + prefix + '_ref-' + i;
a.innerHTML = '^';
w.source = w.references[i].value;
w.nextMatch = 0;
w.subWikifyUnterm(li);
}
}
w.source = oldSource;
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
},
{
name: 'mediaWikiRepeatReference',
match: '<ref[^/]*/>',
lookaheadRegExp: /<ref(\s+(?:.*?)=["'](?:.*?)["'])?\s*\/>/mg,
handler: function(w)
{
if(config.browser.isIE) {
refRegExp = /<ref.*?\/>/mg;
refRegExp.lastIndex = w.matchStart;
var refMatch = refRegExp.exec(w.source);
if(refMatch && refMatch.index == w.matchStart) {
w.nextMatch = refRegExp.lastIndex;
return;
}
}
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var x = {id:'',value:''};
w.nextMatch = this.lookaheadRegExp.lastIndex;
var s = createTiddlyElement(w.output,"sup",null,"reference");
var a = createTiddlyElement2(s,"a");
var prefix = w.tiddler ? w.tiddler.title : '';
if(lookaheadMatch[1]) {
var r = {};
r = MediaWikiFormatter.setFromParams(w,lookaheadMatch[1]);
var name = r.name ? r.name.trim() : '';
name = name.replace(/ /g,'_');
s.id = prefix + '_ref-' + name +'_' + (w.referenceCount+1);
var count = w.references && w.references[name] ? (w.references[name].id+1) : '?';
}
a.href = '#' + prefix + '_note-' + name;
a.innerHTML = '['+count+']';
a.title = name;
}
}//# end handler
},
{
name: 'mediaWikiHtmlEntitiesEncoding',
match: '&#?[a-zA-Z0-9]{2,8};',
handler: function(w)
{
if(!config.browser.isIE)
createTiddlyElement(w.output,"span").innerHTML = w.matchText;
}
},
{
name: 'mediaWikiComment',
match: '<!\\-\\-',
lookaheadRegExp: /<!\-\-((?:.|\n)*?)\-\->/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiIncludeOnly',
match: '<includeonly>',
lookaheadRegExp: /<includeonly>((?:.|\n)*?)<\/includeonly>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiNoWiki',
match: '<nowiki>',
lookaheadRegExp: /<nowiki>((?:.|\n)*?)<\/nowiki>/mg,
element: 'span',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiPreNoWiki',
match: '<pre>\s*<nowiki>',
lookaheadRegExp: /<pre>\s*<nowiki>((?:.|\n)*?)<\/nowiki>\s*<\/pre>/mg,
element: 'pre',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiPre',
match: '<pre>',
lookaheadRegExp: /<pre>((?:.|\n)*?)<\/pre>/mg,
element: 'pre',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiMagicWords',
match: '__',
lookaheadRegExp: /__([A-Z]*?)__/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
if(lookaheadMatch[1]=='NOTOC') {
} else if(config.options.chkDisplayMediaWikiMagicWords) {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiGallery',
match: '<gallery>',
lookaheadRegExp: /[Ii]mage:(.*?)\n/mg,
handler: function(w)
{
var table = createTiddlyElement(w.output,'table',null,'gallery');
table.cellspacing = '0';
table.cellpadding = '0';
var rowElem = createTiddlyElement2(table,'tr');
var col = 0;
this.lookaheadRegExp.lastIndex = w.matchStart;
var nM = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
var oldSource = w.source;
while(lookaheadMatch) {
nM += lookaheadMatch[1].length;
w.source = lookaheadMatch[1] +']]';//!! ]] is hack until getParams is working
w.nextMatch = 0;
var params = MediaWikiFormatter.getParams(w);
var src = params[1];
src = src.trim().replace(/ /mg,'_');
src = src.substr(0,1).toUpperCase() + src.substring(1);
var palign = 'right';
var psrc = '120px-'+src;
var px = 120;
var pframed = false;
ptitle = null;
for(var i=2;i<params.length;i++) {
var p = params[i];
if(p=='right'||p=='left'||p=='center'||p=='none') {
palign = p;
} else if(p=='framed') {
pframed = true;
} else if(/\d{1,4}px/.exec(p)) {
px = p.substr(0,p.length-2).trim();
psrc = px + 'px-' + src;
} else {
ptitle = p;
}
}//#end for
var td = createTiddlyElement2(rowElem,'td');
var gb = createTiddlyElement(td,'div',null,'gallerybox');
var t = createTiddlyElement(gb,'div',null,'thumb');
t.style['padding'] = '26px 0';
var a = createTiddlyElement2(t,'a');
if(config.options.chkMediaWikiDisplayEnableThumbZoom) {
a.href = src;
}
a.title = ptitle;
var img = createTiddlyElement2(a,'img');
img.src = psrc;
img.width = px;
img.alt = '';
var gt = createTiddlyElement(gb,'div',null,'gallerytext');
p = createTiddlyElement2(gt,'p');
var oldSource2 = w.source; var oldMatch = w.nextMatch;
w.source = ptitle; w.nextMatch = 0;
w.subWikifyUnterm(p);
w.source = oldSource2; w.nextMatch = oldMatch;
col++;
if(col>3) {
rowElem = createTiddlyElement2(table,'tr');
col = 0;
}
w.source = oldSource;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
w.nextMatch = nM + '<gallery>'.length*2+1+'Image:'.length;//!! hack
}
},
{
name: 'mediaWikiHtmlTag',
match: "<[a-zA-Z]{2,}(?:\\s*(?:(?:.*?)=[\"']?(?:.*?)[\"']?))*?>",
lookaheadRegExp: /<([a-zA-Z]{2,})((?:\s+(?:.*?)=["']?(?:.*?)["']?)*?)?\s*(\/)?>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e =createTiddlyElement2(w.output,lookaheadMatch[1]);
if(lookaheadMatch[2]) {
MediaWikiFormatter.setAttributesFromParams(e,lookaheadMatch[2]);
}
if(lookaheadMatch[3]) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
} else {
w.subWikify(e,'</'+lookaheadMatch[1]+'>');
}
}
}
}
];
config.parsers.mediawikiFormatter = new Formatter(config.mediawiki.formatters);
config.parsers.mediawikiFormatter.format = 'mediawiki';
config.parsers.mediawikiFormatter.formatTag = 'MediaWikiFormat';
} //# end of 'install only once'
//}}}
Extracted from tiddlywiki.org
/***
|''Name:''|MediaWikiAdaptorPlugin|
|''Description:''|Adaptor for moving and converting data from MediaWikis|
|''Author:''|Martin Budden (mjbudden (at) gmail (dot) com)|
|''Source:''|http://www.martinswiki.com/#MediaWikiAdaptorPlugin |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/adaptors/MediaWikiAdaptorPlugin.js |
|''Version:''|0.8.14|
|''Date:''|Jul 27, 2007|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''~CoreVersion:''|2.4.1|
|''Max number of tiddlers to download''|<<option txtMediaWikiAdaptorLimit>>|
MediaWiki REST documentation is at:
http://meta.wikimedia.org/w/api.php
http://meta.wikimedia.org/w/query.php
''For debug:''
|''Default MediaWiki username''|<<option txtMediaWikiUsername>>|
|''Default MediaWiki password''|<<option txtMediaWikiPassword>>|
***/
//{{{
if(!config.options.txtMediaWikiUsername)
{config.options.txtMediaWikiUsername = '';}
if(!config.options.txtMediaWikiPassword)
{config.options.txtMediaWikiPassword = '';}
//}}}
//{{{
//# Ensure that the plugin is only installed once.
if(!config.adaptors.mediawiki) {
if(config.options.txtMediaWikiAdaptorLimit == undefined)
{config.options.txtMediaWikiAdaptorLimit = '500';}
//#config.adaptors.MediaWikiAdaptor.loadMissingTiddler = Story.prototype.loadMissingTiddler;
//#Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
//#{
//# var i = title.indexOf('#');
//# if(i!=-1) {
//# title = title.substr(0,i);
//# }
//# config.adaptors.MediaWikiAdaptor.loadMissingTiddler.apply(this,arguments);
//#};
config.adaptors.mediawiki = function() {};
(function(adaptor) {
adaptor.prototype = new AdaptorBase();
adaptor.serverType = 'mediawiki';
adaptor.serverParsingErrorMessage = "Error parsing result from server";
adaptor.errorInFunctionMessage = "Error in function MediaWikiAdaptor.%0";
adaptor.doHttpGET = function(uri,callback,params,headers,data,contentType,username,password)
{
return httpReq('GET',uri,callback,params,headers,data,contentType,username,password);
};
adaptor.doHttpPOST = function(uri,callback,params,headers,data,contentType,username,password)
{
return httpReq('POST',uri,callback,params,headers,data,contentType,username,password);
};
adaptor.minHostName = function(host)
{
return host ? host.replace(/^http:\/\//,'').replace(/\/$/,'') : '';
};
adaptor.normalizedTitle = function(title)
{
var n = title.charAt(0).toUpperCase() + title.substr(1);
return n.replace(/\s/g,'_');
};
adaptor.dateFromTimestamp = function(timestamp)
// Convert a MediaWiki timestamp in ISO 8601 (YYYY-MM-DDThh:mm:ssZ) format into a JavaScript Date object
{
var dt = timestamp;
return new Date(Date.UTC(dt.substr(0,4),dt.substr(5,2)-1,dt.substr(8,2),dt.substr(11,2),dt.substr(14,2)));
};
adaptor.anyChild = function(obj)
//# convenience function for getting children whose keys are unknown
//# such as children of pages subobjects, whose keys are numeric page ids
{
for(var key in obj) {
if(typeof obj[key]!='function')
return obj[key];
}
return null;
};
//#
//#api.php ? action=login & lgname=Bob & lgpassword=secret
//#<?xml version="1.0" encoding="utf-8"?>
//#<api>
//# <login
//# result="Success"
//# lguserid="12345"
//# lgusername="Bob"
//# lgtoken="b5780b6e2f27e20b450921d9461010b4"
//# cookieprefix="enwiki"
//# sessionid="08nj1ioefhlvmdjfor5to3mvv5"
//# />
//#</api>
adaptor.prototype.complete = function(context,fn)
{
context.complete = fn;
var ret;
if(context.sessionToken) {
ret = context.complete(context,context.userParams);
} else {
ret = this.login(context);
}
return ret;
};
adaptor.prototype.login = function(context)
{
//#console.log('login',context);
//#context = this.setContext(context,userParams,callback);
var host = this.fullHostName(context.host);
var uriTemplate = '%0/api.php?action=login&format=json&lgname=%1&lgpassword=%2';
var uri = uriTemplate.format([host,escape(config.options.txtMediaWikiUsername),escape(config.options.txtMediaWikiPassword)]);
//#console.log('uri:'+uri);
var req = adaptor.doHttpPOST(uri,adaptor.loginCallback,context,{"Content-Length":"1"}," ");
//#console.log('req:'+req);
return typeof req == 'string' ? req : true;
};
//#{
//# "query": {
//# "pages" : {
//# "5982813": {
//# "pageid":5982813,
//# "ns":0,
//# "title":"MainPage",
//# "touched":"2008-05-07T05:22:48Z",
//# "lastrevid":64058732,
//# "counter":0,
//# "length":22,
//# "redirect":"",
//# "new":"",
//# "edittoken":"19f47e1ab9dc35ebc065e9cdf4a49516+\\",
//# "revisions":[{"revid":64058732,"user":"Ilingod","timestamp":"2006-07-16T03:06:46Z","comment":"Redirecting to [[Main Page]]"}]}
//#}
//#}}
//#{"login":{"result":"Success","lguserid":11,"lgusername":"MartinBudden","lgtoken":"9fa40aeb51e083cf4dc0d0f4909c01e3"}}
adaptor.loginCallback = function(status,context,responseText,uri,xhr)
{
//#console.log('loginCallback:'+status);
if(status) {
try {
eval('var info=' + responseText);
} catch (ex) {
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.complete)
context.complete(context,context.userParams);
return;
}
context.status = true;
context.sessionToken = info.login.lgtoken;
if(context.complete)
context.complete(context,context.userParams);
} else {
context.status = false;
context.statusText = xhr.statusText;
if(context.callback)
context.callback(context,context.userParams);
}
};
adaptor.getWorkspaceId = function(workspace)
{
var workspaces = {
"media": -2, "special":-1,
"":0, "talk":1,"user":2,"user talk":3,"meta":4,"meta talk":5,"image":6,"image talk":7,
"mediawiki":8,"mediawiki talk":9,"template":10,"template talk":11,"help":12,"help talk":13,
"category":14,"category talk":15};
workspace = workspace.toLowerCase();
var id = workspaces[workspace];
if(!id) {
if(workspace=="" || workspace=="main")
id = 0;
else if(workspace.lastIndexOf("talk") != -1)
id = 5;
else
id = 4;
}
return id;
};
adaptor.prototype.openWorkspace = function(workspace,context,userParams,callback)
{
if(!workspace)
workspace = "";
//#console.log("openWorkspace:"+workspace);
this.workspace = workspace;
this.workspaceId = null;
context = this.setContext(context,userParams,callback);
if(workspace) {
if(context.workspaces) {
for(var i=0;i<context.workspaces.length;i++) {
if(context.workspaces[i].name == workspace) {
this.workspaceId = context.workspaces[i].id;
break;
}
}
} else {
workspace = workspace.toLowerCase();
this.workspaceId = adaptor.getWorkspaceId(workspace);
}
}
if(!this.workspaceId) {
if(workspace=="" || workspace.toLowerCase()=="main")
this.workspaceId = 0;
else if(workspace.lastIndexOf("talk") != -1)
this.workspaceId = 5;
else
this.workspaceId = 4;
}
//#console.log("workspaceId:"+this.workspaceId);
if(context.callback) {
context.status = true;
window.setTimeout(function() {callback(context,userParams);},0);
}
return true;
};
adaptor.prototype.getWorkspaceList = function(context,userParams,callback)
{
context = this.setContext(context,userParams,callback);
//#console.log("getWorkspaceList");
//# http://meta.wikimedia.org/w/api.php?&action=query&meta=siteinfo&siprop=namespaces
//# http://wikipedia.org/w/api.php?&action=query&meta=siteinfo&siprop=namespaces
//# http://wiki.unamesa.org/api.php?&action=query&meta=siteinfo&siprop=namespaces
//# http://tiddlywiki.org/api.php?&action=query&meta=siteinfo&siprop=namespaces
if(context.workspace) {
//#console.log("w:"+context.workspace);
context.status = true;
context.workspaces = [{name:context.workspace,title:context.workspace}];
if(context.callback)
window.setTimeout(function() {callback(context,userParams);},0);
return true;
}
var uriTemplate = '%0/api.php?format=json&action=query&meta=siteinfo&siprop=namespaces';
var uri = uriTemplate.format([context.host]);
//#console.log("uri:"+uri);
var req = adaptor.doHttpGET(uri,adaptor.getWorkspaceListCallback,context);
return typeof req == 'string' ? req : true;
};
//#{
//# "query": {
//# "namespaces": {
//# "-2": {"id": -2,"*": "Media"},
//# "-1": {"id": -1,"*": "Special"},
//# "0": {"id": 0,"*": ""},
//# "1": {"id": 1,"*": "Talk"},
//# "2": {"id": 2,"*": "User"},
//# "3": {"id": 3,"*": "User talk"},
//# "4": {"id": 4,"*": "Meta"}, //or Wikipedia or UnaMesa
//# "5": {"id": 5,"*": "Meta talk"}, // or Wikipedia talk or UnaMesa talk
//# "6": {"id": 6,"*": "Image"},
//# "7": {"id": 7,"*": "Image talk"},
//# "8": {"id": 8,"*": "MediaWiki"},
//# "9": {"id": 9,"*": "MediaWiki talk"},
//# "10": {"id": 10,"*": "Template",
//# "11": {"id": 11,"*": "Template talk"},
//# "12": {"id": 12,"*": "Help"},
//# "13": {"id": 13,"*": "Help talk"},
//# "14": {"id": 14,"*": "Category"},
//# "15": {"id": 15,"*": "Category talk"}
//# }
//# }
//# }
//#}
adaptor.getWorkspaceListCallback = function(status,context,responseText,uri,xhr)
{
//#console.log("getWorkspaceListCallback:"+status);
//#console.log(responseText);
context.status = false;
if(status) {
try {
eval('var info=' + responseText);
} catch (ex) {
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.callback)
context.callback(context,context.userParams);
return;
}
var namespaces = info.query.namespaces;
var list = [];
for(var i in namespaces) {
var item = {};
item.id = namespaces[i]['id'];
item.title = namespaces[i]['*'];
item.name = item.title;
list.push(item);
}
context.workspaces = list;
context.status = true;
} else {
context.statusText = xhr.statusText;
context.statusCode = xhr.status;
}
if(context.callback)
context.callback(context,context.userParams);
};
adaptor.prototype.getTiddlerList = function(context,userParams,callback,filter)
// get a list of the tiddlers in the current workspace
{
context = this.setContext(context,userParams,callback);
if(!context.filter)
context.filter = filter;
//#console.log('getTiddlerList');
//# http://meta.wikimedia.org/w/api.php?action=query&list=allpages&aplimit=5&format=jsonfm
//# http://www.wikipedia.org/w/api.php?action=query&list=categorymembers&cmtitle=wiki
//# http://www.wikipedia.org/w/api.php?action=query&list=embeddedin&titles=Template:IPstack&eilimit=50&format=jsonfm
//# http://www.wikipedia.org/w/api.php?action=query&list=allpages&aplimit=50&format=jsonfm
//# http://www.wikipedia.org/w/query.php?what=category&cptitle=Wiki&format=jsonfm
//# http://wiki.unamesa.org/api.php?action=query&list=allpages&apnamespace=10&aplimit=50&format=jsonfm
//# http://tiddlywiki.org/api.php?action=query&list=allpages&format=jsonfm
//# http://tiddlywiki.org/api.php?action=query&list=allpages&aplimit=50&format=jsonfm
context.tiddlers = [];
context.uri = null;
var host = this.fullHostName(context.host);
if(!context.tiddlerLimit)
context.tiddlerLimit = !config.options.txtMediaWikiAdaptorLimit ? config.maxTiddlerImportCount : config.options.txtMediaWikiAdaptorLimit;
context.tiddlerLimit = parseInt(context.tiddlerLimit,10);
var limit = context.tiddlerLimit;
if(limit>500)
limit = 500;
filter = context.filter;
if(host.indexOf('wikipedia.org')!=-1) {
//# if from wikipedia and no filter, then filter on featured articles
if(!filter) {
filter = '[template[Featured_article]]';
}
}
if(filter) {
var re = /\[(\w+)\[([ \w\.\:]+)\]\]/;
var match = re.exec(filter);
if(match) {
var filterParams = adaptor.normalizedTitle(match[2]);
switch(match[1]) {
case 'tag':
//#context.responseType = 'pages';
//#var uriTemplate = '%0/query.php?format=json&what=category&cpnamespace=%1&cplimit=%2&cptitle=%3';
context.responseType = 'query.categorymembers';
var uriTemplate = '%0/api.php?format=json&action=query&list=categorymembers&cmnamespace=%1&cmlimit=%2&cmtitle=Category:%3';
break;
case 'template':
context.responseType = 'query.embeddedin';
uriTemplate = '%0/api.php?format=json&action=query&list=embeddedin&einamespace=%1&eititle=Template:%3';
if(limit)
uriTemplate += '&eilimit=%2';
break;
case 'wikipedia':
context.responseType = 'query.embeddedin';
uriTemplate = '%0/api.php?format=json&action=query&list=embeddedin&einamespace=1&eititle=Wikipedia:%3';
if(limit)
uriTemplate += '&eilimit=%2';
break;
default:
break;
}
} else {
var params = filter.parseParams('anon',null,false);
for(var i=1; i<params.length; i++) {
var tiddler = new Tiddler(params[i].value);
tiddler.fields.workspaceId = this.workspaceId;
context.tiddlers.push(tiddler);
}
context.status = true;
if(context.callback)
window.setTimeout(function() {callback(context,userParams);},0);
return true;
}
} else {
//#context.responseType = 'query.allpages';
//#uriTemplate = '%0/api.php?format=json&action=query&list=allpages&apfilterredir=nonredirects&apfrom=%4&prop=info';
context.responseType = 'query.pages';
uriTemplate = '%0/api.php?format=json&action=query&generator=allpages&gapfilterredir=nonredirects&gapfrom=%4&prop=info';
if(this.workspaceId != 0)
uriTemplate += '&gapnamespace=%1';
if(limit) {
uriTemplate += '&gaplimit=%2';
context.gaplimit = limit;
}
context.count = 0;
context.uri = uriTemplate.format([host,this.workspaceId,limit,filterParams,'%0']);
context.urifrom = 'gapfrom';
//#console.log('context.uri:'+context.uri);
}
var from = '0';
var uri = uriTemplate.format([host,this.workspaceId,limit,filterParams,from]);
//#console.log('uri: '+uri);
var req = adaptor.doHttpGET(uri,adaptor.getTiddlerListCallback,context);
//#displayMessage("req:"+req);
return typeof req == 'string' ? req : true;
};
//#{
//# "query-continue": {
//# "allpages": {
//# "apfrom": "!Xu"
//# }
//# },
//# "query": {
//# "allpages": {
//# "5878274": {
//# "pageid": 5878274,
//# "ns": 0,
//# "title": "!"
//# },
//# "5197186": {
//# "pageid": 5197186,
//# "ns": 0,
//# "title": "!Xoong language"
//# }
//# }
//# }
//#}
//#{
//# "query": {
//# "embeddedin": [
//# {
//# "pageid": 791,
//# "ns": 0,
//# "title": "Asteroid"
//# },
//# {
//# "pageid": 5962,
//# "ns": 0,
//# "title": "Comet"
//# },
adaptor.getTiddlerListCallback = function(status,context,responseText,uri,xhr)
{
//#console.log('getTiddlerListCallback status:'+status);
//#console.log(context);
//#console.log(context.responseType);
//#console.log(responseText.substr(0,400));
context.status = false;
context.statusText = adaptor.errorInFunctionMessage.format(['getTiddlerListCallback']);
if(status) {
try {
//# convert the downloaded data into a javascript object
eval('var info=' + responseText);
var pages;
if(context.responseType == 'query.embeddedin')
pages = info.query.embeddedin;
else if(context.responseType == 'query.categorymembers')
pages = info.query.categorymembers;
else if(context.responseType == 'query.allpages')
pages = info.query.allpages;
else if(context.responseType == 'query.pages')
pages = info.query.pages;
else
pages = info.pages;
var c = null;
if(info['query-continue']) {
if(info['query-continue'].allpages) {
c = adaptor.normalizedTitle(info['query-continue'].allpages[context.urifrom]);
context.count += context.gaplimit;
if(context.count>=context.tiddlerLimit)
c = null;
}
}
var useMain = false;
if(context.workspace=="Talk" && context.filter)
useMain = true;
for(i in pages) {
var title = pages[i].title;
if(useMain&&title)
title = title.replace(/^Talk:/g,"");
if(title && !store.isShadowTiddler(title)) {
//# avoid overwriting shadow tiddlers
tiddler = new Tiddler(title);
tiddler.fields.workspaceId = useMain ? 0 : pages[i].ns;
if(!useMain)
tiddler.fields['temp.size'] = pages[i].length;
context.tiddlers.push(tiddler);
}
}
} catch (ex) {
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.callback)
context.callback(context,context.userParams);
return;
}
context.status = true;
} else {
context.statusText = xhr.statusText;
}
if(context.uri && c) {
var u = context.uri.format([c]);
var req = adaptor.doHttpGET(u,adaptor.getTiddlerListCallback,context);
} else {
if(context.callback)
context.callback(context,context.userParams);
}
};
adaptor.prototype.generateTiddlerInfo = function(tiddler)
{
//# http://tiddlywikiguides.org/index.php?title=AutoTaggerPlugin
//# http://en.wikipedia.org/wiki/Mars
var info = {};
var host = this && this.host ? this.host : tiddler.fields['server.host'];
host = this.fullHostName(host);
if(host.match(/w\/$/)) {
host = host.replace(/w\/$/,'');
var uriTemplate = '%0wiki/%2';
} else {
uriTemplate = '%0/index.php?title=%2';
}
info.uri = uriTemplate.format([host,this.workspace,tiddler.title]);
return info;
};
adaptor.prototype.getTiddlerRevision = function(title,revision,context,userParams,callback)
{
context = this.setContext(context,userParams,callback);
context.revision = revision;
return this.getTiddler(title,context,userParams,callback);
};
adaptor.prototype.getTiddler = function(title,context,userParams,callback)
{
context = this.setContext(context,userParams,callback);
context.title = title;
//# console.log('adaptor.getTiddler:'+context.title+" revision:"+context.revision+" workspace:"+context.workspace);
//# http://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles=Elongation&rvprop=content
//# http://meta.wikimedia.org/w/api.php?format=jsonfm&action=query&prop=revisions&titles=Main%20Page&rvprop=content|timestamp|user
//# http://www.tiddlywiki.org/api.php?action=query&prop=revisions&titles=Main%20Page&rvprop=content
//# http://wiki.unamesa.org/api.php?format=jsonfm&action=query&prop=revisions&titles=Main%20Page&rvprop=content|timestamp|user
var host = this.fullHostName(context.host);
var uriTemplate = '%0/api.php?format=json&action=query&prop=revisions&titles=%1&rvprop=content|timestamp|user|ids';
if(context.revision)
uriTemplate += '&rvstartid=%2&rvlimit=1';
var uri = uriTemplate.format([host,adaptor.normalizedTitle(context.title),context.revision]);
//#console.log('uri: '+uri);
context.tiddler = new Tiddler(context.title);
context.tiddler.fields.wikiformat = 'mediawiki';
context.tiddler.fields['server'] = null;
context.tiddler.fields['server.host'] = adaptor.minHostName(host);
var req = adaptor.doHttpGET(uri,adaptor.getTiddlerCallback,context);
//#console.log('req:'+req);
return typeof req == 'string' ? req : true;
};
//#{
//# "query": {
//# "pages": {
//# "12631": {
//# "pageid": 12631,
//# "ns": 0,
//# "title": "Main Page",
//# "revisions": {
//# "528206": {
//# "timestamp": "2007-06-09T22:45:35Z",
//# "revid": 528206,
//# "pageid": 12631,
//# "oldid": 524243,
//# "minor": "",
//# "*": "{| width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"border:1px solid #ffcc00; background:#FFFDDF; padding-left:0.5em; padding-right:0.5em; padding-top:0.3em; padding-bottom:0.1em;\"\n|-\n| width=\"27%\" align=\"center\" | <font style=\"font-family:Verdana,Arial,Helvetica;\"><big>'''[[Wikimedia projects|Projects]]'''<\/big><\/font><br\/>\n''[[Complete list of Wikimedia projects|Complete list]]'' | ''[[Proposals for new projects|Proposals]]''\n| width=\"42%\" align=center | <font style=\"font-family:Verdana,Arial,Helvetica;\"><big>'''[[Board of Trustees]]'''<\/big><\/font><br\/> \n'''[[Wikimedia:Home|Wikimedia Foundation]]''' | '''[[Wikimedia_meetings|Meetings]]''' <!--| '''[[Election results 2006|Election]]'''-->\n| align=center | <font style=\"font-family:Verdana,Arial,Helvetica;\"><big>'''[[Translation requests|Translations]]'''<\/big><\/font><br\/> \n''[[Translation_requests\/WMF|Foundation]]'' | ''[[Translation request\/WMF\/Fundraising\/2006|Donation form]]'' | ''[[Wikimedia press releases|press releases]]'' <!--\n| ''[[Translation_requests\/Wikimania|Wikimania]]''-->\n|}\n{| width=\"100%\" cellspacing=12 cellpadding=0\n| '''Welcome''' to [[Meta:About|Meta-Wiki]], a website devoted to the coordination of the [[Wikimedia Foundation]]'s projects, including [[Wikipedia]], the free encyclopedia, and the [[MediaWiki]] software on which it runs. Other venues for discussing the Foundation and these projects include the Wikimedia [http:\/\/mail.wikimedia.org mailing lists] (particularly [http:\/\/mail.wikipedia.org\/mailman\/listinfo\/foundation-l '''foundation-l''']) and the various [[IRC channels]].\n|align=\"right\"| <small>Content pages on Meta: '''{{NUMBEROFARTICLES}}'''<\/small><br \/>\n|}\n{| cellpadding=0 cellspacing=1\n|- valign=\"top\"\n|style=\"border: 1px solid gray;padding-left:1em;padding-right:0.5em;background:#FFEFF0;\" class=\"plainlinks\" colspan=\"2\"|\n\n<span style=\"font-size: 130%;\">'''[[Multilingualism|Meta in many languages]]'''<\/span><br\/>\n''<small>[[Template:MetaHomePages|Edit this list:]]<\/small>'' <small>{{MetaHomePages}}<\/small>\n|- valign=\"top\"\n|style=\"border: 1px solid gray;padding-left:1em;padding-right:0.5em;background:#E4FFDF;padding-bottom:0.5em;\" width=\"45%\"|\n==Meta utilities==\n\n===Requests for...===\n* [[Requests for permissions|Permissions]] (to request sysop, bureaucrat and checkuser status on any Wikimedia wiki)\n* [[Requests for bot status|Bot status]]\n* [[Requests for queries|SQL queries]] \n* [[Requests for CheckUser information|CheckUser queries]] (not [[Checkuser#Access|CheckUser access]])\n* [[Meta:Requests for deletion|Deletion]] \/ [[Meta:Requests for undeletion|Undeletion]] \/ [[:Category:Deleteme|Speedy deletion]] \/ [[Multilingual speedy deletions]]\n* [[Translation requests|Translation]]\n* [[Requests for logos|Logos]]\n\n\n===Other tools===\n\n* [[Meta:Sandbox|Sandbox]]\n* [[Meta:Babel templates|Babel templates]] (language skill)\n* [[Transbabel]] templates (translators by language combo)\n* [[Meta:Categories|Browse Meta-Wiki by category]]\n\n===Form & Content===\n\nOrganize and prepare content, e.g. templates, language files, logos, formats; Copyright issues<br\/>\n''See [[Wikimedia content]]''\n* [[Help:Images and other uploaded files|Image]]\n* [[Maps]]\n* [[Copyright]]\n* [[Statistics]]\n\n|valign=\"top\" bgcolor=\"#E8F1FF\" style=\"border-style:solid;border-width:1px;border-color:gray;padding-left:1em;padding-right:0.5em; padding-bottom:0.5em;\" width=\"55%\"|\n==Latest news==\n{{Information thread}}\n\n|- valign=\"top\"\n|colspan=\"2\" style=\"border: 1px solid gray;padding-left:1em;padding-right:0.5em;padding-bottom:0.5em;\"|\n==Wikimedia Foundation==\n\n{{Wikimedia Foundation}}\n|- valign=\"top\"\n|colspan=\"2\" style=\"border: 1px solid gray;padding-left:1em;padding-right:0.5em;padding-bottom:0.5em;\"|\n==Code & technical issues==\nCoordination of the development process, maintenance of servers, and user guide for MediaWiki.<br \/>\n{{MediaWiki links|param=width=\"30%\"}}\n|- valign=\"top\"\n|style=\"border: 1px solid gray;padding-left:1em;padding-right:0.5em;padding-bottom:0.5em;\" width=\"40%\"|\n==Community & Communication==\nAbout the community itself. Organisation of events; philosophical discussions; collaborated essays.\n\n* [[The Wikipedia Community]]\n* [[Wikipedians categorized by sub-cultural affiliation ]]\n* [[Meta:Babel]] (central discussion place here)\n* [[Wikimedia Embassy]] (local contacts)\n* [[We need your help|Requests for help]] to your trouble\n* [[Mailing list|Mailing Lists]] & [[IRC Channels]]\n* [[Wikipedia meetup]] (meetings between participants)\n* [[Status]] (various types of status, sysop, developer...)\n\n|valign=\"top\" style=\"border: 1px solid gray;padding-left:1em;padding-right:0.5em;padding-bottom:0.5em;\"|\n==Core issues & collaboration==\n\nHelping contribute and collaborate (i.e., what makes it easy, what makes it hard, how to do it well, why you have to, what conflicts typically arise, fixing them). Discussing and formulating project-wide (i.e. not language-specific) policies.\n\n* [[Transfer of authority]]\n* [[Wikimedia principles]] (wikiquette, consensus, NPOV, copyrights)\n* [[Growing Wikimedia]] (building the project and the [[community]])\n* [[Power structure|Wikimedia power structure]]\n* [[Interlingual coordination]] (various international issues)\n* [[Wikipedia policies]]\n* [[Conflict resolution]]\n* [[Privacy policy]]\n|}\n__NOTOC__\n__NOEDITSECTION__\n[[Category:Main page]]"
//# }
//# }
//# }
//# }
//# }
//#}
//# Override this to do postprocessing on tiddler after it is retrieved from the server
adaptor.prototype.getTiddlerPostProcess = function(context)
{
return context.tiddler;
};
adaptor.getTiddlerCallback = function(status,context,responseText,uri,xhr)
{
//#console.log('getTiddlerCallback status:'+status);
//#console.log('tiddler:'+context.tiddler.title);
//#console.log(responseText);
context.status = false;
if(status) {
var content = null;
try {
//# convert the downloaded data into a javascript object
eval('var info=' + responseText);
var page = adaptor.anyChild(info.query.pages);
var revision = adaptor.anyChild(page.revisions);
var text = revision['*'];
context.tiddler.fields['server.page.revision'] = String(revision['revid']);
context.tiddler.fields['server.page.timestamp'] = String(revision['timestamp']);
var host = context.tiddler.fields['server.host'];
if(host.indexOf('wikipedia')==-1) {
context.tiddler.modified = adaptor.dateFromTimestamp(revision['timestamp']);
context.tiddler.modifier = revision.user;
} else {
// content is from wikipedia
//# set dates to verion date to avoid them being saved to file
context.tiddler.created = version.date;
context.tiddler.modified= version.date;
// remove links to other language articles
text = text.replace(/\[\[[a-z\-]{2,12}:(?:.*?)\]\](?:\r?)(?:\n?)/g,'');
}
context.tiddler.text = text;
//# convert categories into tags
var catRegExp = /\[\[(Category:[^|\]]*?)\]\]/mg;
var tags = '';
var delim = '';
catRegExp.lastIndex = 0;
var match = catRegExp.exec(text);
while(match) {
tags += delim;
if(match[1].indexOf(' ')==-1)
tags += match[1];
else
tags += '[[' + match[1] + ']]';
delim = ' ';
match = catRegExp.exec(text);
}
context.tiddler.tags = tags.readBracketedList();
context.tiddler = context.adaptor.getTiddlerPostProcess.call(context.adaptor,context);
} catch (ex) {
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.callback)
context.callback(context,context.userParams);
return;
}
context.status = true;
} else {
context.statusText = xhr.statusText;
}
if(context.callback)
context.callback(context,context.userParams);
};
//#{
//# "query": {
//# "pages": {
//# "12631": {
//# "pageid": 12631,
//# "ns": 0,
//# "title": "Main Page",
//# "revisions": {
//# "528206": {
//# "revid": 528206,
//# "pageid": 12631,
//# "oldid": 524243,
//# "user": "Aphaia",
//# "minor": "",
//# "timestamp": "2007-02-11T04:55:56Z"
//# },
//# "525695": {
//# "revid": 525695,
//# "pageid": 12631,
//# "oldid": 521762,
//# "user": "Alex43223",
//# "timestamp": "2007-02-06T23:24:05Z",
//# "comment": "Fixing links to bypass redirects"
//# }
//# }
//# }
//# }
//# },
//# "query-continue": {
//# "revisions": {
//# "rvstartid": "515076"
//# }
//# }
//#}
adaptor.prototype.getTiddlerRevisionList = function(title,limit,context,userParams,callback)
// get a list of the revisions for a tiddler
{
context = this.setContext(context,userParams,callback);
//#console.log('getTiddlerRevisionList:'+title+" lim:"+limit);
//# http://meta.wikimedia.org/w/api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment
//# http://meta.wikimedia.org/w/api.php?format=jsonfm&action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment
var uriTemplate = '%0/api.php?format=json&action=query&prop=revisions&titles=%1&rvlimit=%2&rvprop=ids|flags|timestamp|user|comment';
if(!limit)
limit = 5;
var host = this.fullHostName(context.host);
var uri = uriTemplate.format([host,adaptor.normalizedTitle(title),limit]);
//#console.log('uri: '+uri);
var req = adaptor.doHttpGET(uri,adaptor.getTiddlerRevisionListCallback,context);
//#displayMessage("req:"+req);
return typeof req == 'string' ? req : true;
};
adaptor.getTiddlerRevisionListCallback = function(status,context,responseText,uri,xhr)
{
//#console.log('getTiddlerRevisionListCallback status:'+status);
//#console.log(responseText.substr(0,1000));
context.status = false;
if(status) {
var content = null;
try {
//# convert the downloaded data into a javascript object
eval('var info=' + responseText);
var page = adaptor.anyChild(info.query.pages);
var title = page.title;
var revisions = page.revisions;
var list = [];
for(var i=0;i<revisions.length;i++) {
var tiddler = new Tiddler(title);
tiddler.modified = adaptor.dateFromTimestamp(revisions[i].timestamp);
tiddler.modifier = revisions[i].user;
tiddler.fields.comment = revisions[i].comment;
tiddler.fields['server.page.id'] = adaptor.normalizedTitle(title);
tiddler.fields['server.page.name'] = title;
tiddler.fields['server.page.revision'] = String(revisions[i].revid);
list.push(tiddler);
}
context.revisions = list;
} catch (ex) {
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.callback)
context.callback(context,context.userParams);
return;
}
context.status = true;
} else {
context.statusText = xhr.statusText;
}
if(context.callback)
context.callback(context,context.userParams);
};
//# api.php ? action=edit
//# &title=Talk:Main_Page
//# §ion=new
//# &summary=Hello%20World
//# &text=Hello%20everyone!
//# &watch
//# &basetimestamp=2008-03-20T17:26:39Z
//# &token=cecded1f35005d22904a35cc7b736e18+\
//#<?xml version="1.0" encoding="utf-8"?>
//#<api>
//# <edit result="Success" pageid="12" title="Talk:Main Page" oldrevid="465" newrevid="471" />
//#</api>
adaptor.prototype.putTiddler = function(tiddler,context,userParams,callback)
{
//#console.log('putTiddler:'+tiddler.title);
context = this.setContext(context,userParams,callback);
context.tiddler = tiddler;
context.title = tiddler.title;
//# login if nececessary
return this.complete(context,adaptor.putTiddlerComplete);
};
adaptor.putTiddlerComplete = function(context,userParams)
{
//#console.log('putTiddlerComplete');
//# get an edit token
var uriTemplate = '%0/api.php?format=json&action=query&prop=info|revisions&intoken=edit&titles=%1';
var uri = uriTemplate.format([context.host,escape(adaptor.normalizedTitle(context.tiddler.title))]);
//#console.log('uri:'+uri);
var req = adaptor.doHttpGET(uri,adaptor.putTiddlerCallback,context);
//#console.log(req);
return typeof req == 'string' ? req : true;
};
adaptor.putTiddlerCallback = function(status,context,responseText,uri,xhr)
{
//#console.log('putTiddlerCallback:'+status);
//#console.log(responseText);
if(status) {
try {
eval('var info=' + responseText);
//#console.log('info',info);
var page = adaptor.anyChild(info.query.pages);
var token = page.edittoken;
token = token.substr(0,token.length-2) + '%2B%5C';
} catch (ex) {
//#console.log('exception',ex);
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.callback)
context.callback(context,context.userParams);
return;
}
context.status = true;
var uriTemplate = '%0/api.php?format=json&action=edit&title=%1&text=%2&basetimestamp=%3&token=%4';
var tiddler = context.tiddler;
var timestamp = tiddler.fields['server.page.timestamp'];
uri = uriTemplate.format([context.host,escape(adaptor.normalizedTitle(tiddler.title)),escape(tiddler.text),timestamp,token]);
//#console.log('uri:'+uri);
var req = adaptor.doHttpPOST(uri,adaptor.putTiddlerCallback2,context,{"Content-Length":"1"}," ","application/x-www-form-urlencoded");
//#console.log(req);
} else {
context.status = false;
context.statusText = xhr.statusText;
if(context.callback)
context.callback(context,context.userParams);
}
};
adaptor.putTiddlerCallback2 = function(status,context,responseText,uri,xhr)
{
//#console.log('putTiddlerCallback2:'+status);
//#console.log(xhr);
//#console.log(responseText);
var info;
context.statusText = xhr.statusText;
try {
eval('info=' + responseText + ';');
//#console.log('info2',info);
} catch(ex) {
//#console.log('ex',ex);
status = false;
context.status = false;
context.statusText = 'putTiddler exception';
}
if(status) {
context.status = true;
} else {
context.status = false;
}
if(info && info.error) {
context.status = false;
context.statusText = info.error.info;
}
if(context.callback)
context.callback(context,context.userParams);
};
//# placeholder, not complete
/*adaptor.prototype.deleteTiddler = function(tiddler,context,userParams,callback)
{
//#console.log('deleteTiddler:'+tiddler.title);
context = this.setContext(context,userParams,callback);
context.title = tiddler.title;
return this.complete(context,adaptor.deleteTiddlerComplete);
};*/
adaptor.deleteTiddlerComplete = function(context,userParams)
{
//#console.log('putTiddlerComplete');
//# get an edit token
var uriTemplate = '%0/api.php?format=json&action=query&prop=intoken=edit&titles=%1';
var uri = uriTemplate.format([context.host,escape(adaptor.normalizedTitle(context.tiddler.title))]);
//#console.log('uri:'+uri);
var req = adaptor.doHttpGET(uri,adaptor.deleteTiddlerCallback,context);
//#console.log(req);
return typeof req == 'string' ? req : true;
};
adaptor.deleteTiddlerCallback = function(context,userParams)
{
//#fnLog('deleteTiddlerComplete:'+title);
if(status) {
try {
eval('var info=' + responseText);
//#console.log('info',info);
var page = adaptor.anyChild(info.query.pages);
var token = page.edittoken;
token = token.substr(0,token.length-2) + '%2B%5C';
} catch (ex) {
//#console.log('exception',ex);
context.statusText = exceptionText(ex,adaptor.serverParsingErrorMessage);
if(context.callback)
context.callback(context,context.userParams);
return;
}
context.status = true;
var uriTemplate = '%0/api.php?action=delete&title=%1&token=%2';
var uri = uriTemplate.format([context.host,context.workspace,escape(adaptor.normalizedTitle(context.title)),token]);
//#fnLog('uri: '+uri);
var req = adaptor.doHttpPOST(uri,adaptor.deleteTiddlerCallback,context,{"Content-Length":"1"}," ");
//#fnLog("req:"+req);
} else {
context.status = false;
context.statusText = xhr.statusText;
if(context.callback)
context.callback(context,context.userParams);
}
};
adaptor.deleteTiddlerCallback2 = function(status,context,responseText,uri,xhr)
{
//#fnLog('deleteTiddlerCallback:'+status);
//#fnLog('rt:'+responseText.substr(0,50));
//#fnLog('xhr:'+xhr);
if(status) {
context.status = true;
} else {
context.status = false;
context.statusText = xhr.statusText;
}
if(context.callback)
context.callback(context,context.userParams);
};
})(config.adaptors.mediawiki);
} // end of 'install only once'
//}}}