
Using JQuery With Foswiki
WORK IN PROGRESS please feel free to contribute
Introduction
jQuery is an industry-standard Javascript framework, used by a lot of Foswiki extensions and many foswiki developers. jQuery is the "javascript framework of choice" for use with Foswiki. That doesn't mean you can't use other frameworks (such as Dojo, YUI, or Mootools), just that we focus our efforts on supporting jQuery first.
Getting started
We're going to assume you are familiar with the following:
If you have ever hand-crafted an HTML page, styled it using CSS, and maybe written a bit of Javascript (for example to validate forms input) then you are well positioned to use jQuery. If you are new to any of these, then sorry, you have a steep learning curve ahead of you. There are a wealth of resources on the web to help you get started, though.
Before you start trying to use jQuery in Foswiki, we'd also recommend that you familiarize yourself with how Foswiki works, especially:
Useful links
JQueryPlugin
The easiest way to get started using jQuery is to install the
JQueryPlugin. Once you have done that, all you need to do is to add the appropriate
%JQREQUIRE directive, and you will be able to use the required plugin in your HTML, as described in the plugin documentation. That really is all there is to it!
Adding more jQuery plugins
The plugins shipped with JQueryPlugin itself live in
-
pub/System/JQueryPlugin/plugins/myplugin/
This is the default directory where all js and css code of a new myplugin library is located. All plugins except jQuery-ui and themes live next to this directory.
-
lib/System/JQueryPlugin/MYPLUGIN.pm
This is the perl stub that takes care of loading a library into a page as well as all of its dependencies. This module optionally implements a new %MACRO.
Additional jQuery libraries can be added to the framework by another Foswiki plugin as well, as described below.
Plugins have to be enabled via
configure. Only then are they loadable via
%JQREQUIRE{"mynewplugin"}%.
Managing jQuery plugins in another Foswiki plugin via JQueryPlugin
Foswiki plugins can add jQuery plugins to the set managed by JQueryPlugin. Even if you just want to add some jQuery plugins, and don't want to use any of the other features of Foswiki plugins, this is a good approach, because it reuses Foswiki's plugin mechanism and can make use of the same packaging and installation support.
The first step is to register your new plugin, which is done using
Foswiki::Plugins::JQueryPlugin::registerPlugin. The best place to call this is in the
initPlugin method of your Foswiki plugin. For example, in the (fictional) Foswiki plugin called "BathPlugin" we have a jQuery plugin called 'rubberduck':
sub initPlugin {
my ( $topic, $web, $user, $installWeb ) = @_;
# Register our 'rubberduck' jQuery plugin
my $plugin = Foswiki::Plugins::JQueryPlugin::registerPlugin(
'rubberduck', 'Foswiki::Plugins::BathPlugin::RUBBERDUCK');
This registers the plugin name
rubberduck and associates it with the Perl class
Foswiki::Plugins::BathPlugin::RUBBERDUCK.
lib/Foswiki/Plugins/BathPlugin/RUBBERDUCK.pm contains:
package Foswiki::Plugins::BathPlugin::RUBBERDUCK;
use strict;
use Foswiki::Plugins::JQueryPlugin::Plugin;
our @ISA = ( 'Foswiki::Plugins::JQueryPlugin::Plugin' );
sub new {
my $class = shift;
my $session = shift || $Foswiki::Plugins::SESSION;
my $this = bless($class->SUPER::new(
$session,
name => 'rubberduck',
version => '1.1',
author => 'Your Name',
puburl => '%PUBURLPATH%/%SYSTEMWEB%/BathPlugin',
javascript => [ 'rubberduck.js' ],
summary => <<SUMMARY), $class);
Interactive rubber duck steering component
SUMMARY
return $this;
}
1;
Note, that this perl class must be based on
Foswiki::Plugins::JQueryPlugin::Plugin. This class can also make use of a couple of static helper methods in
Foswiki::Plugins::JQueryPlugin::Plugins (note the plural
s at the end).
The constructor for the
Foswiki::Plugins::JQueryPlugin::Plugin supports a number of fields:
| Field |
Description |
name |
symbolic name of the module; mandatory |
version |
semi-numeric version number of the plugin; this is added to the urls loading this module |
summary |
Descriptive text |
documentation |
topic holding the plugin's documentation |
author |
creator and/or copyright holder of this plugin |
homepage |
url where the bulk of the module's documentation is located |
puburl |
url base for finding CSS and Javascript files. By default this points to the %PUBURLPATH%/%SYSTEMWEB%/JQueryPlugin/plugins/<name> but can be redirected to your own plugin, as in the example above. |
css |
Reference to a perl array of CSS filenames. These files must exist in puburl. |
javascript |
Reference to a perl array of Javascript filenames. These files must exist in puburl. |
dependencies |
names of other plugins to be loaded before this library is added to the head; this can either be the name of another jquery library registered to JQueryPlugin or any other identifier used in an ADDTOHEAD call |
tags |
list of macros this plugin implements; only used for documentation |
debug |
Set to 1 to enable debug |
Once your plugin is registered, you can call it into a topic using
%JQREQUIRE{rubberduck}%.
If you know your plugin is
always required, then add a call to
Foswiki::Plugins::JQueryPlugin::createPlugin to
initPlugin, thus:
Foswiki::Plugins::JQueryPlugin::createPlugin('rubberduck');
If your goal is just to package up some jQuery plugins, all you have to do is finish configuring your plugin and making sure it works with the build system, as described elsewhere in the
DevelopersBible.
See also the example module in
EMPTY.pm and
jquery.empty.uncompressed.js as well as the other plugins shipped with JQueryPlugin.
Debug mode
The
$Foswiki::cfg{JQueryPlugin}{Debug} flag is used to switch on debug mode for JQueryPlugin itself as well as all of its sub-modules.
If enabled, it will load the
uncompressed variants of a module's static file, e.g.
jquery.myplugin.uncompressed.js instead of
jquery.myplugin.js
So make sure your source files are called
...uncompressed.js and
...uncompressed.css as the plugin won't work in debug mode otherwise.
Documentation
The
documentation property of a jquery plugin points to a separate topic that holds all documentation for it, including a pointer to the third-party homepage. Use
System.JQueryPluginEmpty as a template.
The default documentation topic is
System.JQuery + PluginName unless otherwise specified. For example, the docu for
rubberduck should be located in
System.JQueryRubberduck.
Instead of specifying a
summary on perl level, defined a named
summary section in this topic.
Please make sure to document how your plugin is integrated into Foswiki, that is how to make use of it and which class name enables your plugin on a html element (see
#LiveQuery below).
JQuery Coding Standards
Follow
Drupal’s JavaScript coding standards
Wrap $
If you want to use jQuery’s
$ variable, you
must wrap your code like this:
(function($) {
// code using $ variable
})(jQuery);
The global
$ name is not used by jQuery to avoid conflicts with other libraries that may be loaded. JQuery examples that you download
off the web will work as written as long as you wrap them as described here.
Shorthands
Use the jQuery shorthand
$ where possible:
$(function() { ... });
instead of
$(document).ready(function() { ... });
Global variables
If you want to use global variables within the scope of your code, wrap your code like this:
(function() {
var foo = 'bar'; // yay, it's almost like I'm global
})();
If you want to use global variables in the global scope, put the variable in the
foswiki namespace:
foswiki.foo = 'bar';
Mind the predefined global variables. See next section.
Propagating perl settings to javascript
The
jquery.foswiki plugin will initialize the global
foswiki object with a set of variables
that are created by reading
meta tags in the HTML header. These have the format
<meta name="foswiki.foo.bar.baz" content="string/boolean/object/function" />
The foswiki object
This will create the appropriate sub-namespaces and initialize the named property with the value in the
content attribute.
There is a set of predefined variables that can be used in your javascript code via the
foswiki namespace:
| Name |
Content |
| foswiki.web |
%WEB% |
| foswiki.topic |
%TOPIC%" |
| foswiki.scriptUrl |
%SCRIPTURL% |
| foswiki.scriptUrlPath |
%SCRIPTURLPATH% |
| foswiki.pubUrl |
%PUBURL% |
| foswiki.pubUrlPath |
%PUBURLPATH% |
| foswiki.systemWebName |
%SYSTEMWEB% |
| foswiki.usersWebName |
%USERSWEB% |
| foswiki.wikiName |
%WIKINAME% |
| foswiki.loginName |
%USERNAME% |
| foswiki.wikiUserName |
%WIKIUSERNAME% |
| foswiki.serverTime |
02 Sep 2010 - 14:28 |
| foswiki.ImagePluginEnabled |
%IF{"context ImagePluginEnabled? " then="true" else="false"}% |
| foswiki.MathModePluginEnabled |
%IF{"context MathModePluginEnabled? " then="true" else="false"}% |
WARNING These variables have been replaced in more recent code with the
foswiki.getPreference() function. For example,
foswiki.getPreference('TOPIC');
will retrieve the value of %TOPIC% - but only if %TOPIC% is exported. See
DefaultPreferences for more information about the
EXPORTEDPREFERENCES preference and exporting foswiki macros to Javascript.
Avoid Internet Explorer errors
LiveQuery
Use JQueryLiveQuery to initialize your plugin for all html elements of a page. Otherwise content
that is loaded asynchronously using ajax won't be initialized. LiveQuery will take care of that
automatically.
Instead of
$(".jqPluginName").each(function() {
// initializer
});
use
$(".jqPluginName").livequery(function() {
// initializer
});
Metadata
Use JQueryMetadata to integrate jQuery plugins into Foswiki.
Example
Load the plugin as required for the current page:
%JQREQUIRE{"mynewplugin"}%
This is how the markup looks like:
<div class="jqMyNewPlugin {key1:'value1', key2:'value2', key3:'value3'}">
<div>
This is the plugin's initializer reading the metadata:
jQuery(function($) {
var defaults {
key1: 'default1',
key2: 'default2',
key3: 'default3'
};
// find all elements tagged .jqMyNewPlugin that aren't init'ed yet
// ... using livequery instead of each to trigger initialisation of async content
$(".jqMyNewPlugin:not(jqInitedMyNewPlugin)").livequery(function() {
// create a jQuery object for this
var $this = $(this);
// prevent the markup to be init'ed multiple times
$this.addClass("jqInitedMyNewPlugin");
// get plugin options by merging defaults and current json objs
var opts = $.extend({}, defaults, $this.metadata());
// call the plugin handler
$this.myNewPlugin(opts);
});
});
Using jQuery and AJAX
The main docu is located at
http://docs.jquery.com/Ajax.
Making a simple query to the server
The easiest way to make a request in the server is to get it to give you a page of results already formatted in a way that jQuery understands. The simplest approach is to use JSON. For example, let's say I want to perform a search for the string "look for this" on the server. In my Javascript I have:
$.get(viewurl+'/JsonSearch',
{
skin: "text",
contenttype: "text/plain",
inweb: web,
string: "look for this"
},
function(results) {
// do something with the array of results
}, "json");
viewurl is the result of calling
SCRIPTURL{view}%, and can easily be passed to the Javascript, as described in
Propagating perl settings to javascript, above. This request makes Javascript request a topic "JsonSearch" from the server.
When raw-edited, JsonSearch contains:
[%SEARCH{"%URLPARAM{string}%" web="%URLPARAM{inweb}%" nonoise="on" format="'$topic'" separator=","}%]
The use of the
contenttype and
skin parameters stops Foswiki from generating any template furniture around the result, and the formatted search produces a JSON format array, which is then read by jQuery and converted to a Javascript array object
results.
Loading part of the UI asynchronously
jQuery supports a short form of the above
$.get method by directly injecting the HTML result of a REST handler into the DOM tree:
$("#container").load("<url>, [<params>], [<callback>]");
Optionally a jQuery selector can be specified in the URL. Doing so will filter the incoming HTML document, only injecting the elements that match the selector:
$("#mysidebar .installedplugins-list").load("%SCRIPTURLPATH{"view"}/%SYSTEMWEB%/InstalledPlugins ul:first li");
Note also, that javascript included in the HTML fragment will be executed after
load()ing it.
Complex queries involving plugin functions
Foswiki supports plugin handlers (called REST handlers) that can be called by issuing a request to the server with a specifically crafted URL. Exactly the same approach can be used to call these handlers, except that the URL specifies the function to call. See the extensive documentation on plugin REST handlers for more information.