====== JavaScript ====== DokuWiki makes uses of [[wp>JavaScript]] to enhance the user experience. Like for [[css|stylesheets]] all JavaScript files are delivered through a single dispatcher to minimize HTTP requests, for caching and [[config:compress|compression]]. This page gives you an overview how JavaScript is loaded from DokuWiki core, [[:plugins]] and [[:templates]]. It also gives some info about event handling and coding style when writing JavaScript for use in DokuWiki. ===== JavaScript loading ===== All JavaScript is collected and delivered by [[xref>lib/exe/js.php]]. This file will concatenate all found files, whitespace compress (if [[config:compress]] is enabled) and cache the result. It also instructs browsers to cache the file, so when you are developing new JavaScript, be sure to refresh your browser cache (hitting Shift-F5, Shift+CTL+R or similar) whenever your script was updated. DokuWiki will load JavaScript from the following places: * autogenerated JavaScript (language strings, config settings, [[:toolbar]]) * lib/scripts/*.js ((To avoid loading unnecessary script data for reading visitors, the content of edit.js and media.js is only loaded in edit mode or media popup)) * lib/plugins/*/script.js * lib/tpl//script.js * conf/userscript.js As you can see you can provide JavaScript with your [[:templates]] and [[:plugins]] (through a ''script.js'' file) and can define your own scripts in ''conf/userscript.js''. JavaScript can also be added in main.php located under lib/tpl/. The familiar HTML code will accept JavaScript. ==== Include Syntax ==== DokuWiki's JavaScript dispatcher allows you to use special JavaScript comments to include other script files. This is useful for cases where usually only a single JavaScript file would be parsed, e.g. in templates or plugins. :!: Included files are not checked for updates by the cache logic. You need to touch the master file for updating the cache. :!: Includes are *not* supported inside included files to avoid any circular references. :!: Includepath may only consist of letter,digit, underscore, "/" and ".". === include === /* DOKUWIKI:include somefile.js */ This syntax will include the given file where the comment is placed. The filename is relative to the file containing the include markup unless it starts with a slash which indicates an absolute file system path. === include_once === /* DOKUWIKI:include_once common_library.js */ This syntax will include the given file where the comment is placed. The filename is relative to the file containing the include markup unless it starts with a slash which indicates an absolute file system path. The file will only be included if not a file of the same base name was previously loaded through the include_once statement. This name is shared over all script files (from all plugins), so you should use a meaningful file name. Using this statement makes sense if you write multiple independent [[:plugins]] all using the same JavaScript library. Including it with ''include_once'' using the same basename will make sure the library is loaded only once even if multiple of your plugins are installed. ===== Coding Guidelines ===== When writing JavaScript for the use within DokuWiki you should follow a few rules. Because of the nature of JavaScript, failing to do so might result in not only breaking your script but all scripts in DokuWiki. ==== Validate your Code ==== As mentioned above, DokuWiki will shrink the JavaScript code when the [[config:compress]] option is enabled (which it is by default). To do this without introducing syntax errors, the JavaScript has to be checked more strictly than it might be when run uncompressed. To check your code you should use the [[http://www.jslint.com/|JSLint]] online service. * debug your code with [[config:compress]] disabled but * verify your code still works with [[config:compress]] enabled ==== Use unobtrusive JavaScript ===== Do not assume people have JavaScript enabled, when writing new DokuWiki functionality. Instead use JavaScript as enhancement of the user interface only, when JavaScript is not available you code should fallback to normal page reload based behavior. To help you with this DokuWiki has a few predefined functions to help you with [[#Event Handling]]. ==== Avoid Inappropriate Mixing ===== The old way of doing things is to embed JavaScript directly in the HTML. However, JavaScript and (X)HTML shouldn't be mixed, and indeed with DokuWiki there are many cases where they //cannot// be mixed. Here are some examples of **INAPPROPRIATE MIXING**((Please note as well that there is no ''language'' attribute of the ''script'' tag! Instead use ''type="text/javascript"'' to be standards compliant.)):

some HTML

more HTML

