Note: An alternative skin, TW4MW (work in progress), is available from the user preferences.
Dev:Best Practices
TiddlyWiki Community Wiki
Contents |
[edit] Metadata
[edit] Code Structure
[edit] Namespacing
Plugins' functions should be registered in the config.extensions namespace (built in from v2.5):
config.extensions.SamplePlugin = { sampleFunction: function() { /* ... */ } };
Similarly, functions specific to a certain macro or toolbar command should be attached to the config.macros or config.commands object, respectively:
config.macros.SampleMacro = { handler: function(place, macroName, params, wikifier, paramString, tiddler) { /* ... */ } }; config.commands.sampleCommand = { handler: function(event, src, title) { /* ... */ } };
Overview of common TiddlyWiki namespaces:
-
config.extensions: Plugins -
config.macros: Macros -
config.commands: Toolbar Commands -
config.paramifiers: Paramifiers -
config.formatters: Formatters -
config.adaptors: Adaptors
[edit] 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 (
/* [...] */) - inline comments (
// [...])
This distinction can be used to create a semantic and visual distinction between headers and annotations:
/* ** [section] */ // [function description] function foo() { // [procedure description] var foo = bar; // [explanatory comment] } /* [sub-section] */ function bar() { var foo = bar; }
While CSS only provides syntax for block comments, the same principle can be applied to StyleSheets.
[edit] Extending Core Functionality
Instead of overwriting core functions, hijacking should be used whenever possible to add the desired functionality either before or after invoking the hijacked code (via the apply(this,arguments); 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 fields).
[edit] Declaring Global Functions
Function statements should be avoided within plugins. Function statements are of the form:
function foo() { // ... }
Because plugins' code is processed using eval(), certain browsers (e.g. IE7 and Safari 3) leave the function in eval()'s local scope.
The way around this is to use expressions to declare such functions as variables, ideally adding an explicit window prefix:
window.foo = function() { // ... }
This is especially relevant when overriding core functions that are declared using function statements, such as displayMessage(), event handlers, etc.
[edit] 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, object detection should be employed for maximum flexibility:
if(!window.foo) { //# "window" only required to prevent ReferenceError if "foo" is a global variable throw "Missing dependency: Foo"; }
(foo might be a property of TiddlyWiki's config namespace, e.g. config.extensions.SamplePlugin)
The pluginInfo.log array can be extended to add a message without stopping execution of the plugin:
pluginInfo.log.push("lorem ipsum");
[edit] Creating Aliases
Sometimes variable names can become very long - especially when using namespaces.
A closure (anonymous wrapper function) can be used to create a local scope in which local variables can be used as aliases, essentially providing "shortcuts" for accessing variables from the parent scope:
(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
Here $ is used as a local alias for jQuery.
Similarly, plugin works as an alias for config.extensions.SamplePlugin.

