javascript.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. JavaScript
  2. ##########
  3. As of Elgg 1.9, we encourage all developers to adopt the `AMD (Asynchronous Module
  4. Definition) <http://requirejs.org/docs/whyamd.html>`_ standard for writing JavaScript code in Elgg.
  5. The 1.8 version is still functional and is :ref:`described below<1.8-js>`.
  6. .. contents:: Contents
  7. :local:
  8. :depth: 2
  9. AMD
  10. ===
  11. Here we'll describe making and executing AMD modules. The RequireJS documentation for
  12. `defining modules <http://requirejs.org/docs/api.html#define>`_ may also be of use.
  13. Executing a module in the current page
  14. ======================================
  15. Telling Elgg to load an existing module in the current page is easy:
  16. .. code-block:: php
  17. <?php
  18. elgg_require_js("myplugin/say_hello");
  19. On the client-side, this will asynchronously load the module, load any dependencies, and
  20. execute the module's definition function, if it has one.
  21. Defining the Module
  22. ===================
  23. Here we define a basic module that alters the page, by passing a "definition function" to ``define()``:
  24. .. code-block:: javascript
  25. // in views/default/js/myplugin/say_hello.js
  26. define(function(require) {
  27. var elgg = require("elgg");
  28. var $ = require("jquery");
  29. $('body').append(elgg.echo('hello_world'));
  30. });
  31. The module's name is determined by the view name, which here is ``js/myplugin/say_hello.js``.
  32. We strip the leading ``js/`` and the ``.js`` extension, leaving ``myplugin/say_hello``.
  33. .. warning::
  34. The definition function **must** have one argument named ``require``.
  35. Making modules dependent on other modules
  36. -----------------------------------------
  37. Below we refactor a bit so that the module depends on a new ``myplugin/hello`` module to provide
  38. the greeting:
  39. .. code-block:: javascript
  40. // in views/default/js/myplugin/hello.js
  41. define(function(require) {
  42. var elgg = require("elgg");
  43. return elgg.echo('hello_world');
  44. });
  45. .. code-block:: javascript
  46. // in views/default/js/myplugin/say_hello.js
  47. define(function(require) {
  48. var $ = require("jquery");
  49. var hello = require("myplugin/hello");
  50. $('body').append(hello);
  51. });
  52. Passing plugin/Elgg settings to modules
  53. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  54. You can use a PHP-based module to pass values from the server. To make the module ``myplugin/settings``,
  55. create the view file ``views/default/js/myplugin/settings.js.php`` (note the double extension
  56. ``.js.php``).
  57. .. code-block:: php
  58. <?php
  59. $settings = elgg_get_plugin_from_id('myplugin')->getAllSettings();
  60. $settings = [
  61. 'foo' => elgg_extract('foo', $settings),
  62. 'bar' => elgg_extract('bar', $settings),
  63. ];
  64. ?>
  65. define(<?php echo json_encode($settings); ?>);
  66. You must also manually register the view as an external resource:
  67. .. code-block:: php
  68. <?php
  69. // note the view name does not include ".php"
  70. elgg_register_simplecache_view('js/myplugin/settings.js');
  71. .. note::
  72. The PHP view is cached, so you should treat the output as static (the same for all users) and
  73. avoid session-specific logic.
  74. Setting the URL of a module
  75. ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  76. You may have a script outside your views you wish to make available as a module.
  77. In your PHP ``init, system`` event handler, you can use ``elgg_define_js()`` to do this:
  78. .. code-block:: php
  79. <?php
  80. elgg_define_js('underscore', [
  81. 'src' => '/mod/myplugin/vendors/underscore/underscore-min.js',
  82. ]);
  83. .. note::
  84. The ``src`` option in ``elgg_define_js()`` is passed through ``elgg_normalize_url``, so you can use paths
  85. relative to the site URL.
  86. Using traditional JS libraries as modules
  87. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  88. JavaScript libraries that define global resources can also be defined as AMD modules if you shim them by
  89. setting ``exports`` and optionally ``deps``:
  90. .. code-block:: php
  91. <?php
  92. // set the path, define its dependencies, and what value it returns
  93. elgg_define_js('jquery.form', [
  94. 'src' => '/mod/myplugin/vendors/jquery.form.js',
  95. 'deps' => array('jquery'),
  96. 'exports' => 'jQuery.fn.ajaxForm',
  97. ]);
  98. When this is requested client-side:
  99. #. The jQuery module is loaded, as it's marked as a dependency.
  100. #. ``http://example.org/elgg/mod/myplugin/vendors/jquery/jquery.form.js`` is loaded and executed.
  101. #. The value of ``window.jQuery.fn.ajaxForm`` is returned by the module.
  102. .. warning:: Calls to ``elgg_define_js()`` must be in an ``init, system`` event handler.
  103. Some things to note
  104. ^^^^^^^^^^^^^^^^^^^
  105. #. Do not use ``elgg.provide()`` anymore nor other means to attach code to ``elgg`` or other
  106. global objects. Use modules.
  107. #. Return the value of the module instead of adding to a global variable.
  108. #. JS and CSS views (names starting with ``js/`` or ``css/``) as well as static (.js/.css) files
  109. are automatically minified and cached by Elgg's simplecache system.
  110. Migrating JS from Elgg 1.8 to AMD / 1.9
  111. =======================================
  112. **Current 1.8 JavaScript modules will continue to work with Elgg**.
  113. We do not anticipate any backwards compatibility issues with this new direction and will fix any
  114. issues that do come up. The old system will still be functional in Elgg 1.9, but developers are
  115. encouraged to begin looking to AMD as the future of JS in Elgg.
  116. .. _1.8-js:
  117. Traditional JavaScript (1.8)
  118. ============================
  119. Register third-party libraries with ``elgg_register_js``:
  120. .. code:: php
  121. elgg_register_js('jquery', $cdnjs_url);
  122. This will override any URLs previously registered under this name.
  123. Load a library on the current page with ``elgg_load_js``:
  124. .. code:: php
  125. elgg_load_js('jquery');
  126. This will include and execute the linked code.
  127. .. tip::
  128. Using inline scripts is strongly discouraged because:
  129. * They are not testable (maintainability)
  130. * They are not cacheable (performance)
  131. * Doing so forces some scripts to be loaded in ``<head>`` (performance)
  132. Inline scripts in core or bundled plugins are considered legacy bugs.
  133. Core functions available in JS
  134. ==============================
  135. ``elgg.echo()``
  136. Translate interface text
  137. .. code:: js
  138. elgg.echo('example:text', ['arg1']);
  139. ``elgg.system_message()``
  140. Display a status message to the user.
  141. .. code:: js
  142. elgg.system_message(elgg.echo('success'));
  143. ``elgg.register_error()``
  144. Display an error message to the user.
  145. .. code:: js
  146. elgg.register_error(elgg.echo('error'));
  147. ``elgg.forward()``
  148. ``elgg.normalize_url()``
  149. Normalize a URL relative to the elgg root:
  150. .. code:: js
  151. // "http://localhost/elgg/blog"
  152. elgg.normalize_url('/blog');
  153. Redirect to a new page.
  154. .. code:: js
  155. elgg.forward('/blog');
  156. This function automatically normalizes the URL.
  157. ``elgg.parse_url()``
  158. Parse a URL into its component parts:
  159. .. code:: js
  160. // returns {
  161. // fragment: "fragment",
  162. // host: "community.elgg.org",
  163. // path: "/file.php",
  164. // query: "arg=val"
  165. // }
  166. elgg.parse_url(
  167. 'http://community.elgg.org/file.php?arg=val#fragment');
  168. ``elgg.get_page_owner_guid()``
  169. Get the GUID of the current page's owner.
  170. ``elgg.register_hook_handler()``
  171. Register a hook handler with the event system.
  172. .. code:: js
  173. // old initialization style
  174. elgg.register_hook_handler('init', 'system', my_plugin.init);
  175. // new: AMD module
  176. define(function (require) {
  177. var elgg = require('elgg');
  178. // [init, system] has fired
  179. });
  180. ``elgg.trigger_hook()``
  181. Emit a hook event in the event system.
  182. .. code:: js
  183. // allow other plugins to alter value
  184. value = elgg.trigger_hook('my_plugin:filter', 'value', {}, value);
  185. ``elgg.security.refreshToken()``
  186. Force a refresh of all XSRF tokens on the page.
  187. This is automatically called every 5 minutes by default.
  188. This requires a valid security token in 1.8, but not in 1.9.
  189. The user will be warned if their session has expired.
  190. ``elgg.security.addToken()``
  191. Add a security token to an object, URL, or query string:
  192. .. code:: js
  193. // returns {
  194. // __elgg_token: "1468dc44c5b437f34423e2d55acfdd87",
  195. // __elgg_ts: 1328143779,
  196. // other: "data"
  197. // }
  198. elgg.security.addToken({'other': 'data'});
  199. // returns: "action/add?__elgg_ts=1328144079&__elgg_token=55fd9c2d7f5075d11e722358afd5fde2"
  200. elgg.security.addToken("action/add");
  201. // returns "?arg=val&__elgg_ts=1328144079&__elgg_token=55fd9c2d7f5075d11e722358afd5fde2"
  202. elgg.security.addToken("?arg=val");
  203. ``elgg.get_logged_in_user_entity()``
  204. Returns the logged in user as an JS ElggUser object.
  205. ``elgg.get_logged_in_user_guid()``
  206. Returns the logged in user's guid.
  207. ``elgg.is_logged_in()``
  208. True if the user is logged in.
  209. ``elgg.is_admin_logged_in()``
  210. True if the user is logged in and is an admin.
  211. ``elgg.config.get_language()``
  212. Get the current page's language.
  213. There are a number of configuration values set in the elgg object:
  214. .. code:: js
  215. // The root of the website.
  216. elgg.config.wwwroot;
  217. // The default site language.
  218. elgg.config.language;
  219. // The current page's viewtype
  220. elgg.config.viewtype;
  221. // The Elgg version (YYYYMMDDXX).
  222. elgg.config.version;
  223. // The Elgg release (X.Y.Z).
  224. elgg.config.release;
  225. Module ``elgg/spinner``
  226. -----------------------
  227. The ``elgg/spinner`` module can be used to create an Ajax loading indicator fixed to the top of the window.
  228. .. code:: js
  229. define(function (require) {
  230. var spinner = require('elgg/spinner');
  231. elgg.action('friend/add', {
  232. beforeSend: spinner.start,
  233. complete: spinner.stop,
  234. success: function (json) {
  235. // ...
  236. }
  237. });
  238. });
  239. Hooks
  240. -----
  241. The JS engine has a hooks system similar to the PHP engine's plugin hooks: hooks are triggered and plugins can register callbacks to react or alter information. There is no concept of Elgg events in the JS engine; everything in the JS engine is implemented as a hook.
  242. Registering a callback to a hook
  243. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  244. Callbacks are registered using ``elgg.register_hook_handler()``. Multiple callbacks can be registered for the same hook.
  245. The following example registers the ``elgg.ui.initDatePicker`` callback for the *init*, *system* event. Note that a difference in the JS engine is that instead of passing a string you pass the function itself to ``elgg.register_hook_handler()`` as the callback.
  246. .. code:: javascript
  247. elgg.provide('elgg.ui.initDatePicker');
  248. elgg.ui.initDatePicker = function() { ... }
  249. elgg.register_hook_handler('init', 'system', elgg.ui.initDatePicker);
  250. The callback
  251. ^^^^^^^^^^^^
  252. The callback accepts 4 arguments:
  253. - **hook** - The hook name
  254. - **type** - The hook type
  255. - **params** - An object or set of parameters specific to the hook
  256. - **value** - The current value
  257. The ``value`` will be passed through each hook. Depending on the hook, callbacks can simply react or alter data.
  258. Triggering custom hooks
  259. ^^^^^^^^^^^^^^^^^^^^^^^
  260. Plugins can trigger their own hooks:
  261. .. code:: javascript
  262. elgg.hook.trigger_hook('name', 'type', {params}, "value");
  263. Available hooks
  264. ^^^^^^^^^^^^^^^
  265. init, system
  266. This hook is fired when the JS system is ready. Plugins should register their init functions for this hook.
  267. ready, system
  268. This hook is fired when the system has fully booted.
  269. getOptions, ui.popup
  270. This hook is fired for pop up displays ("rel"="popup") and allows for customized placement options.