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:
  • HTML
  • CSS
  • Javascript
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:

  • templates
  • macros

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

  • Use an object if you need an associative array, not an array. [source]
  • Declare all local variables with var before they are used.
  • Remove trailing commas from array and object definitions, i.e.:
    var object = { foo: 'bar' }
    not
    var object = { foo: 'bar', }

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.
Topic revision: r12 - 31 May 2010 - 09:24:40 - CrawfordCurrie
 
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. see CopyrightStatement. Creative Commons LicenseGet Foswiki at sourceforge.net. Fast, secure and Free Open Source software downloads