This isn't just a matter of philosophical purity: some of the JavaScript may not work. In the above example, it turns out that both DokuWiki and the ''%%%%'' tag are trying to assign the page's ''onload'' handler to different JavaScript functions. Browsers cannot handle this conflict and the results are unpredictable. Strictly speaking, it is possible to embed JavaScript in your HTML, but only if you know that the JavaScript has no conflict with DokuWiki. Because this requires knowledge of DokuWiki's implementation, and because DokuWiki's implementation can change, this is still not a good idea. It's wiser to be philosophically pure. ==== Using IDs ==== To modify a DOM object the JavaScript must be able to locate the object. The easiest way to locate the object is to give the associated HTML tag an ID. This ID must be unique among all IDs on the page so that referencing this ID produces exactly the right DOM object. When you are producing your own HTML (e.g. from a template or plugin) that should be accessed from JavaScript later, be sure that the ID does not conflict with an existing ID. In particular, be sure that it won't conflict with the IDs automatically assigned to section headers. The easiest way to ensure this is to use two adjacent underscores (''%%__%%'') in your ID. Because section IDs are always valid [[:pagenames]], they will never contain adjacent underscores. ==== Inline scripts ==== As said before you should avoid mixing JavaScript and XHTML. However if you need to use inline JavaScript, you should wrap it like this: This ensures your script code is served in the most compatible way. Some more info is available at the [[http://www.w3.org/TR/xhtml1/#h-4.8| XHTML 1.0: Script and Style elements]] specification and [[http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-E067D597|CDATA section interface]] definition. If you need to add inline JavaScript to the section you should write an [[action_plugin]] and handle the [[events_list#TPL_METAHEADER_OUTPUT]] event. ===== jQuery ===== [[develonly]] Starting with the October 2011 release "Angua", DokuWiki will ship with the jQuery and jQuery UI library. From that point forward the section [[#DokuWiki JavaScript Library]] below will be removed. Please follow these coding conventions when working with jQuery in DokuWiki. Please also refer to our [[devel:jqueryfaq|JQuery FAQ for Plugin Developers]]. ==== No $() ==== jQuery is only used in compatibility mode. There is no ''$()'' method((it might be defined but will **not** do what you expect)). Use the ''jQuery()'' method instead. Do not map ''$()'' to ''jQuery()'', not even within your own (anonymous) functions. ==== Prefix jQuery object variables ==== To make it clear that a variable contains an instance of the jQuery object all these variables should be prefixed by a ''$'' character: var $obj = jQuery('#some__id'); ===== DokuWiki JavaScript Library ===== DokuWiki does not use any of the bigger JavaScript libraries like Prototype, Dojo or JQuery. Instead it comes with a small set of handy classes and functions that may help you with writing JavaScript code for DokuWiki. ==== Event Handling ==== As said in [[#Avoid Inappropriate Mixing]], event handlers should not be mixed into HTML code. Instead those handlers should be assigned when the [[wp>Document Object Model]] (DOM) was loaded. The DOM is a tree-based object representation of the HTML that is available to the JavaScript. Your JavaScript need merely figure out which objects to attach functions to and then to attach functions to them. To attach functions to any given DOM Object in a cross-browser compatible way, the **''addEvent()''** function is provided. It takes the DOM object, an event name (like 'click') and a callback function (the handler) as arguments. This function also takes care of multiple plugins trying to register an event on the same DOM object. Additionally, ''addEvent()'' changes the properties and methods of the event in Internet Explorer - so you can use the ''target'' property and can call the ''preventDefault()'' and ''stopPropagation()'' methods. Unfortunately, the JavaScript in your ''script.js'' loads before the HTML has finished loading and before the DOM tree has been made. The objects that the JavaScript needs don't yet exist. However, you may still run JavaScript when ''script.js'' loads, as long as that JavaScript doesn't require DOM. To solve this problem DokuWiki provides the **''addInitEvent()''** function. This function will register a given callback to be run as soon as the DOM is ready. Here's an example (from the [[:tips:summary_enforcement]] tip) using both methods: function enforceSummary() { /*...*/ } function installSummaryEnforcement() { var summary_input = document.getElementById('edit__summary'); if(summary_input !== null) { /*...*/ addEvent(summary_input, 'change', enforceSummary); addEvent(summary_input, 'keyup', enforceSummary); } } addInitEvent(installSummaryEnforcement); In this example, we need to attach ''enforceSummary()'' to the ''onchange'' and ''onkeyup'' handlers for the summary input field. ''installSummaryEnforcement()'' does this. The call to ''addInitEvent()'' will run the ''installSummaryEnforcement()'' function as soon as the DOM is loaded. Notice how ''installSummaryEnforcement()'' itself works. First it acquires a DOM object by ID (though there are other ways to acquire it). In this case the object may not exist since the summary field is only shown when editing a page, so the function first tests to see if it got the object. If it did, it calls ''addEvent()'' to attach ''enforceSummary()'' to the event handlers. The DokuWiki event functions were originally provided by Dean Edwards [[http://dean.edwards.name/weblog/2005/10/add-event/|here]] and [[http://dean.edwards.name/weblog/2005/10/add-event2/|here]]. * For deeper insight on the event system see the [[http://dev.splitbrain.org/reference/dokuwiki/nav.html?lib/scripts/events.js.source.html|source]] ==== Predefined Global Variable ==== DokuWiki defines certain JavaScript variables for the use in your script: * ''DOKU_BASE'' -- the full webserver path to the DokuWiki installation * ''DOKU_TPL'' -- the full webserver path to the used [[:Template]] * ''LANG'' -- an array of languagestrings ==== SACK (AJAX) Library ==== DokuWiki provides a simple AJAX library named ''SACK'' by Gregory Wild-Smith. * See the [[http://github.com/splitbrain/dokuwiki/blob/master/lib/scripts/tw-sack.js|Source tw-sack.js]] for details on how to use it. ==== $() ==== The $() function is a handy shortcut to the all-too-frequent ''document.getElementById()'' function of the DOM. Like the DOM function, this one returns the element that has the id passed as an argument. Unlike the DOM function, though, this one goes further. You can pass more than one id and $() will return an Array object with all the requested elements. * Taken from the [[http://prototype.conio.net/|prototype library]] * See [[http://www.sergiopereira.com/articles/prototype.js.html|Docs by Sergio Pereira]] ==== Additional functions ==== DokuWiki provides various other tool methods. Especially the following might be useful for your development: isset, getElementsByClass, findPosX, findPosY, jsEscape, prependChild. * For details check the source of these files: * [[http://github.com/splitbrain/dokuwiki/blob/master/lib/scripts/script.js|script.js]] * [[http://github.com/splitbrain/dokuwiki/blob/master/lib/scripts/helpers.js|helpers.js]] ==== JSINFO ==== Since [[http://github.com/splitbrain/dokuwiki/commit/85b9dd81bb1eac380c07b7491f48e23fb74a0923|november 23, 2009]] DokuWiki passes the global [[devel:environment#jsinfo|$JSINFO]] to javascript. (see the mailinglist [[http://www.freelists.org/post/dokuwiki/INFO,44|mailinglist]]) The usual way in a plugin is this: function register(&$controller) { $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, '_adduser'); } function _adduser(&$event, $param) { global $JSINFO; $JSINFO['user'] = $_SERVER['REMOTE_USER']; } FIXME add exact time when the array is send to JavaScript