Commit 1daf8de1 authored by jhodgdon's avatar jhodgdon

Issue #2493677 by nod_, dawehner, Wim Leers: JSDoc for misc/ files

parent 9ae64742
......@@ -18,6 +18,8 @@
*
* Does not discriminate based on element type, so allows you to set the
* is-active class on any element: a, li…
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.activeLinks = {
attach: function (context) {
......@@ -28,7 +30,8 @@
var originalSelectors = ['[data-drupal-link-system-path="' + path.currentPath + '"]'];
var selectors;
// If this is the front page, we have to check for the <front> path as well.
// If this is the front page, we have to check for the <front> path as
// well.
if (path.isFront) {
originalSelectors.push('[data-drupal-link-system-path="<front>"]');
}
......
/**
* @file
* Provides Ajax page updating via jQuery $.ajax.
*
* Ajax is a method of making a request via JavaScript while viewing an HTML
* page. The request returns an array of commands encoded in JSON, which is
* then executed to make any changes that are necessary to the page.
*
* Drupal uses this file to enhance form elements with `#ajax['url']` and
* `#ajax['wrapper']` properties. If set, this file will automatically be
* included to provide Ajax capabilities.
*/
(function ($, window, Drupal, drupalSettings) {
"use strict";
/**
* Attaches the Ajax behavior to each Ajax form element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.AJAX = {
attach: function (context, settings) {
......@@ -58,7 +73,8 @@
element_settings.setClick = true;
// Form buttons use the 'click' event rather than mousedown.
element_settings.event = 'click';
// Clicked form buttons look better with the throbber than the progress bar.
// Clicked form buttons look better with the throbber than the progress
// bar.
element_settings.progress = {'type': 'throbber'};
element_settings.base = $(this).attr('id');
element_settings.element = this;
......@@ -70,6 +86,15 @@
/**
* Extends Error to provide handling for Errors in Ajax.
*
* @constructor
*
* @augments Error
*
* @param {XMLHttpRequest} xmlhttp
* XMLHttpRequest object used for the failed request.
* @param {string} uri
* The URI where the error occurred.
*/
Drupal.AjaxError = function (xmlhttp, uri) {
......@@ -87,14 +112,15 @@
statusCode += "\n" + Drupal.t("Debugging information follows.");
pathText = "\n" + Drupal.t("Path: !uri", {'!uri': uri});
statusText = '';
// In some cases, when statusCode === 0, xmlhttp.statusText may not be defined.
// Unfortunately, testing for it with typeof, etc, doesn't seem to catch that
// and the test causes an exception. So we need to catch the exception here.
// In some cases, when statusCode === 0, xmlhttp.statusText may not be
// defined. Unfortunately, testing for it with typeof, etc, doesn't seem to
// catch that and the test causes an exception. So we need to catch the
// exception here.
try {
statusText = "\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)});
}
catch (e) {
// empty
// Empty.
}
responseText = '';
......@@ -114,7 +140,18 @@
// We don't need readyState except for status == 0.
readyStateText = xmlhttp.status === 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
/**
* Formatted and translated error message.
*
* @type {string}
*/
this.message = statusCode + pathText + statusText + responseText + readyStateText;
/**
* Used by some browsers to display a more accurate stack trace.
*
* @type {string}
*/
this.name = 'AjaxError';
};
......@@ -125,8 +162,8 @@
* Provides Ajax page updating via jQuery $.ajax.
*
* This function is designed to improve developer experience by wrapping the
* initialization of Drupal.Ajax objects and storing all created object in the
* Drupal.ajax.instances array.
* initialization of {@link Drupal.Ajax} objects and storing all created
* objects in the {@link Drupal.ajax.instances} array.
*
* @example
* Drupal.behaviors.myCustomAJAXStuff = {
......@@ -158,17 +195,18 @@
* }
* };
*
* @see Drupal.AjaxCommands
*
* @param {object} settings
* The settings object passed to Drupal.Ajax constructor.
* The settings object passed to {@link Drupal.Ajax} constructor.
* @param {string} [settings.base]
* Base is passed to Drupal.Ajax constructor as the 'base' parameter.
* Base is passed to {@link Drupal.Ajax} constructor as the 'base'
* parameter.
* @param {HTMLElement} [settings.element]
* Element parameter of Drupal.Ajax constructor, element on which
* Element parameter of {@link Drupal.Ajax} constructor, element on which
* event listeners will be bound.
*
* @return {Drupal.Ajax}
*
* @see Drupal.AjaxCommands
*/
Drupal.ajax = function (settings) {
if (arguments.length !== 1) {
......@@ -195,7 +233,7 @@
/**
* Contains all created Ajax objects.
*
* @type {Array}
* @type {Array.<Drupal.Ajax>}
*/
Drupal.ajax.instances = [];
......@@ -205,16 +243,16 @@
* The Ajax request returns an array of commands encoded in JSON, which is
* then executed to make any changes that are necessary to the page.
*
* Drupal uses this file to enhance form elements with #ajax['url'] and
* #ajax['wrapper'] properties. If set, this file will automatically be
* Drupal uses this file to enhance form elements with `#ajax['url']` and
* `#ajax['wrapper']` properties. If set, this file will automatically be
* included to provide Ajax capabilities.
*
* @constructor
*
* @param {string} [base]
* Base parameter of Drupal.Ajax constructor
* Base parameter of {@link Drupal.Ajax} constructor
* @param {HTMLElement} [element]
* Element parameter of Drupal.Ajax constructor, element on which
* Element parameter of {@link Drupal.Ajax} constructor, element on which
* event listeners will be bound.
* @param {object} element_settings
* @param {string} element_settings.url
......@@ -244,6 +282,9 @@
$.extend(this, defaults, element_settings);
/**
* @type {Drupal.AjaxCommands}
*/
this.commands = new Drupal.AjaxCommands();
this.instanceIndex = false;
......@@ -252,15 +293,30 @@
// - Include the '#' for ID-based selectors.
// - Support non-ID-based selectors.
if (this.wrapper) {
/**
* @type {string}
*/
this.wrapper = '#' + this.wrapper;
}
/**
* @type {HTMLElement}
*/
this.element = element;
/**
* @type {object}
*/
this.element_settings = element_settings;
// If there isn't a form, jQuery.ajax() will be used instead, allowing us to
// bind Ajax to links as well.
if (this.element && this.element.form) {
/**
* @type {jQuery}
*/
this.$form = $(this.element.form);
}
......@@ -273,15 +329,16 @@
else if (this.element && element.form) {
this.url = this.$form.attr('action');
// @todo If there's a file input on this form, then jQuery will submit the
// Ajax response with a hidden Iframe rather than the XHR object. If the
// response to the submission is an HTTP redirect, then the Iframe will
// follow it, but the server won't content negotiate it correctly,
// because there won't be an ajax_iframe_upload POST variable. Until we
// figure out a work around to this problem, we prevent Ajax-enabling
// elements that submit to the same URL as the form when there's a file
// input. For example, this means the Delete button on the edit form of
// an Article node doesn't open its confirmation form in a dialog.
// @todo If there's a file input on this form, then jQuery will submit
// the AJAX response with a hidden Iframe rather than the XHR object.
// If the response to the submission is an HTTP redirect, then the
// Iframe will follow it, but the server won't content negotiate it
// correctly, because there won't be an ajax_iframe_upload POST
// variable. Until we figure out a work around to this problem, we
// prevent AJAX-enabling elements that submit to the same URL as the
// form when there's a file input. For example, this means the Delete
// button on the edit form of an Article node doesn't open its
// confirmation form in a dialog.
if (this.$form.find(':file').length) {
return;
}
......@@ -300,6 +357,26 @@
// Set the options for the ajaxSubmit function.
// The 'this' variable will not persist inside of the options object.
var ajax = this;
/**
* Options for the ajaxSubmit function.
*
* @name Drupal.Ajax#options
*
* @type {object}
*
* @prop {string} url
* @prop {object} data
* @prop {function} beforeSerialize
* @prop {function} beforeSubmit
* @prop {function} beforeSend
* @prop {function} success
* @prop {function} complete
* @prop {string} dataType
* @prop {object} accepts
* @prop {string} accepts.json
* @prop {string} type
*/
ajax.options = {
url: ajax.url,
data: ajax.submit,
......@@ -373,6 +450,8 @@
*
* The wrapper format determines how the HTML is wrapped, for example in a
* modal dialog.
*
* @const {string}
*/
Drupal.ajax.WRAPPER_FORMAT = '_wrapper_format';
......@@ -409,6 +488,9 @@
* In this case we're handling RETURN and SPACEBAR keypresses (event codes 13
* and 32. RETURN is often used to submit a form when in a textfield, and
* SPACE is often used to activate an element without submitting.
*
* @param {HTMLElement} element
* @param {jQuery.Event} event
*/
Drupal.Ajax.prototype.keypressResponse = function (element, event) {
// Create a synonym for this to reduce code confusion.
......@@ -417,8 +499,8 @@
// Detect enter key and space bar and allow the standard response for them,
// except for form elements of type 'text', 'tel', 'number' and 'textarea',
// where the spacebar activation causes inappropriate activation if
// #ajax['keypress'] is TRUE. On a text-type widget a space should always be a
// space.
// #ajax['keypress'] is TRUE. On a text-type widget a space should always
// be a space.
if (event.which === 13 || (event.which === 32 && element.type !== 'text' &&
element.type !== 'textarea' && element.type !== 'tel' && element.type !== 'number')) {
event.preventDefault();
......@@ -434,6 +516,9 @@
* perform the actual Ajax call. It is bound to the event using
* bind() in the constructor, and it uses the options specified on the
* Ajax object.
*
* @param {HTMLElement} element
* @param {jQuery.Event} event
*/
Drupal.Ajax.prototype.eventResponse = function (element, event) {
event.preventDefault();
......@@ -479,6 +564,10 @@
*
* Runs before the beforeSend() handler (see below), and unlike that one, runs
* before field data is collected.
*
* @param {HTMLElement} element
* @param {object} options
* @param {object} options.data
*/
Drupal.Ajax.prototype.beforeSerialize = function (element, options) {
// Allow detaching behaviors to update field values before collecting them.
......@@ -515,6 +604,10 @@
/**
* Modify form values prior to form submission.
*
* @param {object} form_values
* @param {HTMLElement} element
* @param {object} options
*/
Drupal.Ajax.prototype.beforeSubmit = function (form_values, element, options) {
// This function is left empty to make it simple to override for modules
......@@ -523,30 +616,35 @@
/**
* Prepare the Ajax request before it is sent.
*
* @param {XMLHttpRequest} xmlhttprequest
* @param {object} options
* @param {object} options.extraData
*/
Drupal.Ajax.prototype.beforeSend = function (xmlhttprequest, options) {
// For forms without file inputs, the jQuery Form plugin serializes the form
// values, and then calls jQuery's $.ajax() function, which invokes this
// handler. In this circumstance, options.extraData is never used. For forms
// with file inputs, the jQuery Form plugin uses the browser's normal form
// submission mechanism, but captures the response in a hidden IFRAME. In this
// circumstance, it calls this handler first, and then appends hidden fields
// to the form to submit the values in options.extraData. There is no simple
// way to know which submission mechanism will be used, so we add to extraData
// regardless, and allow it to be ignored in the former case.
// For forms without file inputs, the jQuery Form plugin serializes the
// form values, and then calls jQuery's $.ajax() function, which invokes
// this handler. In this circumstance, options.extraData is never used. For
// forms with file inputs, the jQuery Form plugin uses the browser's normal
// form submission mechanism, but captures the response in a hidden IFRAME.
// In this circumstance, it calls this handler first, and then appends
// hidden fields to the form to submit the values in options.extraData.
// There is no simple way to know which submission mechanism will be used,
// so we add to extraData regardless, and allow it to be ignored in the
// former case.
if (this.$form) {
options.extraData = options.extraData || {};
// Let the server know when the IFRAME submission mechanism is used. The
// server can use this information to wrap the JSON response in a TEXTAREA,
// as per http://jquery.malsup.com/form/#file-upload.
// server can use this information to wrap the JSON response in a
// TEXTAREA, as per http://jquery.malsup.com/form/#file-upload.
options.extraData.ajax_iframe_upload = '1';
// The triggering element is about to be disabled (see below), but if it
// contains a value (e.g., a checkbox, textfield, select, etc.), ensure that
// value is included in the submission. As per above, submissions that use
// $.ajax() are already serialized prior to the element being disabled, so
// this is only needed for IFRAME submissions.
// contains a value (e.g., a checkbox, textfield, select, etc.), ensure
// that value is included in the submission. As per above, submissions
// that use $.ajax() are already serialized prior to the element being
// disabled, so this is only needed for IFRAME submissions.
var v = $.fieldValue(this.element);
if (v !== null) {
options.extraData[this.element.name] = v;
......@@ -563,7 +661,7 @@
return;
}
// Insert progress indicator
// Insert progress indicator.
var progressIndicatorMethod = 'setProgressIndicator' + this.progress.type.slice(0, 1).toUpperCase() + this.progress.type.slice(1).toLowerCase();
if (progressIndicatorMethod in this && typeof this[progressIndicatorMethod] === 'function') {
this[progressIndicatorMethod].call(this);
......@@ -607,6 +705,9 @@
/**
* Handler for the form redirection completion.
*
* @param {Array.<Drupal.AjaxCommands~commandDefinition>} response
* @param {number} status
*/
Drupal.Ajax.prototype.success = function (response, status) {
// Remove the progress element.
......@@ -639,7 +740,13 @@
};
/**
* Build an effect object which tells us how to apply the effect when adding new HTML.
* Build an effect object to apply an effect when adding new HTML.
*
* @param {object} response
* @param {string} [response.effect]
* @param {string|number} [response.speed]
*
* @return {object}
*/
Drupal.Ajax.prototype.getEffect = function (response) {
var type = response.effect || this.effect;
......@@ -667,6 +774,9 @@
/**
* Handler for the form redirection error.
*
* @param {object} response
* @param {string} uri
*/
Drupal.Ajax.prototype.error = function (response, uri) {
// Remove the progress element.
......@@ -689,12 +799,46 @@
};
/**
* Provide a series of commands that the server can request the client perform.
* @typedef {object} Drupal.AjaxCommands~commandDefinition
*
* @prop {string} command
* @prop {string} [method]
* @prop {string} [selector]
* @prop {string} [data]
* @prop {object} [settings]
* @prop {bool} [asterisk]
* @prop {string} [text]
* @prop {string} [title]
* @prop {string} [url]
* @prop {object} [argument]
* @prop {string} [name]
* @prop {string} [value]
* @prop {string} [old]
* @prop {string} [new]
* @prop {bool} [merge]
* @prop {Array} [args]
*
* @see Drupal.AjaxCommands
*/
/**
* Provide a series of commands that the client will perform.
*
* @constructor
*/
Drupal.AjaxCommands = function () {};
Drupal.AjaxCommands.prototype = {
/**
* Command to insert new content into the DOM.
*
* @param {Drupal.Ajax} ajax
* @param {object} response
* @param {string} response.data
* @param {string} [response.method]
* @param {string} [response.selector]
* @param {object} [response.settings]
* @param {number} [status]
*/
insert: function (ajax, response, status) {
// Get information from the response. If it is not there, default to
......@@ -712,16 +856,17 @@
var new_content_wrapped = $('<div></div>').html(response.data);
var new_content = new_content_wrapped.contents();
// For legacy reasons, the effects processing code assumes that new_content
// consists of a single top-level element. Also, it has not been
// sufficiently tested whether attachBehaviors() can be successfully called
// with a context object that includes top-level text nodes. However, to
// give developers full control of the HTML appearing in the page, and to
// enable Ajax content to be inserted in places where DIV elements are not
// allowed (e.g., within TABLE, TR, and SPAN parents), we check if the new
// content satisfies the requirement of a single top-level element, and
// only use the container DIV created above when it doesn't. For more
// information, please see https://www.drupal.org/node/736066.
// For legacy reasons, the effects processing code assumes that
// new_content consists of a single top-level element. Also, it has not
// been sufficiently tested whether attachBehaviors() can be successfully
// called with a context object that includes top-level text nodes.
// However, to give developers full control of the HTML appearing in the
// page, and to enable Ajax content to be inserted in places where DIV
// elements are not allowed (e.g., within TABLE, TR, and SPAN parents),
// we check if the new content satisfies the requirement of a single
// top-level element, and only use the container DIV created above when
// it doesn't. For more information, please see
// https://www.drupal.org/node/736066.
if (new_content.length !== 1 || new_content.get(0).nodeType !== 1) {
new_content = new_content_wrapped;
}
......@@ -756,9 +901,9 @@
new_content[effect.showEffect](effect.showSpeed);
}
// Attach all JavaScript behaviors to the new content, if it was successfully
// added to the page, this if statement allows #ajax['wrapper'] to be
// optional.
// Attach all JavaScript behaviors to the new content, if it was
// successfully added to the page, this if statement allows
// `#ajax['wrapper']` to be optional.
if (new_content.parents('html').length > 0) {
// Apply any settings from the returned JSON if available.
settings = response.settings || ajax.settings || drupalSettings;
......@@ -768,6 +913,12 @@
/**
* Command to remove a chunk from the page.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.selector
* @param {object} [response.settings]
* @param {number} [status]
*/
remove: function (ajax, response, status) {
var settings = response.settings || ajax.settings || drupalSettings;
......@@ -779,6 +930,12 @@
/**
* Command to mark a chunk changed.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.selector
* @param {bool} [response.asterisk]
* @param {number} [status]
*/
changed: function (ajax, response, status) {
if (!$(response.selector).hasClass('ajax-changed')) {
......@@ -791,6 +948,12 @@
/**
* Command to provide an alert.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.text
* @param {string} response.title
* @param {number} [status]
*/
alert: function (ajax, response, status) {
window.alert(response.text, response.title);
......@@ -798,6 +961,11 @@
/**
* Command to set the window.location, redirecting the browser.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.url
* @param {number} [status]
*/
redirect: function (ajax, response, status) {
window.location = response.url;
......@@ -805,13 +973,24 @@
/**
* Command to provide the jQuery css() function.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {object} response.argument
* @param {number} [status]
*/
css: function (ajax, response, status) {
$(response.selector).css(response.argument);
},
/**
* Command to set the settings that will be used for other commands in this response.
* Command to set the settings used for other commands in this response.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {bool} response.merge
* @param {object} response.settings
* @param {number} [status]
*/
settings: function (ajax, response, status) {
if (response.merge) {
......@@ -824,6 +1003,13 @@
/**
* Command to attach data using jQuery's data API.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.name
* @param {string} response.selector
* @param {string|object} response.value
* @param {number} [status]
*/
data: function (ajax, response, status) {
$(response.selector).data(response.name, response.value);
......@@ -831,6 +1017,13 @@
/**
* Command to apply a jQuery method.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {Array} response.args
* @param {string} response.method
* @param {string} response.selector
* @param {number} [status]
*/
invoke: function (ajax, response, status) {
var $element = $(response.selector);
......@@ -839,6 +1032,11 @@
/**
* Command to restripe a table.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.selector
* @param {number} [status]
*/
restripe: function (ajax, response, status) {
// :even and :odd are reversed because jQuery counts from 0 and
......@@ -852,6 +1050,12 @@
/**
* Command to update a form's build ID.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.old
* @param {string} response.new
* @param {number} [status]
*/
update_build_id: function (ajax, response, status) {
$('input[name="form_build_id"][value="' + response.old + '"]').val(response.new);
......@@ -863,6 +1067,11 @@
* Uses the proprietary addImport method if available as browsers which
* support that method ignore @import statements in dynamically added
* stylesheets.
*
* @param {Drupal.Ajax} [ajax]
* @param {object} response
* @param {string} response.data
* @param {number} [status]
*/
add_css: function (ajax, response, status) {
// Add the styles in the normal way.
......
/**
* @file
* Adds an HTML element and method to trigger audio UAs to read system messages.
*
* Use Drupal.announce() to indicate to screen reader users that an element on
* the page has changed state. For instance, if clicking a link loads 10 more
* items into a list, one might announce the change like this.
* Use {@link Drupal.announce} to indicate to screen reader users that an
* element on the page has changed state. For instance, if clicking a link
* loads 10 more items into a list, one might announce the change like this.
*
* @example
* $('#search-list')
* .on('itemInsert', function (event, data) {
* // Insert the new items.
......@@ -14,6 +17,7 @@
* ));
* });
*/
(function (Drupal, debounce) {
"use strict";
......@@ -22,8 +26,9 @@
var announcements = [];
/**
* Builds a div element with the aria-live attribute and attaches it
* to the DOM.