diff --git a/.eslintrc b/.eslintrc index 14f7b95..e6124e4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -42,6 +42,15 @@ "strict": 2, // Warnings. "max-nested-callbacks": [1, 3], + "valid-jsdoc": [1, { + "prefer": { + "returns": "return", + "property": "prop" + }, + "requireReturn": false, + "requireParamDescription": false, + "requireReturnDescription": false + }], // Disabled. "camelcase": 0, "consistent-return": 0, diff --git a/core/misc/active-link.js b/core/misc/active-link.js index 3a2f818..97c9966 100644 --- a/core/misc/active-link.js +++ b/core/misc/active-link.js @@ -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 path as well. + // If this is the front page, we have to check for the path as + // well. if (path.isFront) { originalSelectors.push('[data-drupal-link-system-path=""]'); } diff --git a/core/misc/ajax.js b/core/misc/ajax.js index 5ec87f5..e9f4649 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -1,9 +1,24 @@ +/** + * @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 Drupal.Ajax objects and storing all created object in + * the {@link Drupal.ajax.instances} array. * * @example * Drupal.behaviors.myCustomAJAXStuff = { @@ -158,8 +195,6 @@ * } * }; * - * @see Drupal.AjaxCommands - * * @param {object} settings * The settings object passed to Drupal.Ajax constructor. * @param {string} [settings.base] @@ -169,6 +204,8 @@ * event listeners will be bound. * * @return {Drupal.Ajax} + * + * @see Drupal.AjaxCommands */ Drupal.ajax = function (settings) { if (arguments.length !== 1) { @@ -195,7 +232,7 @@ /** * Contains all created Ajax objects. * - * @type {Array} + * @type {Array.} */ Drupal.ajax.instances = []; @@ -205,8 +242,8 @@ * 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 @@ -244,6 +281,9 @@ $.extend(this, defaults, element_settings); + /** + * @type {Drupal.AjaxCommands} + */ this.commands = new Drupal.AjaxCommands(); this.instanceIndex = false; @@ -252,15 +292,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 +328,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 +356,26 @@ // Set the options for the ajaxSubmit function. // The 'this' variable will not persist inside of the options object. var ajax = this; + + /** + * Set the 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 +449,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 +487,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 +498,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 +515,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 +563,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 +603,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 +615,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 +660,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); @@ -608,6 +705,9 @@ /** * Handler for the form redirection completion. + * + * @param {Array.} response + * @param {number} status */ Drupal.Ajax.prototype.success = function (response, status) { // Remove the progress element. @@ -640,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; @@ -668,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. @@ -690,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 @@ -713,16 +856,17 @@ var new_content_wrapped = $('
').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 http://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 + // http://drupal.org/node/736066. if (new_content.length !== 1 || new_content.get(0).nodeType !== 1) { new_content = new_content_wrapped; } @@ -757,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; @@ -769,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; @@ -780,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')) { @@ -792,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); @@ -799,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; @@ -806,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) { @@ -825,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); @@ -832,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); @@ -840,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 @@ -853,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); @@ -864,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. diff --git a/core/misc/announce.js b/core/misc/announce.js index 9aafbbc..188684d 100644 --- a/core/misc/announce.js +++ b/core/misc/announce.js @@ -1,9 +1,12 @@ /** + * @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. + * + * @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. + * Builds a div element with the aria-live attribute and add it to the DOM. + * + * @type {Drupal~behavior} */ Drupal.behaviors.drupalAnnounce = { attach: function (context) { @@ -86,12 +91,14 @@ * These messages are then joined and append to the aria-live region as one * text node. * - * @param String text + * @param {string} text * A string to be read by the UA. - * @param String priority + * @param {string} [priority='polite'] * A string to indicate the priority of the message. Can be either * 'polite' or 'assertive'. Polite is the default. * + * @return {function} + * * @see http://www.w3.org/WAI/PF/aria-practices/#liveprops */ Drupal.announce = function (text, priority) { diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js index 92bb867..f826281 100644 --- a/core/misc/autocomplete.js +++ b/core/misc/autocomplete.js @@ -1,3 +1,8 @@ +/** + * @file + * Autocomplete based on jQuery UI. + */ + (function ($, Drupal) { "use strict"; @@ -7,7 +12,9 @@ /** * Helper splitting terms from the autocomplete value. * - * @param {String} value + * @function Drupal.autocomplete.splitValues + * + * @param {string} value * * @return {Array} */ @@ -43,9 +50,11 @@ /** * Returns the last value of an multi-value textfield. * - * @param {String} terms + * @function Drupal.autocomplete.extractLastTerm + * + * @param {string} terms * - * @return {String} + * @return {string} */ function extractLastTerm(terms) { return autocomplete.splitValues(terms).pop(); @@ -54,9 +63,11 @@ /** * The search handler is called before a search is performed. * - * @param {Object} event + * @function Drupal.autocomplete.options.search * - * @return {Boolean} + * @param {object} event + * + * @return {bool} */ function searchHandler(event) { var options = autocomplete.options; @@ -70,10 +81,10 @@ } /** - * jQuery UI autocomplete source callback. + * JQuery UI autocomplete source callback. * - * @param {Object} request - * @param {Function} response + * @param {object} request + * @param {function} response */ function sourceData(request, response) { var elementId = this.element.attr('id'); @@ -86,7 +97,7 @@ * Filter through the suggestions removing all terms already tagged and * display the available terms to the user. * - * @param {Object} suggestions + * @param {object} suggestions */ function showSuggestions(suggestions) { var tagged = autocomplete.splitValues(request.term); @@ -103,7 +114,7 @@ /** * Transforms the data object into an array and update autocomplete results. * - * @param {Object} data + * @param {object} data */ function sourceCallbackHandler(data) { autocomplete.cache[elementId][term] = data; @@ -128,7 +139,7 @@ /** * Handles an autocompletefocus event. * - * @return {Boolean} + * @return {bool} */ function focusHandler() { return false; @@ -137,10 +148,10 @@ /** * Handles an autocompleteselect event. * - * @param {Object} event - * @param {Object} ui + * @param {jQuery.Event} event + * @param {object} ui * - * @return {Boolean} + * @return {bool} */ function selectHandler(event, ui) { var terms = autocomplete.splitValues(event.target.value); @@ -161,10 +172,10 @@ /** * Override jQuery UI _renderItem function to output HTML by default. * - * @param {Object} ul - * @param {Object} item + * @param {object} ul + * @param {object} item * - * @return {Object} + * @return {object} */ function renderItem(ul, item) { return $("
  • ") @@ -174,6 +185,8 @@ /** * Attaches the autocomplete behavior to all required fields. + * + * @type {Drupal~behavior} */ Drupal.behaviors.autocomplete = { attach: function (context) { @@ -202,6 +215,8 @@ /** * Autocomplete object implementation. + * + * @namespace Drupal.autocomplete */ autocomplete = { cache: {}, @@ -209,6 +224,12 @@ splitValues: autocompleteSplitValues, extractLastTerm: extractLastTerm, // jQuery UI autocomplete options. + + /** + * JQuery UI option object. + * + * @name Drupal.autocomplete.options + */ options: { source: sourceData, focus: focusHandler, diff --git a/core/misc/batch.js b/core/misc/batch.js index ee6cf68..43242ea 100644 --- a/core/misc/batch.js +++ b/core/misc/batch.js @@ -1,12 +1,16 @@ /** + * @file * Drupal's batch API. */ + (function ($, Drupal) { "use strict"; /** * Attaches the batch behavior to progress bars. + * + * @type {Drupal~behavior} */ Drupal.behaviors.batch = { attach: function (context, settings) { diff --git a/core/misc/collapse.js b/core/misc/collapse.js index 6aa2e25..b4472f6 100644 --- a/core/misc/collapse.js +++ b/core/misc/collapse.js @@ -1,9 +1,18 @@ +/** + * @file + * Polyfill for HTML5 details elements. + */ + (function ($, Modernizr, Drupal) { "use strict"; /** - * The collapsible details object represents a single collapsible details element. + * The collapsible details object represents a single details element. + * + * @constructor Drupal.CollapsibleDetails + * + * @param {HTMLElement} node */ function CollapsibleDetails(node) { this.$node = $(node); @@ -20,22 +29,24 @@ this.setupLegend(); } - /** - * Extend CollapsibleDetails function. - */ - $.extend(CollapsibleDetails, { + $.extend(CollapsibleDetails, /** @lends Drupal.CollapsibleDetails */{ + /** * Holds references to instantiated CollapsibleDetails objects. + * + * @type {Array.} */ instances: [] }); - /** - * Extend CollapsibleDetails prototype. - */ - $.extend(CollapsibleDetails.prototype, { + $.extend(CollapsibleDetails.prototype, /** @lends Drupal.CollapsibleDetails# */{ + /** * Initialize and setup summary events and markup. + * + * @fires event:summaryUpdated + * + * @listens event:summaryUpdated */ setupSummary: function () { this.$summary = $(''); @@ -43,6 +54,7 @@ .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this)) .trigger('summaryUpdated'); }, + /** * Initialize and setup legend markup. */ @@ -65,20 +77,25 @@ .append(this.$summary) .on('click', $.proxy(this.onLegendClick, this)); }, + /** - * Handle legend clicks + * Handle legend clicks. + * + * @param {jQuery.Event} e */ onLegendClick: function (e) { this.toggle(); e.preventDefault(); }, + /** - * Update summary + * Update summary. */ onSummaryUpdated: function () { var text = $.trim(this.$node.drupalGetSummary()); this.$summary.html(text ? ' (' + text + ')' : ''); }, + /** * Toggle the visibility of a details element using smooth animations. */ @@ -95,6 +112,11 @@ } }); + /** + * Polyfill HTML5 details element. + * + * @type {Drupal~behavior} + */ Drupal.behaviors.collapse = { attach: function (context) { if (Modernizr.details) { diff --git a/core/misc/debounce.js b/core/misc/debounce.js index 45a5b87..0239ce6 100644 --- a/core/misc/debounce.js +++ b/core/misc/debounce.js @@ -1,7 +1,10 @@ /** - * Limits the invocations of a function in a given time frame. - * + * @file * Adapted from underscore.js with the addition Drupal namespace. + */ + +/** + * Limits the invocations of a function in a given time frame. * * The debounce function wrapper should be used sparingly. One clear use case * is limiting the invocation of a callback attached to the window resize event. @@ -11,13 +14,17 @@ * function can be written in such a way that it is only invoked under specific * conditions. * - * @param {Function} callback + * @param {function} func * The function to be invoked. - * - * @param {Number} wait + * @param {number} wait * The time period within which the callback function should only be * invoked once. For example if the wait period is 250ms, then the callback * will only be called at most 4 times per second. + * @param {bool} immediate + * Whether we wait at the beginning or end to execute the function. + * + * @return {function} + * The debounced function. */ Drupal.debounce = function (func, wait, immediate) { diff --git a/core/misc/dialog/dialog.ajax.js b/core/misc/dialog/dialog.ajax.js index e786d5a..d70ca70 100644 --- a/core/misc/dialog/dialog.ajax.js +++ b/core/misc/dialog/dialog.ajax.js @@ -7,6 +7,11 @@ "use strict"; + /** + * Initialize dialogs for Ajax purposes. + * + * @type {Drupal~behavior} + */ Drupal.behaviors.dialog = { attach: function (context, settings) { var $context = $(context); @@ -46,9 +51,10 @@ /** * Scan a dialog for any primary buttons and move them to the button area. * - * @param $dialog + * @param {jQuery} $dialog * An jQuery object containing the element that is the dialog target. - * @return + * + * @return {Array} * An array of buttons that need to be added to the button area. */ prepareDialogButtons: function ($dialog) { @@ -81,6 +87,12 @@ /** * Command to open a dialog. + * + * @param {Drupal.Ajax} ajax + * @param {object} response + * @param {number} [status] + * + * @return {bool|undefined} */ Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) { if (!response.selector) { @@ -107,7 +119,7 @@ response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog); } - // Bind dialogButtonsChange + // Bind dialogButtonsChange. $dialog.on('dialogButtonsChange', function () { var buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog); $dialog.dialog('option', 'buttons', buttons); @@ -131,6 +143,12 @@ * Command to close a dialog. * * If no selector is given, it defaults to trying to close the modal. + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {string} response.selector + * @param {bool} response.persist + * @param {number} [status] */ Drupal.AjaxCommands.prototype.closeDialog = function (ajax, response, status) { var $dialog = $(response.selector); @@ -141,14 +159,21 @@ } } - // Unbind dialogButtonsChange + // Unbind dialogButtonsChange. $dialog.off('dialogButtonsChange'); }; /** * Command to set a dialog property. * - * jQuery UI specific way of setting dialog options. + * JQuery UI specific way of setting dialog options. + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {string} response.selector + * @param {string} response.optionsName + * @param {string} response.optionValue + * @param {number} [status] */ Drupal.AjaxCommands.prototype.setDialogOption = function (ajax, response, status) { var $dialog = $(response.selector); @@ -159,6 +184,11 @@ /** * Binds a listener on dialog creation to handle the cancel link. + * + * @param {jQuery.Event} e + * @param {Drupal.dialog~dialogDefinition} dialog + * @param {jQuery} $element + * @param {object} settings */ $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { $element.on('click.dialog', '.dialog-cancel', function (e) { @@ -170,6 +200,10 @@ /** * Removes all 'dialog' listeners. + * + * @param {jQuery.Event} e + * @param {Drupal.dialog~dialogDefinition} dialog + * @param {jQuery} $element */ $(window).on('dialog:beforeclose', function (e, dialog, $element) { $element.off('.dialog'); diff --git a/core/misc/dialog/dialog.jquery-ui.js b/core/misc/dialog/dialog.jquery-ui.js index cd6e863..27fff75 100644 --- a/core/misc/dialog/dialog.jquery-ui.js +++ b/core/misc/dialog/dialog.jquery-ui.js @@ -2,6 +2,7 @@ * @file * Adds default classes to buttons for styling purposes. */ + (function ($) { "use strict"; diff --git a/core/misc/dialog/dialog.js b/core/misc/dialog/dialog.js index f63b405..cb7fb92 100644 --- a/core/misc/dialog/dialog.js +++ b/core/misc/dialog/dialog.js @@ -1,30 +1,64 @@ /** * @file + * Dialog API inspired by HTML5 dialog element. * - * Dialog API inspired by HTML5 dialog element: - * http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element + * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element */ + (function ($, Drupal, drupalSettings) { "use strict"; + /** + * Default dialog options. + * + * @type {object} + * + * @prop {bool} [autoOpen=true] + * @prop {string} [dialogClass=''] + * @prop {string} [buttonClass='button'] + * @prop {string} [buttonPrimaryClass='button--primary'] + * @prop {function} close + */ drupalSettings.dialog = { autoOpen: true, dialogClass: '', // Drupal-specific extensions: see dialog.jquery-ui.js. buttonClass: 'button', buttonPrimaryClass: 'button--primary', - // When using this API directly (when generating dialogs on the client side), - // you may want to override this method and do - // @code - // jQuery(event.target).remove() - // @endcode - // as well, to remove the dialog on closing. + // When using this API directly (when generating dialogs on the client + // side), you may want to override this method and do + // `jQuery(event.target).remove()` as well, to remove the dialog on + // closing. close: function (event) { Drupal.detachBehaviors(event.target, null, 'unload'); } }; + /** + * @typedef {object} Drupal.dialog~dialogDefinition + * + * @prop {boolean} open + * Is the dialog open or not. + * @prop {*} returnValue + * Return value of the dialog. + * @prop {function} show + * Method to display the dialog on the page. + * @prop {function} showModal + * Method to display the dialog as a modal on the page. + * @prop {function} close + * Method to hide the dialog from the page. + */ + + /** + * Polyfill HTML5 dialog element with jQueryUI. + * + * @param {HTMLElement} element + * @param {object} options + * jQuery UI options to be passed to the dialog. + * + * @return {Drupal.dialog~dialogDefinition} + */ Drupal.dialog = function (element, options) { function openDialog(settings) { diff --git a/core/misc/dialog/dialog.position.js b/core/misc/dialog/dialog.position.js index 3e08574..72e6ddf 100644 --- a/core/misc/dialog/dialog.position.js +++ b/core/misc/dialog/dialog.position.js @@ -1,3 +1,14 @@ +/** + * @file + * Positioning extensions for dialogs. + */ + +/** + * Triggers when content inside a dialog changes. + * + * @event dialogContentResize + */ + (function ($, Drupal, drupalSettings, debounce, displace) { "use strict"; @@ -12,6 +23,12 @@ * jQuery UI dialog. Although not a built-in jQuery UI option, this can * be disabled by setting autoResize: false in the options array when creating * a new Drupal.dialog(). + * + * @function Drupal.dialog~resetSize + * + * @param {jQuery.Event} event + * + * @fires event:dialogContentResize */ function resetSize(event) { var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position']; @@ -46,6 +63,12 @@ /** * Position the dialog's center at the center of displace.offsets boundaries. + * + * @function Drupal.dialog~resetPosition + * + * @param {object} options + * + * @return {object} */ function resetPosition(options) { var offsets = displace.offsets; diff --git a/core/misc/displace.js b/core/misc/displace.js index a52a826..d267d36 100644 --- a/core/misc/displace.js +++ b/core/misc/displace.js @@ -1,13 +1,38 @@ /** + * @file * Manages elements that can offset the size of the viewport. * * Measures and reports viewport offset dimensions from elements like the * toolbar that can potentially displace the positioning of other elements. */ + +/** + * @typedef {object} Drupal.displace~offset + * + * @prop {number} top + * @prop {number} left + * @prop {number} right + * @prop {number} bottom + */ + +/** + * Triggers when layout of the page changes. + * + * This is used to position fixed element on the page during page resize and + * Toolbar toggling. + * + * @event drupalViewportOffsetChange + */ + (function ($, Drupal, debounce) { "use strict"; + /** + * @name Drupal.displace.offsets + * + * @type {Drupal.displace~offset} + */ var offsets = { top: 0, right: 0, @@ -17,6 +42,8 @@ /** * Registers a resize handler on the window. + * + * @type {Drupal~behavior} */ Drupal.behaviors.drupalDisplace = { attach: function () { @@ -33,11 +60,15 @@ /** * Informs listeners of the current offset dimensions. * - * @param {boolean} broadcast - * (optional) When true or undefined, causes the recalculated offsets values to be + * @name Drupal.displace + * + * @fires event:drupalViewportOffsetChange + * + * @param {bool} [broadcast] + * When true or undefined, causes the recalculated offsets values to be * broadcast to listeners. * - * @return {object} + * @return {Drupal.displace~offset} * An object whose keys are the for sides an element -- top, right, bottom * and left. The value of each key is the viewport displacement distance for * that edge. @@ -53,7 +84,7 @@ /** * Determines the viewport offsets. * - * @return {object} + * @return {Drupal.displace~offset} * An object whose keys are the for sides an element -- top, right, bottom * and left. The value of each key is the viewport displacement distance for * that edge. @@ -75,6 +106,8 @@ * numeric value, that value will be used. If no value is provided, one will * be calculated using the element's dimensions and placement. * + * @name Drupal.displace.calculateOffset + * * @param {string} edge * The name of the edge to calculate. Can be 'top', 'right', * 'bottom' or 'left'. @@ -111,7 +144,7 @@ /** * Calculates displacement for element based on its dimensions and placement. * - * @param {jQuery} $el + * @param {HTMLElement} el * The jQuery element whose dimensions and placement will be measured. * * @param {string} edge @@ -163,15 +196,23 @@ /** * Assign the displace function to a property of the Drupal global object. + * + * @ignore */ Drupal.displace = displace; $.extend(Drupal.displace, { + /** - * Expose offsets to other scripts to avoid having to recalculate offsets + * Expose offsets to other scripts to avoid having to recalculate offsets. + * + * @ignore */ offsets: offsets, + /** * Expose method to compute a single edge offsets. + * + * @ignore */ calculateOffset: calculateOffset }); diff --git a/core/misc/dropbutton/dropbutton.js b/core/misc/dropbutton/dropbutton.js index eebf354..787cc56 100644 --- a/core/misc/dropbutton/dropbutton.js +++ b/core/misc/dropbutton/dropbutton.js @@ -1,9 +1,16 @@ +/** + * @file + * Dropbutton feature. + */ + (function ($, Drupal) { "use strict"; /** * Process elements with the .dropbutton class on page load. + * + * @type {Drupal~behavior} */ Drupal.behaviors.dropButton = { attach: function (context, settings) { @@ -25,6 +32,10 @@ /** * Delegated callback for opening and closing dropbutton secondary actions. + * + * @function Drupal.DropButton~dropbuttonClickHandler + * + * @param {jQuery.Event} e */ function dropbuttonClickHandler(e) { e.preventDefault(); @@ -37,21 +48,36 @@ * All secondary actions beyond the first in the list are presented in a * dropdown list accessible through a toggle arrow associated with the button. * - * @param {jQuery} $dropbutton - * A jQuery element. + * @constructor Drupal.DropButton * - * @param {Object} settings + * @param {HTMLElement} dropbutton + * A DOM element. + * @param {object} settings * A list of options including: - * - {String} title: The text inside the toggle link element. This text is - * hidden from visual UAs. + * @param {string} settings.title + * The text inside the toggle link element. This text is hidden + * from visual UAs. */ function DropButton(dropbutton, settings) { // Merge defaults with settings. var options = $.extend({'title': Drupal.t('List additional actions')}, settings); var $dropbutton = $(dropbutton); + + /** + * @type {jQuery} + */ this.$dropbutton = $dropbutton; + + /** + * @type {jQuery} + */ this.$list = $dropbutton.find('.dropbutton'); - // Find actions and mark them. + + /** + * Find actions and mark them. + * + * @type {jQuery} + */ this.$actions = this.$list.find('li').addClass('dropbutton-action'); // Add the special dropdown only if there are hidden actions. @@ -67,18 +93,31 @@ this.$dropbutton .addClass('dropbutton-multiple') .on({ + /** * Adds a timeout to close the dropdown on mouseleave. + * + * @ignore */ 'mouseleave.dropbutton': $.proxy(this.hoverOut, this), + /** * Clears timeout when mouseout of the dropdown. + * + * @ignore */ 'mouseenter.dropbutton': $.proxy(this.hoverIn, this), + /** * Similar to mouseleave/mouseenter, but for keyboard navigation. + * + * @ignore */ 'focusout.dropbutton': $.proxy(this.focusOut, this), + + /** + * @ignore + */ 'focusin.dropbutton': $.proxy(this.focusIn, this) }); } @@ -90,11 +129,11 @@ /** * Extend the DropButton constructor. */ - $.extend(DropButton, { + $.extend(DropButton, /** @lends Drupal.DropButton */{ /** * Store all processed DropButtons. * - * @type {Array} + * @type {Array.} */ dropbuttons: [] }); @@ -102,12 +141,13 @@ /** * Extend the DropButton prototype. */ - $.extend(DropButton.prototype, { + $.extend(DropButton.prototype, /** @lends Drupal.DropButton# */{ + /** * Toggle the dropbutton open and closed. * - * @param {Boolean} show - * (optional) Force the dropbutton to open by passing true or to close by + * @param {bool} [show] + * Force the dropbutton to open by passing true or to close by * passing false. */ toggle: function (show) { @@ -116,6 +156,9 @@ this.$dropbutton.toggleClass('open', show); }, + /** + * @method + */ hoverIn: function () { // Clear any previous timer we were using. if (this.timerID) { @@ -123,37 +166,53 @@ } }, + /** + * @method + */ hoverOut: function () { // Wait half a second before closing. this.timerID = window.setTimeout($.proxy(this, 'close'), 500); }, + /** + * @method + */ open: function () { this.toggle(true); }, + /** + * @method + */ close: function () { this.toggle(false); }, + /** + * @param {jQuery.Event} e + */ focusOut: function (e) { this.hoverOut.call(this, e); }, + /** + * @param {jQuery.Event} e + */ focusIn: function (e) { this.hoverIn.call(this, e); } }); - $.extend(Drupal.theme, { + $.extend(Drupal.theme, /** @lends Drupal.theme */{ + /** * A toggle is an interactive element often bound to a click handler. * - * @param {Object} options - * - {String} title: (optional) The HTML anchor title attribute and - * text for the inner span element. + * @param {object} options + * @param {string} [options.title] + * The HTML anchor title attribute and text for the inner span element. * - * @return {String} + * @return {string} * A string representing a DOM fragment. */ dropbuttonToggle: function (options) { diff --git a/core/misc/drupal.js b/core/misc/drupal.js index 9e9d24e..c342c69 100644 --- a/core/misc/drupal.js +++ b/core/misc/drupal.js @@ -1,7 +1,40 @@ /** - * Base framework for Drupal-specific JavaScript, behaviors, and settings. + * @file + * Defines the Drupal JS API. */ -window.Drupal = {behaviors: {}}; + +/** + * A jQuery object. + * + * @typedef {object} jQuery + * + * @prop {number} length=0 + */ + +/** + * Variable generated by Drupal with all the configuration created from PHP. + * + * @global + * + * @var {object} drupalSettings + */ + +/** + * Variable generated by Drupal that holds all translated strings from PHP. + * + * @global + * + * @var {object} drupalTranslations + */ + +/** + * Global Drupal object. + * + * @global + * + * @namespace + */ +window.Drupal = {behaviors: {}, locale: {}}; // Class indicating that JS is enabled; used for styling purpose. document.documentElement.className += ' js'; @@ -18,16 +51,43 @@ if (window.jQuery) { "use strict"; /** - * Custom error type thrown after attach/detach if one or more behaviors failed. + * Custom error type thrown after attach/detach if one or more behaviors + * failed. + * + * @memberof Drupal * - * @param list + * @constructor + * + * @augments Error + * + * @param {Array} list * An array of errors thrown during attach/detach. - * @param event + * @param {string} event * A string containing either 'attach' or 'detach'. + * + * @inner */ function DrupalBehaviorError(list, event) { + + /** + * Setting name helps debuggers. + * + * @type {string} + */ this.name = 'DrupalBehaviorError'; + + /** + * Execution phase errors were triggered. + * + * @type {string} + */ this.event = event || 'attach'; + + /** + * All thrown errors. + * + * @type {Array.} + */ this.list = list; // Makes the list of errors readable. var messageList = []; @@ -36,48 +96,100 @@ if (window.jQuery) { for (var i = 0; i < il; i++) { messageList.push(this.list[i].behavior + ': ' + this.list[i].error.message); } + + /** + * Final message to send to debuggers. + * + * @type {string} + */ this.message = messageList.join(' ; '); } DrupalBehaviorError.prototype = new Error(); /** + * Callback function initializing code run on page load and Ajax requests. + * + * @callback Drupal~behaviorAttach + * + * @param {HTMLElement} context + * @param {object} settings + * + * @see Drupal.attachBehaviors + */ + + /** + * Callback function for reverting and cleaning up behavior initialization. + * + * @callback Drupal~behaviorDetach + * + * @param {HTMLElement} context + * @param {object} settings + * @param {string} trigger + * One of 'unload', 'serialize' or 'move'. + * + * @see Drupal.detachBehaviors + */ + + /** + * @typedef {object} Drupal~behavior + * + * @prop {Drupal~behaviorAttach} attach + * Function run on page load and after an AJAX call. + * @prop {Drupal~behaviorDetach} detach + * Function run when content is serialized or removed from the page. + */ + + /** + * Holds all initialization methods. + * + * @namespace Drupal.behaviors + * + * @type {Object.} + */ + + /** * Attach all registered behaviors to a page element. * - * Behaviors are event-triggered actions that attach to page elements, enhancing - * default non-JavaScript UIs. Behaviors are registered in the Drupal.behaviors - * object using the method 'attach' and optionally also 'detach' as follows: - * @code - * Drupal.behaviors.behaviorName = { - * attach: function (context, settings) { - * ... - * }, - * detach: function (context, settings, trigger) { - * ... - * } - * }; - * @endcode - * - * Drupal.attachBehaviors is added below to the jQuery.ready event and therefore - * runs on initial page load. Developers implementing Ajax in their solutions - * should also call this function after new page content has been loaded, - * feeding in an element to be processed, in order to attach all behaviors to - * the new content. + * Behaviors are event-triggered actions that attach to page elements, + * enhancing default non-JavaScript UIs. Behaviors are registered in the + * Drupal.behaviors object using the method 'attach' and optionally also + * 'detach' as follows: + * + * Drupal.attachBehaviors is added below to the jQuery.ready event and + * therefore runs on initial page load. Developers implementing Ajax in their + * solutions should also call this function after new page content has been + * loaded, feeding in an element to be processed, in order to attach all + * behaviors to the new content. * * Behaviors should use - * @code - * var elements = $(context).find(selector).once('behavior-name'); - * @endcode + * `var elements = $(context).find(selector).once('behavior-name');` * to ensure the behavior is attached only once to a given element. (Doing so - * enables the reprocessing of given elements, which may be needed on occasion - * despite the ability to limit behavior attachment to a particular element.) + * enables the reprocessing of given elements, which may be needed on + * occasion despite the ability to limit behavior attachment to a particular + * element.) * - * @param context - * An element to attach behaviors to. If none is given, the document element - * is used. - * @param settings + * @example + * Drupal.behaviors.behaviorName = { + * attach: function (context, settings) { + * ... + * }, + * detach: function (context, settings, trigger) { + * ... + * } + * }; + * + * @param {Element} context + * An element to attach behaviors to. If none is given, the document + * element is used. + * @param {object} settings * An object containing settings for the current context. If none is given, * the global drupalSettings object is used. + * + * @see Drupal~behaviorAttach + * @see Drupal.detachBehaviors + * + * @throws {Drupal~DrupalBehaviorError} */ Drupal.attachBehaviors = function (context, settings) { context = context || document; @@ -115,16 +227,16 @@ if (window.jQuery) { * * Such implementations should use .findOnce() and .removeOnce() to find * elements with their corresponding Drupal.behaviors.behaviorName.attach - * implementation, i.e. .removeOnce('behaviorName'), to ensure the behavior is - * detached only from previously processed elements. - * - * @param context - * An element to detach behaviors from. If none is given, the document element - * is used. - * @param settings - * An object containing settings for the current context. If none given, the - * global drupalSettings object is used. - * @param trigger + * implementation, i.e. .removeOnce('behaviorName'), to ensure the behavior + * is detached only from previously processed elements. + * + * @param {Element} context + * An element to detach behaviors from. If none is given, the document + * element is used. + * @param {object} settings + * An object containing settings for the current context. If none given, + * the global drupalSettings object is used. + * @param {string} trigger * A string containing what's causing the behaviors to be detached. The * possible triggers are: * - unload: (default) The context element is being removed from the DOM. @@ -143,6 +255,9 @@ if (window.jQuery) { * that WYSIWYG editors can update the hidden textarea to which they are * bound. * + * @throws {Drupal~DrupalBehaviorError} + * + * @see Drupal~behaviorDetach * @see Drupal.attachBehaviors */ Drupal.detachBehaviors = function (context, settings, trigger) { @@ -171,7 +286,12 @@ if (window.jQuery) { /** * Helper to test document width for mobile configurations. - * @todo Temporary solution for the mobile initiative. + * + * @param {number} [width] + * + * @return {bool} + * + * @deprecated Temporary solution for the mobile initiative. */ Drupal.checkWidthBreakpoint = function (width) { width = width || drupalSettings.widthBreakpoint || 640; @@ -181,10 +301,12 @@ if (window.jQuery) { /** * Encode special characters in a plain-text string for display as HTML. * - * @param str + * @param {string} str * The string to be encoded. - * @return + * + * @return {string} * The encoded string. + * * @ingroup sanitization */ Drupal.checkPlain = function (str) { @@ -199,9 +321,9 @@ if (window.jQuery) { /** * Replace placeholders with sanitized values in a string. * - * @param {String} str + * @param {string} str * A string with placeholders. - * @param {Object} args + * @param {object} args * An object of replacements pairs to make. Incidences of any key in this * array are replaced with the corresponding value. Based on the first * character of the key, the value is escaped and/or themed: @@ -210,11 +332,9 @@ if (window.jQuery) { * - %variable: escape text and theme as a placeholder for user-submitted * content (checkPlain + Drupal.theme('placeholder')) * - * @return {String} - * Returns the replaced string. + * @return {string} * - * @see Drupal.t() - * @ingroup sanitization + * @see Drupal.t */ Drupal.formatString = function (str, args) { // Keep args intact. @@ -227,10 +347,12 @@ if (window.jQuery) { case '@': processedArgs[key] = Drupal.checkPlain(args[key]); break; + // Pass-through. case '!': processedArgs[key] = args[key]; break; + // Escaped and placeholder. default: processedArgs[key] = Drupal.theme('placeholder', args[key]); @@ -248,14 +370,14 @@ if (window.jQuery) { * The longest keys will be tried first. Once a substring has been replaced, * its new value will not be searched again. * - * @param {String} str + * @param {string} str * A string with placeholders. - * @param {Object} args + * @param {object} args * Key-value pairs. * @param {Array|null} keys * Array of keys from the "args". Internal use only. * - * @return {String} + * @return {string} * Returns the replaced string. */ Drupal.stringReplace = function (str, args, keys) { @@ -299,18 +421,17 @@ if (window.jQuery) { * * See the documentation of the server-side t() function for further details. * - * @param str + * @param {string} str * A string containing the English string to translate. - * @param args + * @param {Object.} [args] * An object of replacements pairs to make after translation. Incidences * of any key in this array are replaced with the corresponding value. - * See Drupal.formatString(). + * See {@link Drupal.formatString}. + * @param {object} [options] + * @param {string} [options.context=''] + * The context the source string belongs to. * - * @param options - * - 'context' (defaults to the empty context): The context the source string - * belongs to. - * - * @return + * @return {string} * The translated string. */ Drupal.t = function (str, args, options) { @@ -330,6 +451,11 @@ if (window.jQuery) { /** * Returns the URL to a Drupal page. + * + * @param {string} path + * Drupal path to transform to URL. + * + * @return {string} */ Drupal.url = function (path) { return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path; @@ -346,26 +472,26 @@ if (window.jQuery) { * \Drupal\Core\StringTranslation\TranslationInterface::formatPlural() * function for more details. * - * @param {Number} count + * @param {number} count * The item count to display. - * @param {String} singular + * @param {string} singular * The string for the singular case. Please make sure it is clear this is * singular, to ease translation (e.g. use "1 new comment" instead of "1 * new"). Do not use @count in the singular string. - * @param {String} plural + * @param {string} plural * The string for the plural case. Please make sure it is clear this is * plural, to ease translation. Use @count in place of the item count, as in * "@count new comments". - * @param {Object} args + * @param {object} [args] * An object of replacements pairs to make after translation. Incidences * of any key in this array are replaced with the corresponding value. * See Drupal.formatString(). * Note that you do not need to include @count in this array. * This replacement is done automatically for the plural case. - * @param {Object} options + * @param {object} [options] * The options to pass to the Drupal.t() function. * - * @return {String} + * @return {string} * A translated string. */ Drupal.formatPlural = function (count, singular, plural, args, options) { @@ -391,6 +517,11 @@ if (window.jQuery) { * Encodes a Drupal path for use in a URL. * * For aesthetic reasons slashes are not escaped. + * + * @param {string} item + * Unencoded path. + * + * @return {string} */ Drupal.encodePath = function (item) { return window.encodeURIComponent(item).replace(/%2F/g, '/'); @@ -406,13 +537,16 @@ if (window.jQuery) { * * For example, to retrieve the HTML for text that should be emphasized and * displayed as a placeholder inside a sentence, call - * Drupal.theme('placeholder', text). + * `Drupal.theme('placeholder', text)`. + * + * @namespace * - * @param func + * @param {function} func * The name of the theme function to call. - * @param ... + * @param {...args} * Additional arguments to pass along to the theme function. - * @return + * + * @return {string|object|HTMLElement|jQuery} * Any data the theme function returns. This could be a plain HTML string, * but also a complex object. */ @@ -426,9 +560,10 @@ if (window.jQuery) { /** * Formats text for emphasized display in a placeholder inside a sentence. * - * @param str + * @param {string} str * The text to format (plain-text). - * @return + * + * @return {string} * The formatted text (html). */ Drupal.theme.placeholder = function (str) { diff --git a/core/misc/form.js b/core/misc/form.js index b1d1fff..d73db7a 100644 --- a/core/misc/form.js +++ b/core/misc/form.js @@ -1,9 +1,25 @@ +/** + * @file + * Form features. + */ + +/** + * Triggers when a value in the form changed. + * + * The event triggers when content is typed or pasted in a text field, before + * the change event triggers. + * + * @event formUpdated + */ + (function ($, Drupal, debounce) { "use strict"; /** * Retrieves the summary for the first element. + * + * @return {string} */ $.fn.drupalGetSummary = function () { var callback = this.data('summaryCallback'); @@ -13,9 +29,15 @@ /** * Sets the summary for all matched elements. * - * @param callback + * @param {function} callback * Either a function that will be called each time the summary is * retrieved or a string (which is returned each time). + * + * @return {jQuery} + * + * @fires event:summaryUpdated + * + * @listens event:formUpdated */ $.fn.drupalSetSummary = function (callback) { var self = this; @@ -43,13 +65,13 @@ /** * Prevents consecutive form submissions of identical form values. * - * Repetitive form submissions that would submit the identical form values are - * prevented, unless the form values are different to the previously submitted - * values. + * Repetitive form submissions that would submit the identical form values + * are prevented, unless the form values are different to the previously + * submitted values. * - * This is a simplified re-implementation of a user-agent behavior that should - * be natively supported by major web browsers, but at this time, only Firefox - * has a built-in protection. + * This is a simplified re-implementation of a user-agent behavior that + * should be natively supported by major web browsers, but at this time, only + * Firefox has a built-in protection. * * A form value-based approach ensures that the constraint is triggered for * consecutive, identical form submissions only. Compared to that, a form @@ -57,26 +79,28 @@ * technically not required and (2) require more complex state management if * there are multiple buttons in a form. * - * This implementation is based on form-level submit events only and relies on - * jQuery's serialize() method to determine submitted form values. As such, the - * following limitations exist: + * This implementation is based on form-level submit events only and relies + * on jQuery's serialize() method to determine submitted form values. As such, + * the following limitations exist: * * - Event handlers on form buttons that preventDefault() do not receive a * double-submit protection. That is deemed to be fine, since such button - * events typically trigger reversible client-side or server-side operations - * that are local to the context of a form only. - * - Changed values in advanced form controls, such as file inputs, are not part - * of the form values being compared between consecutive form submits (due to - * limitations of jQuery.serialize()). That is deemed to be acceptable, - * because if the user forgot to attach a file, then the size of HTTP payload - * will most likely be small enough to be fully passed to the server endpoint - * within (milli)seconds. If a user mistakenly attached a wrong file and is - * technically versed enough to cancel the form submission (and HTTP payload) - * in order to attach a different file, then that edge-case is not supported - * here. - * - * Lastly, all forms submitted via HTTP GET are idempotent by definition of HTTP - * standards, so excluded in this implementation. + * events typically trigger reversible client-side or server-side + * operations that are local to the context of a form only. + * - Changed values in advanced form controls, such as file inputs, are not + * part of the form values being compared between consecutive form submits + * (due to limitations of jQuery.serialize()). That is deemed to be + * acceptable, because if the user forgot to attach a file, then the size of + * HTTP payload will most likely be small enough to be fully passed to the + * server endpoint within (milli)seconds. If a user mistakenly attached a + * wrong file and is technically versed enough to cancel the form submission + * (and HTTP payload) in order to attach a different file, then that + * edge-case is not supported here. + * + * Lastly, all forms submitted via HTTP GET are idempotent by definition of + * HTTP standards, so excluded in this implementation. + * + * @type {Drupal~behavior} */ Drupal.behaviors.formSingleSubmit = { attach: function () { @@ -99,6 +123,10 @@ /** * Sends a 'formUpdated' event each time a form element is modified. + * + * @param {HTMLElement} element + * + * @fires event:formUpdated */ function triggerFormUpdated(element) { $(element).trigger('formUpdated'); @@ -108,6 +136,7 @@ * Collects the IDs of all form fields in the given form. * * @param {HTMLFormElement} form + * * @return {Array} */ function fieldsList(form) { @@ -122,6 +151,10 @@ /** * Triggers the 'formUpdated' event on form elements when they are modified. + * + * @type {Drupal~behavior} + * + * @fires event:formUpdated */ Drupal.behaviors.formUpdated = { attach: function (context) { @@ -132,7 +165,8 @@ if ($forms.length) { // Initialize form behaviors, use $.makeArray to be able to use native - // forEach array method and have the callback parameters in the right order. + // forEach array method and have the callback parameters in the right + // order. $.makeArray($forms).forEach(function (form) { var events = 'change.formUpdated keypress.formUpdated'; var eventHandler = debounce(function (event) { triggerFormUpdated(event.target); }, 300); @@ -147,7 +181,7 @@ formFields = fieldsList(context).join(','); // @todo replace with form.getAttribute() when #1979468 is in. var currentFields = $(context).attr('data-drupal-form-fields'); - // if there has been a change in the fields or their order, trigger + // If there has been a change in the fields or their order, trigger // formUpdated. if (formFields !== currentFields) { triggerFormUpdated(context); @@ -172,6 +206,8 @@ /** * Prepopulate form fields with information from the visitor browser. + * + * @type {Drupal~behavior} */ Drupal.behaviors.fillUserInfoFromBrowser = { attach: function (context, settings) { diff --git a/core/misc/machine-name.js b/core/misc/machine-name.js index 5319aa9..25e8b84 100644 --- a/core/misc/machine-name.js +++ b/core/misc/machine-name.js @@ -1,28 +1,39 @@ +/** + * @file + * Machine name functionality. + */ + (function ($, Drupal, drupalSettings) { "use strict"; /** * Attach the machine-readable name form element behavior. + * + * @type {Drupal~behavior} */ Drupal.behaviors.machineName = { + /** * Attaches the behavior. * - * @param settings.machineName - * A list of elements to process, keyed by the HTML ID of the form element - * containing the human-readable value. Each element is an object defining - * the following properties: + * @param {Element} context + * @param {object} settings + * @param {object} settings.machineName + * A list of elements to process, keyed by the HTML ID of the form + * element containing the human-readable value. Each element is an object + * defining the following properties: * - target: The HTML ID of the machine name form element. - * - suffix: The HTML ID of a container to show the machine name preview in - * (usually a field suffix after the human-readable name form element). + * - suffix: The HTML ID of a container to show the machine name preview + * in (usually a field suffix after the human-readable name + * form element). * - label: The label to show for the machine name preview. * - replace_pattern: A regular expression (without modifiers) matching * disallowed characters in the machine name; e.g., '[^a-z0-9]+'. - * - replace: A character to replace disallowed characters with; e.g., '_' - * or '-'. - * - standalone: Whether the preview should stay in its own element rather - * than the suffix of the source element. + * - replace: A character to replace disallowed characters with; e.g., + * '_' or '-'. + * - standalone: Whether the preview should stay in its own element + * rather than the suffix of the source element. * - field_prefix: The #field_prefix of the form element. * - field_suffix: The #field_suffix of the form element. */ @@ -95,9 +106,9 @@ options.maxlength = $target.attr('maxlength'); // Hide the form item container of the machine name form element. $wrapper.hide(); - // Determine the initial machine name value. Unless the machine name form - // element is disabled or not empty, the initial default value is based on - // the human-readable form element value. + // Determine the initial machine name value. Unless the machine name + // form element is disabled or not empty, the initial default value is + // based on the human-readable form element value. if ($target.is(':disabled') || $target.val() !== '') { machine = $target.val(); } @@ -160,17 +171,19 @@ /** * Transliterate a human-readable name to a machine name. * - * @param source + * @param {string} source * A string to transliterate. - * @param settings - * The machine name settings for the corresponding field, containing: - * - replace_pattern: A regular expression (without modifiers) matching - * disallowed characters in the machine name; e.g., '[^a-z0-9]+'. - * - replace: A character to replace disallowed characters with; e.g., '_' - * or '-'. - * - maxlength: The maximum length of the machine name. + * @param {object} settings + * The machine name settings for the corresponding field. + * @param {string} settings.replace_pattern + * A regular expression (without modifiers) matching disallowed characters + * in the machine name; e.g., '[^a-z0-9]+'. + * @param {string} settings.replace + * A character to replace disallowed characters with; e.g., '_' or '-'. + * @param {number} settings.maxlength + * The maximum length of the machine name. * - * @return + * @return {jQuery} * The transliterated source string. */ transliterate: function (source, settings) { diff --git a/core/misc/progress.js b/core/misc/progress.js index 7305d24..62b9b27 100644 --- a/core/misc/progress.js +++ b/core/misc/progress.js @@ -1,3 +1,8 @@ +/** + * @file + * Progress bar. + */ + (function ($, Drupal) { "use strict"; @@ -5,7 +10,9 @@ /** * Theme function for the progress bar. * - * @return + * @param {string} id + * + * @return {string} * The HTML for the progress bar. */ Drupal.theme.progressBar = function (id) { @@ -21,11 +28,19 @@ * A progressbar object. Initialized with the given id. Must be inserted into * the DOM afterwards through progressBar.element. * - * method is the function which will perform the HTTP request to get the + * Method is the function which will perform the HTTP request to get the * progress bar state. Either "GET" or "POST". * - * e.g. pb = new Drupal.ProgressBar('myProgressBar'); - * some_element.appendChild(pb.element); + * @example + * pb = new Drupal.ProgressBar('myProgressBar'); + * some_element.appendChild(pb.element); + * + * @constructor + * + * @param {string} id + * @param {function} updateCallback + * @param {string} method + * @param {function} errorCallback */ Drupal.ProgressBar = function (id, updateCallback, method, errorCallback) { this.id = id; @@ -33,14 +48,21 @@ this.updateCallback = updateCallback; this.errorCallback = errorCallback; - // The WAI-ARIA setting aria-live="polite" will announce changes after users - // have completed their current activity and not interrupt the screen reader. + // The WAI-ARIA setting aria-live="polite" will announce changes after + // users + // have completed their current activity and not interrupt the screen + // reader. this.element = $(Drupal.theme('progressBar', id)); }; - $.extend(Drupal.ProgressBar.prototype, { + $.extend(Drupal.ProgressBar.prototype, /** @lends Drupal.ProgressBar# */{ + /** * Set the percentage and status message for the progressbar. + * + * @param {number} percentage + * @param {string} message + * @param {string} label */ setProgress: function (percentage, message, label) { if (percentage >= 0 && percentage <= 100) { @@ -56,6 +78,9 @@ /** * Start monitoring progress via Ajax. + * + * @param {string} uri + * @param {number} delay */ startMonitoring: function (uri, delay) { this.delay = delay; @@ -109,6 +134,8 @@ /** * Display errors on the page. + * + * @param {string} string */ displayError: function (string) { var error = $('
    ').html(string); diff --git a/core/misc/states.js b/core/misc/states.js index b969da7..3a67aea 100644 --- a/core/misc/states.js +++ b/core/misc/states.js @@ -1,6 +1,8 @@ /** + * @file * Drupal's states library. */ + (function ($) { "use strict"; @@ -10,14 +12,21 @@ * * Having the local states variable allows us to use the States namespace * without having to always declare "Drupal.states". + * + * @namespace Drupal.states */ var states = Drupal.states = { - // An array of functions that should be postponed. + + /** + * An array of functions that should be postponed. + */ postponed: [] }; /** * Attaches the states. + * + * @type {Drupal~behavior} */ Drupal.behaviors.states = { attach: function (context, settings) { @@ -48,13 +57,18 @@ /** * Object representing an element that depends on other elements. * - * @param args - * Object with the following keys (all of which are required): - * - element: A jQuery object of the dependent element - * - state: A State object describing the state that is dependent - * - constraints: An object with dependency specifications. Lists all elements - * that this element depends on. It can be nested and can contain arbitrary - * AND and OR clauses. + * @constructor Drupal.states.Dependent + * + * @param {object} args + * Object with the following keys (all of which are required) + * @param {jQuery} args.element + * A jQuery object of the dependent element + * @param {Drupal.states.State} args.state + * A State object describing the state that is dependent + * @param {object} args.constraints + * An object with dependency specifications. Lists all elements that this + * element depends on. It can be nested and can contain + * arbitrary AND and OR clauses. */ states.Dependent = function (args) { $.extend(this, {values: {}, oldValue: null}, args); @@ -71,6 +85,12 @@ * Comparison functions for comparing the value of an element with the * specification from the dependency settings. If the object type can't be * found in this list, the === operator is used by default. + * + * @name Drupal.states.Dependent.comparisons + * + * @prop {function} RegExp + * @prop {function} Function + * @prop {function} Number */ states.Dependent.comparisons = { 'RegExp': function (reference, value) { @@ -81,21 +101,25 @@ return reference(value); }, 'Number': function (reference, value) { - // If "reference" is a number and "value" is a string, then cast reference - // as a string before applying the strict comparison in compare(). Otherwise - // numeric keys in the form's #states array fail to match string values - // returned from jQuery's val(). + // If "reference" is a number and "value" is a string, then cast + // reference as a string before applying the strict comparison in + // compare(). + // Otherwise numeric keys in the form's #states array fail to match + // string values returned from jQuery's val(). return (typeof value === 'string') ? compare(reference.toString(), value) : compare(reference, value); } }; states.Dependent.prototype = { + /** * Initializes one of the elements this dependent depends on. * - * @param selector + * @memberof Drupal.states.Dependent# + * + * @param {string} selector * The CSS selector describing the dependee. - * @param dependeeStates + * @param {object} dependeeStates * The list of states that have to be monitored for tracking the * dependee's compliance status. */ @@ -113,7 +137,8 @@ for (var i in dependeeStates) { if (dependeeStates.hasOwnProperty(i)) { state = dependeeStates[i]; - // Make sure we're not initializing this selector/state combination twice. + // Make sure we're not initializing this selector/state combination + // twice. if ($.inArray(state, dependeeStates) === -1) { continue; } @@ -135,14 +160,16 @@ /** * Compares a value with a reference value. * - * @param reference + * @memberof Drupal.states.Dependent# + * + * @param {object} reference * The value used for reference. - * @param selector + * @param {string} selector * CSS selector describing the dependee. - * @param state + * @param {Drupal.states.State} state * A State object describing the dependee's updated state. * - * @return + * @return {bool} * true or false. */ compare: function (reference, selector, state) { @@ -160,11 +187,13 @@ /** * Update the value of a dependee's state. * - * @param selector + * @memberof Drupal.states.Dependent# + * + * @param {string} selector * CSS selector describing the dependee. - * @param state + * @param {Drupal.states.state} state * A State object describing the dependee's updated state. - * @param value + * @param {string} value * The new value for the dependee's updated state. */ update: function (selector, state, value) { @@ -177,6 +206,8 @@ /** * Triggers change events in case a state changed. + * + * @memberof Drupal.states.Dependent# */ reevaluate: function () { // Check whether any constraint for this dependent state is satisfied. @@ -200,14 +231,16 @@ /** * Evaluates child constraints to determine if a constraint is satisfied. * - * @param constraints + * @memberof Drupal.states.Dependent# + * + * @param {object|Array} constraints * A constraint object or an array of constraints. - * @param selector + * @param {string} selector * The selector for these constraints. If undefined, there isn't yet a * selector that these constraints apply to. In that case, the keys of the * object are interpreted as the selector if encountered. * - * @return + * @return {bool} * true or false, depending on whether these constraints are satisfied. */ verifyConstraints: function (constraints, selector) { @@ -219,8 +252,8 @@ for (var i = 0; i < len; i++) { if (constraints[i] !== 'xor') { var constraint = this.checkConstraints(constraints[i], selector, i); - // Return if this is OR and we have a satisfied constraint or if this - // is XOR and we have a second satisfied constraint. + // Return if this is OR and we have a satisfied constraint or if + // this is XOR and we have a second satisfied constraint. if (constraint && (hasXor || result)) { return hasXor; } @@ -229,15 +262,15 @@ } } // Make sure we don't try to iterate over things other than objects. This - // shouldn't normally occur, but in case the condition definition is bogus, - // we don't want to end up with an infinite loop. + // shouldn't normally occur, but in case the condition definition is + // bogus, we don't want to end up with an infinite loop. else if ($.isPlainObject(constraints)) { // This constraint is an object (AND). for (var n in constraints) { if (constraints.hasOwnProperty(n)) { result = ternary(result, this.checkConstraints(constraints[n], selector, n)); - // False and anything else will evaluate to false, so return when any - // false condition is found. + // False and anything else will evaluate to false, so return when + // any false condition is found. if (result === false) { return false; } } } @@ -248,27 +281,28 @@ /** * Checks whether the value matches the requirements for this constraint. * - * @param value + * @memberof Drupal.states.Dependent# + * + * @param {string|Array|object} value * Either the value of a state or an array/object of constraints. In the * latter case, resolving the constraint continues. - * @param selector + * @param {string} [selector] * The selector for this constraint. If undefined, there isn't yet a - * selector that this constraint applies to. In that case, the state key is - * propagates to a selector and resolving continues. - * @param state + * selector that this constraint applies to. In that case, the state key + * is propagates to a selector and resolving continues. + * @param {Drupal.states.State} [state] * The state to check for this constraint. If undefined, resolving - * continues. - * If both selector and state aren't undefined and valid non-numeric - * strings, a lookup for the actual value of that selector's state is - * performed. This parameter is not a State object but a pristine state - * string. + * continues. If both selector and state aren't undefined and valid + * non-numeric strings, a lookup for the actual value of that selector's + * state is performed. This parameter is not a State object but a pristine + * state string. * - * @return + * @return {bool} * true or false, depending on whether this constraint is satisfied. */ checkConstraints: function (value, selector, state) { - // Normalize the last parameter. If it's non-numeric, we treat it either as - // a selector (in case there isn't one yet) or as a trigger/state. + // Normalize the last parameter. If it's non-numeric, we treat it either + // as a selector (in case there isn't one yet) or as a trigger/state. if (typeof state !== 'string' || (/[0-9]/).test(state[0])) { state = null; } @@ -279,7 +313,7 @@ } if (state !== null) { - // constraints is the actual constraints of an element to check for. + // Constraints is the actual constraints of an element to check for. state = states.State.sanitize(state); return invert(this.compare(value, selector, state), state.invert); } @@ -291,11 +325,15 @@ /** * Gathers information about all required triggers. + * + * @memberof Drupal.states.Dependent# + * + * @return {object} */ getDependees: function () { var cache = {}; - // Swivel the lookup function so that we can record all available selector- - // state combinations for initialization. + // Swivel the lookup function so that we can record all available + // selector- state combinations for initialization. var _compare = this.compare; this.compare = function (reference, selector, state) { (cache[selector] || (cache[selector] = [])).push(state.name); @@ -317,6 +355,11 @@ } }; + /** + * @constructor Drupal.states.Trigger + * + * @param {object} args + */ states.Trigger = function (args) { $.extend(this, args); @@ -332,6 +375,10 @@ }; states.Trigger.prototype = { + + /** + * @memberof Drupal.states.Trigger# + */ initialize: function () { var trigger = states.Trigger.states[this.state]; @@ -351,6 +398,12 @@ this.element.data('trigger:' + this.state, true); }, + /** + * @memberof Drupal.states.Trigger# + * + * @param {jQuery.Event} event + * @param {function} valueFn + */ defaultTrigger: function (event, valueFn) { var oldValue = valueFn.call(this.element); @@ -376,9 +429,16 @@ * of an element. Whenever an element depends on the state of another element, * one of these trigger functions is added to the dependee so that the * dependent element can be updated. + * + * @name Drupal.states.Trigger.states + * + * @prop empty + * @prop checked + * @prop value + * @prop collapsed */ states.Trigger.states = { - // 'empty' describes the state to be monitored + // 'empty' describes the state to be monitored. empty: { // 'keyup' is the (native DOM) event that we watch for. 'keyup': function () { @@ -390,9 +450,9 @@ checked: { 'change': function () { - // prop() and attr() only takes the first element into account. To support - // selectors matching multiple checkboxes, iterate over all and return - // whether any is checked. + // prop() and attr() only takes the first element into account. To + // support selectors matching multiple checkboxes, iterate over all and + // return whether any is checked. var checked = false; this.each(function () { // Use prop() here as we want a boolean of the checkbox state. @@ -434,9 +494,16 @@ /** * A state object is used for describing the state and performing aliasing. + * + * @constructor Drupal.states.State + * + * @param {string} state */ states.State = function (state) { - // We may need the original unresolved name later. + + /** + * Original unresolved name. + */ this.pristine = this.name = state; // Normalize the state name. @@ -460,6 +527,12 @@ /** * Creates a new State object by sanitizing the passed value. + * + * @name Drupal.states.State.sanitize + * + * @param {string|Drupal.states.State} state + * + * @return {Drupal.states.state} */ states.State.sanitize = function (state) { if (state instanceof states.State) { @@ -471,8 +544,10 @@ }; /** - * This list of aliases is used to normalize states and associates negated names - * with their respective inverse state. + * This list of aliases is used to normalize states and associates negated + * names with their respective inverse state. + * + * @name Drupal.states.State.aliases */ states.State.aliases = { 'enabled': '!disabled', @@ -490,10 +565,18 @@ }; states.State.prototype = { + + /** + * @memberof Drupal.states.State# + */ invert: false, /** * Ensures that just using the state object returns the name. + * + * @memberof Drupal.states.State# + * + * @return {string} */ toString: function () { return this.name; @@ -563,6 +646,13 @@ /** * Bitwise AND with a third undefined state. + * + * @function Drupal.states~ternary + * + * @param {*} a + * @param {*} b + * + * @return {bool} */ function ternary(a, b) { if (typeof a === 'undefined') { @@ -578,6 +668,13 @@ /** * Inverts a (if it's not undefined) when invertState is true. + * + * @function Drupal.states~invert + * + * @param {*} a + * @param {bool} invertState + * + * @return {bool} */ function invert(a, invertState) { return (invertState && typeof a !== 'undefined') ? !a : a; @@ -585,6 +682,13 @@ /** * Compares two values while ignoring undefined values. + * + * @function Drupal.states~compare + * + * @param {*} a + * @param {*} b + * + * @return {bool} */ function compare(a, b) { if (a === b) { diff --git a/core/misc/tabbingmanager.js b/core/misc/tabbingmanager.js index dc21c02..8a68ee0 100644 --- a/core/misc/tabbingmanager.js +++ b/core/misc/tabbingmanager.js @@ -3,12 +3,38 @@ * Manages page tabbing modifications made by modules. */ +/** + * Allow modules to respond to the constrain event. + * + * @event drupalTabbingConstrained + */ + +/** + * Allow modules to respond to the tabbingContext release event. + * + * @event drupalTabbingContextReleased + */ + +/** + * Allow modules to respond to the constrain event. + * + * @event drupalTabbingContextActivated + */ + +/** + * Allow modules to respond to the constrain event. + * + * @event drupalTabbingContextDeactivated + */ + (function ($, Drupal) { "use strict"; /** * Provides an API for managing page tabbing order modifications. + * + * @constructor */ function TabbingManager() { // Tabbing sets are stored as a stack. The active set is at the top of the @@ -22,23 +48,26 @@ /** * Add public methods to the TabbingManager class. */ - $.extend(TabbingManager.prototype, { + $.extend(TabbingManager.prototype, /** @lends TabbingManager# */{ + /** * Constrain tabbing to the specified set of elements only. * - * Makes elements outside of the specified set of elements unreachable via the - * tab key. + * Makes elements outside of the specified set of elements unreachable via + * the tab key. + * + * @fires event:drupalTabbingConstrained * - * @param jQuery elements - * The set of elements to which tabbing should be constrained. Can also be - * a jQuery-compatible selector string. + * @param {jQuery} elements + * The set of elements to which tabbing should be constrained. Can also + * be a jQuery-compatible selector string. * - * @return TabbingContext + * @return {TabbingContext} */ constrain: function (elements) { // Deactivate all tabbingContexts to prepare for the new constraint. A - // tabbingContext instance will only be reactivated if the stack is unwound - // to it in the _unwindStack() method. + // tabbingContext instance will only be reactivated if the stack is + // unwound to it in the _unwindStack() method. var il = this.stack.length; for (var i = 0; i < il; i++) { this.stack[i].deactivate(); @@ -68,14 +97,15 @@ }, /** - * Restores a former tabbingContext when an active tabbingContext is released. + * Restores a former tabbingContext when an active one is released. * - * The TabbingManager stack of tabbingContext instances will be unwound from - * the top-most released tabbingContext down to the first non-released + * The TabbingManager stack of tabbingContext instances will be unwound + * from the top-most released tabbingContext down to the first non-released * tabbingContext instance. This non-released instance is then activated. */ release: function () { - // Unwind as far as possible: find the topmost non-released tabbingContext. + // Unwind as far as possible: find the topmost non-released + // tabbingContext. var toActivate = this.stack.length - 1; while (toActivate >= 0 && this.stack[toActivate].released) { toActivate--; @@ -94,11 +124,11 @@ /** * Makes all elements outside the of the tabbingContext's set untabbable. * - * Elements made untabbable have their original tabindex and autofocus values - * stored so that they might be restored later when this tabbingContext - * is deactivated. + * Elements made untabbable have their original tabindex and autofocus + * values stored so that they might be restored later when this + * tabbingContext is deactivated. * - * @param TabbingContext tabbingContext + * @param {TabbingContext} tabbingContext * The TabbingContext instance that has been activated. */ activate: function (tabbingContext) { @@ -115,17 +145,18 @@ for (var i = 0; i < il; i++) { this.recordTabindex($disabledSet.eq(i), level); } - // Make all tabbable elements outside of the active tabbing set unreachable. + // Make all tabbable elements outside of the active tabbing set + // unreachable. $disabledSet .prop('tabindex', -1) .prop('autofocus', false); - // Set focus on an element in the tabbingContext's set of tabbable elements. - // First, check if there is an element with an autofocus attribute. Select - // the last one from the DOM order. + // Set focus on an element in the tabbingContext's set of tabbable + // elements. First, check if there is an element with an autofocus + // attribute. Select the last one from the DOM order. var $hasFocus = $set.filter('[autofocus]').eq(-1); - // If no element in the tabbable set has an autofocus attribute, select the - // first element in the set. + // If no element in the tabbable set has an autofocus attribute, select + // the first element in the set. if ($hasFocus.length === 0) { $hasFocus = $set.eq(0); } @@ -135,10 +166,10 @@ /** * Restores that tabbable state of a tabbingContext's disabled elements. * - * Elements that were made untabbable have their original tabindex and autofocus - * values restored. + * Elements that were made untabbable have their original tabindex and + * autofocus values restored. * - * @param TabbingContext tabbingContext + * @param {TabbingContext} tabbingContext * The TabbingContext instance that has been deactivated. */ deactivate: function (tabbingContext) { @@ -153,9 +184,9 @@ /** * Records the tabindex and autofocus values of an untabbable element. * - * @param jQuery $set + * @param {jQuery} $el * The set of elements that have been disabled. - * @param Number level + * @param {number} level * The stack level for which the tabindex attribute should be recorded. */ recordTabindex: function ($el, level) { @@ -170,9 +201,9 @@ /** * Restores the tabindex and autofocus values of a reactivated element. * - * @param jQuery $el + * @param {jQuery} $el * The element that is being reactivated. - * @param Number level + * @param {number} level * The stack level for which the tabindex attribute should be restored. */ restoreTabindex: function ($el, level) { @@ -214,19 +245,25 @@ * * This constraint can be removed with the release() method. * - * @param Object options - * A set of initiating values that include: - * - Number level: The level in the TabbingManager's stack of this - * tabbingContext. - * - jQuery $tabbableElements: The DOM elements that should be reachable via - * the tab key when this tabbingContext is active. - * - jQuery $disabledElements: The DOM elements that should not be reachable - * via the tab key when this tabbingContext is active. - * - Boolean released: A released tabbingContext can never be activated again. - * It will be cleaned up when the TabbingManager unwinds its stack. - * - Boolean active: When true, the tabbable elements of this tabbingContext - * will be reachable via the tab key and the disabled elements will not. Only - * one tabbingContext can be active at a time. + * @constructor + * + * @param {object} options + * A set of initiating values + * @param {number} options.level + * The level in the TabbingManager's stack of this tabbingContext. + * @param {jQuery} options.$tabbableElements + * The DOM elements that should be reachable via the tab key when this + * tabbingContext is active. + * @param {jQuery} options.$disabledElements + * The DOM elements that should not be reachable via the tab key when this + * tabbingContext is active. + * @param {bool} options.released + * A released tabbingContext can never be activated again. It will be + * cleaned up when the TabbingManager unwinds its stack. + * @param {bool} options.active + * When true, the tabbable elements of this tabbingContext will be reachable + * via the tab key and the disabled elements will not. Only one + * tabbingContext can be active at a time. */ function TabbingContext(options) { $.extend(this, { @@ -241,11 +278,15 @@ /** * Add public methods to the TabbingContext class. */ - $.extend(TabbingContext.prototype, { + $.extend(TabbingContext.prototype, /** @lends TabbingContext# */{ + /** * Releases this TabbingContext. * - * Once a TabbingContext object is released, it can never be activated again. + * Once a TabbingContext object is released, it can never be activated + * again. + * + * @fires event:drupalTabbingContextReleased */ release: function () { if (!this.released) { @@ -259,6 +300,8 @@ /** * Activates this TabbingContext. + * + * @fires event:drupalTabbingContextActivated */ activate: function () { // A released TabbingContext object can never be activated again. @@ -272,6 +315,8 @@ /** * Deactivates this TabbingContext. + * + * @fires event:drupalTabbingContextDeactivated */ deactivate: function () { if (this.active) { @@ -288,6 +333,10 @@ if (Drupal.tabbingManager) { return; } + + /** + * @type {TabbingManager} + */ Drupal.tabbingManager = new TabbingManager(); }(jQuery, Drupal)); diff --git a/core/misc/tabledrag.js b/core/misc/tabledrag.js index 01d4654..cfcabc2 100644 --- a/core/misc/tabledrag.js +++ b/core/misc/tabledrag.js @@ -1,9 +1,21 @@ +/** + * @file + * Provide dragging capabilities to admin uis. + */ + +/** + * Triggers when weights columns are toggled. + * + * @event columnschange + */ + (function ($, Drupal, drupalSettings) { "use strict"; /** * Store the state of weight columns display for all tables. + * * Default value is to hide weight columns. */ var showWeight = JSON.parse(localStorage.getItem('Drupal.tableDrag.showWeight')); @@ -12,12 +24,15 @@ * Drag and drop table rows with field manipulation. * * Using the drupal_attach_tabledrag() function, any table with weights or - * parent relationships may be made into draggable tables. Columns containing a - * field may optionally be hidden, providing a better user experience. + * parent relationships may be made into draggable tables. Columns containing + * a field may optionally be hidden, providing a better user experience. * * Created tableDrag instances may be modified with custom behaviors by * overriding the .onDrag, .onDrop, .row.onSwap, and .row.onIndent methods. - * See blocks.js for an example of adding additional functionality to tableDrag. + * See blocks.js for an example of adding additional functionality to + * tableDrag. + * + * @type {Drupal~behavior} */ Drupal.behaviors.tableDrag = { attach: function (context, settings) { @@ -38,39 +53,124 @@ }; /** - * Constructor for the tableDrag object. Provides table and field manipulation. + * Provides table and field manipulation. + * + * @constructor * - * @param table + * @param {HTMLElement} table * DOM object for the table to be made draggable. - * @param tableSettings + * @param {object} tableSettings * Settings for the table added via drupal_add_dragtable(). */ Drupal.tableDrag = function (table, tableSettings) { var self = this; var $table = $(table); - // Required object variables. + /** + * @type {jQuery} + */ this.$table = $(table); + + /** + * + * @type {HTMLElement} + */ this.table = table; + + /** + * @type {object} + */ this.tableSettings = tableSettings; - this.dragObject = null; // Used to hold information about a current drag operation. - this.rowObject = null; // Provides operations for row manipulation. - this.oldRowElement = null; // Remember the previous element. - this.oldY = 0; // Used to determine up or down direction from last mouse move. - this.changed = false; // Whether anything in the entire table has changed. - this.maxDepth = 0; // Maximum amount of allowed parenting. - this.rtl = $(this.table).css('direction') === 'rtl' ? -1 : 1; // Direction of the table. + + /** + * Used to hold information about a current drag operation. + * + * @type {?HTMLElement} + */ + this.dragObject = null; + + /** + * Provides operations for row manipulation. + * + * @type {?HTMLElement} + */ + this.rowObject = null; + + /** + * Remember the previous element. + * + * @type {?HTMLElement} + */ + this.oldRowElement = null; + + /** + * Used to determine up or down direction from last mouse move. + * + * @type {number} + */ + this.oldY = 0; + + /** + * + * @type {bool} + */ + this.changed = false; + // Whether anything in the entire table has changed. + /** + * + * @type {number} + */ + this.maxDepth = 0; + // Maximum amount of allowed parenting. + /** + * Direction of the table. + * + * @type {number} + */ + this.rtl = $(this.table).css('direction') === 'rtl' ? -1 : 1; + + /** + * + * @type {bool} + */ this.striping = $(this.table).data('striping') === 1; - // Configure the scroll settings. + /** + * Configure the scroll settings. + * + * @type {object} + * + * @prop {number} amount + * @prop {number} interval + * @prop {number} trigger + */ this.scrollSettings = {amount: 4, interval: 50, trigger: 70}; + + /** + * + * @type {?number} + */ this.scrollInterval = null; + + /** + * + * @type {number} + */ this.scrollY = 0; + + /** + * + * @type {number} + */ this.windowHeight = 0; - // Check this table's settings to see if there are parent relationships in - // this table. For efficiency, large sections of code can be skipped if we - // don't need to track horizontal movement and indentations. + /** + * Check this table's settings to see if there are parent relationships in + * this table. For efficiency, large sections of code can be skipped if we + * don't need to track horizontal movement and indentations. + * + * @type {bool} + */ this.indentEnabled = false; for (var group in tableSettings) { if (tableSettings.hasOwnProperty(group)) { @@ -87,7 +187,13 @@ } } if (this.indentEnabled) { - this.indentCount = 1; // Total width of indents, set in makeDraggable. + + /** + * Total width of indents, set in makeDraggable. + * + * @type {number} + */ + this.indentCount = 1; // Find the width of indentations to measure mouse movements against. // Because the table doesn't need to start with any indentations, we // manually append 2 indentations in the first draggable row, measure @@ -96,6 +202,11 @@ var testRow = $('').addClass('draggable').appendTo(table); var testCell = $('').appendTo(testRow).prepend(indent).prepend(indent); var $indentation = testCell.find('.js-indentation'); + + /** + * + * @type {number} + */ this.indentAmount = $indentation.get(1).offsetLeft - $indentation.get(0).offsetLeft; testRow.remove(); } @@ -136,7 +247,8 @@ $(window).on('storage', $.proxy(function (e) { // Only react to 'Drupal.tableDrag.showWeight' value change. if (e.originalEvent.key === 'Drupal.tableDrag.showWeight') { - // This was changed in another window, get the new value for this window. + // This was changed in another window, get the new value for this + // window. showWeight = JSON.parse(e.originalEvent.newValue); this.displayColumns(showWeight); } @@ -144,8 +256,7 @@ }; /** - * Initialize columns containing form elements to be hidden by default, - * according to the settings for this tableDrag instance. + * Initialize columns containing form elements to be hidden by default. * * Identify and mark each cell with a CSS class so we can easily toggle * show/hide it. Finally, hide columns if user does not have a @@ -157,7 +268,9 @@ var cell; var columnIndex; for (var group in this.tableSettings) { - if (this.tableSettings.hasOwnProperty(group)) { // Find the first field in this group. + if (this.tableSettings.hasOwnProperty(group)) { + + // Find the first field in this group. for (var d in this.tableSettings[group]) { if (this.tableSettings[group].hasOwnProperty(d)) { var field = $table.find('.' + this.tableSettings[group][d].target).eq(0); @@ -171,8 +284,9 @@ // Mark the column containing this field so it can be hidden. if (hidden && cell[0]) { - // Add 1 to our indexes. The nth-child selector is 1 based, not 0 based. - // Match immediate children of the parent element to allow nesting. + // Add 1 to our indexes. The nth-child selector is 1 based, not 0 + // based. Match immediate children of the parent element to allow + // nesting. columnIndex = cell.parent().find('> td').index(cell.get(0)) + 1; $table.find('> thead > tr, > tbody > tr, > tr').each(this.addColspanClass(columnIndex)); } @@ -182,8 +296,13 @@ }; /** - * Mark cells that have colspan so we can adjust the colspan - * instead of hiding them altogether. + * Mark cells that have colspan. + * + * In order to adjust the colspan instead of hiding them altogether. + * + * @param {number} columnIndex + * + * @return {function} */ Drupal.tableDrag.prototype.addColspanClass = function (columnIndex) { return function () { @@ -214,7 +333,9 @@ /** * Hide or display weight columns. Triggers an event on change. * - * @param bool displayWeight + * @fires event:columnschange + * + * @param {bool} displayWeight * 'true' will show weight columns. */ Drupal.tableDrag.prototype.displayColumns = function (displayWeight) { @@ -232,6 +353,7 @@ /** * Toggle the weight column depending on 'showWeight' value. + * * Store only default override. */ Drupal.tableDrag.prototype.toggleColumns = function () { @@ -249,6 +371,7 @@ /** * Hide the columns containing weight/parent form elements. + * * Undo showColumns(). */ Drupal.tableDrag.prototype.hideColumns = function () { @@ -266,7 +389,8 @@ }; /** - * Show the columns containing weight/parent form elements + * Show the columns containing weight/parent form elements. + * * Undo hideColumns(). */ Drupal.tableDrag.prototype.showColumns = function () { @@ -285,6 +409,11 @@ /** * Find the target used within a particular row and group. + * + * @param {string} group + * @param {HTMLElement} row + * + * @return {object} */ Drupal.tableDrag.prototype.rowSettings = function (group, row) { var field = $(row).find('.' + group); @@ -308,11 +437,13 @@ /** * Take an item and add event handlers to make it become draggable. + * + * @param {HTMLElement} item */ Drupal.tableDrag.prototype.makeDraggable = function (item) { var self = this; var $item = $(item); - // Add a class to the title link + // Add a class to the title link. $item.find('td').eq(0).find('a').addClass('menu-item__link'); // Create the handle. var handle = $('
     
    ').attr('title', Drupal.t('Drag to re-order')); @@ -369,13 +500,18 @@ var keyChange = false; var groupHeight; switch (event.keyCode) { - case 37: // Left arrow. - case 63234: // Safari left arrow. + // Left arrow. + case 37: + case 63234: + // Safari left arrow. keyChange = true; self.rowObject.indent(-1 * self.rtl); break; - case 38: // Up arrow. - case 63232: // Safari up arrow. + + // Up arrow. + case 38: + case 63232: + // Safari up arrow. var $previousRow = $(self.rowObject.element).prev('tr').eq(0); var previousRow = $previousRow.get(0); while (previousRow && $previousRow.is(':hidden')) { @@ -383,7 +519,8 @@ previousRow = $previousRow.get(0); } if (previousRow) { - self.safeBlur = false; // Do not allow the onBlur cleanup. + self.safeBlur = false; + // Do not allow the onBlur cleanup. self.rowObject.direction = 'up'; keyChange = true; @@ -402,23 +539,30 @@ } } else if (self.table.tBodies[0].rows[0] !== previousRow || $previousRow.is('.draggable')) { - // Swap with the previous row (unless previous row is the first one - // and undraggable). + // Swap with the previous row (unless previous row is the first + // one and undraggable). self.rowObject.swap('before', previousRow); self.rowObject.interval = null; self.rowObject.indent(0); window.scrollBy(0, -parseInt(item.offsetHeight, 10)); } - handle.trigger('focus'); // Regain focus after the DOM manipulation. + handle.trigger('focus'); + // Regain focus after the DOM manipulation. } break; - case 39: // Right arrow. - case 63235: // Safari right arrow. + + // Right arrow. + case 39: + case 63235: + // Safari right arrow. keyChange = true; self.rowObject.indent(self.rtl); break; - case 40: // Down arrow. - case 63233: // Safari down arrow. + + // Down arrow. + case 40: + case 63233: + // Safari down arrow. var $nextRow = $(self.rowObject.group).eq(-1).next('tr').eq(0); var nextRow = $nextRow.get(0); while (nextRow && $nextRow.is(':hidden')) { @@ -426,7 +570,8 @@ nextRow = $nextRow.get(0); } if (nextRow) { - self.safeBlur = false; // Do not allow the onBlur cleanup. + self.safeBlur = false; + // Do not allow the onBlur cleanup. self.rowObject.direction = 'down'; keyChange = true; @@ -451,7 +596,8 @@ self.rowObject.indent(0); window.scrollBy(0, parseInt(item.offsetHeight, 10)); } - handle.trigger('focus'); // Regain focus after the DOM manipulation. + handle.trigger('focus'); + // Regain focus after the DOM manipulation. } break; } @@ -474,15 +620,20 @@ } }); - // Compatibility addition, return false on keypress to prevent unwanted scrolling. - // IE and Safari will suppress scrolling on keydown, but all other browsers - // need to return false on keypress. http://www.quirksmode.org/js/keys.html + // Compatibility addition, return false on keypress to prevent unwanted + // scrolling. IE and Safari will suppress scrolling on keydown, but all + // other browsers need to return false on keypress. + // http://www.quirksmode.org/js/keys.html handle.on('keypress', function (event) { switch (event.keyCode) { - case 37: // Left arrow. - case 38: // Up arrow. - case 39: // Right arrow. - case 40: // Down arrow. + // Left arrow. + case 37: + // Up arrow. + case 38: + // Right arrow. + case 39: + // Down arrow. + case 40: return false; } }); @@ -491,11 +642,11 @@ /** * Pointer event initiator, creates drag object and information. * - * @param jQuery.Event event + * @param {jQuery.Event} event * The event object that trigger the drag. - * @param Drupal.tableDrag self + * @param {Drupal.tableDrag} self * The drag handle. - * @param DOM item + * @param {HTMLElement} item * The item that that is being dragged. */ Drupal.tableDrag.prototype.dragStart = function (event, self, item) { @@ -531,6 +682,11 @@ /** * Pointer movement handler, bound to document. + * + * @param {jQuery.Event} event + * @param {Drupal.tableDrag} self + * + * @return {bool|undefined} */ Drupal.tableDrag.prototype.dragRow = function (event, self) { if (self.dragObject) { @@ -541,8 +697,8 @@ // Check for row swapping and vertical scrolling. if (y !== self.oldY) { self.rowObject.direction = y > self.oldY ? 'down' : 'up'; - self.oldY = y; // Update the old value. - + self.oldY = y; + // Update the old value. // Check if the window should be scrolled (and how fast). var scrollAmount = self.checkScroll(self.currentPointerCoords.y); // Stop any current scrolling. @@ -570,7 +726,8 @@ // Similar to row swapping, handle indentations. if (self.indentEnabled) { var xDiff = self.currentPointerCoords.x - self.dragObject.indentPointerPos.x; - // Set the number of indentations the pointer has been moved left or right. + // Set the number of indentations the pointer has been moved left or + // right. var indentDiff = Math.round(xDiff / self.indentAmount); // Indent the row with our estimated diff, which may be further // restricted according to the rows around this row. @@ -586,6 +743,9 @@ /** * Pointerup behavior. + * + * @param {jQuery.Event} event + * @param {Drupal.tableDrag} self */ Drupal.tableDrag.prototype.dropRow = function (event, self) { var droppedRow; @@ -644,6 +804,10 @@ /** * Get the coordinates from the event (allowing for browser differences). + * + * @param {jQuery.Event} event + * + * @return {{x: number, y: number}} */ Drupal.tableDrag.prototype.pointerCoords = function (event) { if (event.pageX || event.pageY) { @@ -656,8 +820,15 @@ }; /** + * Get the event offset from the target element. + * * Given a target element and a pointer event, get the event offset from that * element. To do this we need the element's position and the target position. + * + * @param {HTMLElement} target + * @param {jQuery.Event} event + * + * @return {{x: number, y: number}} */ Drupal.tableDrag.prototype.getPointerOffset = function (target, event) { var docPos = $(target).offset(); @@ -666,13 +837,16 @@ }; /** - * Find the row the mouse is currently over. This row is then taken and swapped - * with the one being dragged. + * Find the row the mouse is currently over. + * + * This row is then taken and swapped with the one being dragged. * - * @param x + * @param {number} x * The x coordinate of the mouse on the page (not the screen). - * @param y + * @param {number} y * The y coordinate of the mouse on the page (not the screen). + * + * @return {*} */ Drupal.tableDrag.prototype.findDropTargetRow = function (x, y) { var rows = $(this.table.tBodies[0].rows).not(':hidden'); @@ -715,8 +889,8 @@ } // We may have found the row the mouse just passed over, but it doesn't - // take into account hidden rows. Skip backwards until we find a draggable - // row. + // take into account hidden rows. Skip backwards until we find a + // draggable row. while ($row.is(':hidden') && $row.prev('tr').is(':hidden')) { $row = $row.prev('tr').eq(0); row = $row.get(0); @@ -728,10 +902,9 @@ }; /** - * After the row is dropped, update the table fields according to the settings - * set for this table. + * After the row is dropped, update the table fields. * - * @param changedRow + * @param {HTMLElement} changedRow * DOM object for the row that was just dropped. */ Drupal.tableDrag.prototype.updateFields = function (changedRow) { @@ -745,12 +918,11 @@ }; /** - * After the row is dropped, update a single table field according to specific - * settings. + * After the row is dropped, update a single table field. * - * @param changedRow + * @param {HTMLElement} changedRow * DOM object for the row that was just dropped. - * @param group + * @param {string} group * The settings group on which field updates will occur. */ Drupal.tableDrag.prototype.updateField = function (changedRow, group) { @@ -843,10 +1015,12 @@ // Get the depth of the target row. targetElement.value = $(sourceElement).closest('tr').find('.js-indentation').length; break; + case 'match': // Update the value. targetElement.value = sourceElement.value; break; + case 'order': var siblings = this.rowObject.findSiblings(rowSettings); if ($(targetElement).is('select')) { @@ -858,7 +1032,8 @@ var maxVal = values[values.length - 1]; // Populate the values in the siblings. $(siblings).find(targetClass).each(function () { - // If there are more items than possible values, assign the maximum value to the row. + // If there are more items than possible values, assign the + // maximum value to the row. if (values.length > 0) { this.value = values.shift(); } @@ -881,9 +1056,15 @@ }; /** + * Copy all tableDrag related classes from one row to another. + * * Copy all special tableDrag classes from one row's form elements to a * different one, removing any special classes that the destination row * may have had. + * + * @param {HTMLElement} sourceRow + * @param {HTMLElement} targetRow + * @param {string} group */ Drupal.tableDrag.prototype.copyDragClasses = function (sourceRow, targetRow, group) { var sourceElement = $(sourceRow).find('.' + group); @@ -893,6 +1074,10 @@ } }; + /** + * @param {number} cursorY + * @return {number} + */ Drupal.tableDrag.prototype.checkScroll = function (cursorY) { var de = document.documentElement; var b = document.body; @@ -921,6 +1106,9 @@ } }; + /** + * @param {number} scrollAmount + */ Drupal.tableDrag.prototype.setScroll = function (scrollAmount) { var self = this; @@ -935,6 +1123,9 @@ }, this.scrollSettings.interval); }; + /** + * Command to restripe table properly. + */ Drupal.tableDrag.prototype.restripeTable = function () { // :even and :odd are reversed because jQuery counts from 0 and // we count from 1, so we're out of sync. @@ -947,6 +1138,8 @@ /** * Stub function. Allows a custom handler when a row begins dragging. + * + * @return {?bool} */ Drupal.tableDrag.prototype.onDrag = function () { return null; @@ -954,6 +1147,8 @@ /** * Stub function. Allows a custom handler when a row is dropped. + * + * @return {?bool} */ Drupal.tableDrag.prototype.onDrop = function () { return null; @@ -962,16 +1157,18 @@ /** * Constructor to make a new object to manipulate a table row. * - * @param tableRow + * @param {HTMLElement} tableRow * The DOM element for the table row we will be manipulating. - * @param method - * The method in which this row is being moved. Either 'keyboard' or 'mouse'. - * @param indentEnabled + * @param {string} method + * The method in which this row is being moved. Either 'keyboard' or + * 'mouse'. + * @param {bool} indentEnabled * Whether the containing table uses indentations. Used for optimizations. - * @param maxDepth + * @param {number} maxDepth * The maximum amount of indentations this row may contain. - * @param addClasses - * Whether we want to add classes to this row to indicate child relationships. + * @param {bool} addClasses + * Whether we want to add classes to this row to indicate child + * relationships. */ Drupal.tableDrag.prototype.row = function (tableRow, method, indentEnabled, maxDepth, addClasses) { var $tableRow = $(tableRow); @@ -984,8 +1181,8 @@ this.table = $tableRow.closest('table')[0]; this.indentEnabled = indentEnabled; this.maxDepth = maxDepth; - this.direction = ''; // Direction the row is being moved. - + this.direction = ''; + // Direction the row is being moved. if (this.indentEnabled) { this.indents = $tableRow.find('.js-indentation').length; this.children = this.findChildren(addClasses); @@ -1000,8 +1197,11 @@ /** * Find all children of rowObject by indentation. * - * @param addClasses - * Whether we want to add classes to this row to indicate child relationships. + * @param {bool} addClasses + * Whether we want to add classes to this row to indicate child + * relationships. + * + * @return {Array} */ Drupal.tableDrag.prototype.row.prototype.findChildren = function (addClasses) { var parentIndentation = this.indents; @@ -1045,8 +1245,10 @@ /** * Ensure that two rows are allowed to be swapped. * - * @param row + * @param {HTMLElement} row * DOM object for the row being considered for swapping. + * + * @return {bool} */ Drupal.tableDrag.prototype.row.prototype.isValidSwap = function (row) { var $row = $(row); @@ -1080,9 +1282,9 @@ /** * Perform the swap between two rows. * - * @param position + * @param {string} position * Whether the swap will occur 'before' or 'after' the given row. - * @param row + * @param {HTMLElement} row * DOM element what will be swapped with the row group. */ Drupal.tableDrag.prototype.row.prototype.swap = function (position, row) { @@ -1100,15 +1302,16 @@ }; /** - * Determine the valid indentations interval for the row at a given position - * in the table. + * Determine the valid indentations interval for the row at a given position. * - * @param prevRow + * @param {HTMLElement|null} prevRow * DOM object for the row before the tested position * (or null for first position in the table). - * @param nextRow + * @param {HTMLElement|null} nextRow * DOM object for the row after the tested position * (or null for last position in the table). + * + * @return {{min: number, max: number}} */ Drupal.tableDrag.prototype.row.prototype.validIndentInterval = function (prevRow, nextRow) { var $prevRow = $(prevRow); @@ -1142,10 +1345,12 @@ /** * Indent a row within the legal bounds of the table. * - * @param indentDiff + * @param {number} indentDiff * The number of additional indentations proposed for the row (can be * positive or negative). This number will be adjusted to nearest valid * indentation level for the row. + * + * @return {number} */ Drupal.tableDrag.prototype.row.prototype.indent = function (indentDiff) { var $group = $(this.group); @@ -1184,11 +1389,15 @@ }; /** - * Find all siblings for a row, either according to its subgroup or indentation. - * Note that the passed-in row is included in the list of siblings. + * Find all siblings for a row. * - * @param settings + * According to its subgroup or indentation. Note that the passed-in row is + * included in the list of siblings. + * + * @param {object} rowSettings * The field settings we're using to identify what constitutes a sibling. + * + * @return {Array} */ Drupal.tableDrag.prototype.row.prototype.findSiblings = function (rowSettings) { var siblings = []; @@ -1257,6 +1466,8 @@ /** * Stub function. Allows a custom handler when a row is indented. + * + * @return {?bool} */ Drupal.tableDrag.prototype.row.prototype.onIndent = function () { return null; @@ -1264,18 +1475,34 @@ /** * Stub function. Allows a custom handler when a row is swapped. + * + * @param {HTMLElement} swappedRow + * + * @return {?bool} */ Drupal.tableDrag.prototype.row.prototype.onSwap = function (swappedRow) { return null; }; - $.extend(Drupal.theme, { + $.extend(Drupal.theme, /** @lends Drupal.theme */{ + + /** + * @return {string} + */ tableDragChangedMarker: function () { return '*'; }, + + /** + * @return {string} + */ tableDragIndentation: function () { return '
     
    '; }, + + /** + * @return {string} + */ tableDragChangedWarning: function () { return ''; } diff --git a/core/misc/tableheader.js b/core/misc/tableheader.js index 2d6ce16..062247b 100644 --- a/core/misc/tableheader.js +++ b/core/misc/tableheader.js @@ -1,9 +1,16 @@ +/** + * @file + * Sticky table headers. + */ + (function ($, Drupal, displace) { "use strict"; /** * Attaches sticky table headers. + * + * @type {Drupal~behavior} */ Drupal.behaviors.tableHeader = { attach: function (context) { @@ -48,26 +55,36 @@ // Bind event that need to change all tables. $(window).on({ + /** * When resizing table width can change, recalculate everything. + * + * @ignore */ 'resize.TableHeader': tableHeaderResizeHandler, /** * Bind only one event to take care of calling all scroll callbacks. + * + * @ignore */ 'scroll.TableHeader': tableHeaderOnScrollHandler }); // Bind to custom Drupal events. $(document).on({ + /** * Recalculate columns width when window is resized and when show/hide * weight is triggered. + * + * @ignore */ 'columnschange.TableHeader': tableHeaderResizeHandler, /** - * Recalculate TableHeader.topOffset when viewport is resized + * Recalculate TableHeader.topOffset when viewport is resized. + * + * @ignore */ 'drupalViewportOffsetChange.TableHeader': tableHeaderOffsetChangeHandler }); @@ -78,19 +95,37 @@ * TableHeader will make the current table header stick to the top of the page * if the table is very long. * - * @param table + * @constructor Drupal.TableHeader + * + * @param {HTMLElement} table * DOM object for the table to add a sticky header to. * - * @constructor + * @listens event:columnschange */ function TableHeader(table) { var $table = $(table); + /** + * @name Drupal.TableHeader#$originalTable + * + * @type {HTMLElement} + */ this.$originalTable = $table; + + /** + * @type {jQuery} + */ this.$originalHeader = $table.children('thead'); + + /** + * @type {jQuery} + */ this.$originalHeaderCells = this.$originalHeader.find('> tr > th'); - this.displayWeight = null; + /** + * @type {null|bool} + */ + this.displayWeight = null; this.$originalTable.addClass('sticky-table'); this.tableHeight = $table[0].clientHeight; this.tableOffset = this.$originalTable.offset(); @@ -111,11 +146,12 @@ /** * Store the state of TableHeader. */ - $.extend(TableHeader, { + $.extend(TableHeader, /** @lends Drupal.TableHeader */{ + /** * This will store the state of all processed tables. * - * @type {Array} + * @type {Array.} */ tables: [] }); @@ -123,24 +159,33 @@ /** * Extend TableHeader prototype. */ - $.extend(TableHeader.prototype, { + $.extend(TableHeader.prototype, /** @lends Drupal.TableHeader# */{ + /** * Minimum height in pixels for the table to have a sticky header. + * + * @type {number} */ minHeight: 100, /** * Absolute position of the table on the page. + * + * @type {?Drupal.displace~offset} */ tableOffset: null, /** * Absolute position of the table on the page. + * + * @type {?number} */ tableHeight: null, /** * Boolean storing the sticky header visibility state. + * + * @type {bool} */ stickyVisible: false, @@ -169,8 +214,10 @@ /** * Set absolute position of sticky. * - * @param offsetTop - * @param offsetLeft + * @param {number} offsetTop + * @param {number} offsetLeft + * + * @return {jQuery} */ stickyPosition: function (offsetTop, offsetLeft) { var css = {}; @@ -185,6 +232,8 @@ /** * Returns true if sticky is currently visible. + * + * @return {bool} */ checkStickyVisible: function () { var scrollTop = scrollValue('scrollTop'); @@ -203,9 +252,10 @@ /** * Check if sticky header should be displayed. * - * This function is throttled to once every 250ms to avoid unnecessary calls. + * This function is throttled to once every 250ms to avoid unnecessary + * calls. * - * @param event + * @param {jQuery.Event} e */ onScroll: function (e) { this.checkStickyVisible(); @@ -217,7 +267,7 @@ /** * Event handler: recalculates position of the sticky table header. * - * @param event + * @param {jQuery.Event} event * Event being triggered. */ recalculateSticky: function (event) { diff --git a/core/misc/tableresponsive.js b/core/misc/tableresponsive.js index b4eab62..c82968b 100644 --- a/core/misc/tableresponsive.js +++ b/core/misc/tableresponsive.js @@ -1,9 +1,16 @@ +/** + * @file + * Responsive table functionality. + */ + (function ($, Drupal, window) { "use strict"; /** * Attach the tableResponsive function to Drupal.behaviors. + * + * @type {Drupal~behavior} */ Drupal.behaviors.tableResponsive = { attach: function (context, settings) { @@ -18,14 +25,18 @@ }; /** - * The TableResponsive object optimizes table presentation for all screen sizes. + * The TableResponsive object optimizes table presentation for screen size. * * A responsive table hides columns at small screen sizes, leaving the most - * important columns visible to the end user. Users should not be prevented from - * accessing all columns, however. This class adds a toggle to a table with - * hidden columns that exposes the columns. Exposing the columns will likely - * break layouts, but it provides the user with a means to access data, which - * is a guiding principle of responsive design. + * important columns visible to the end user. Users should not be prevented + * from accessing all columns, however. This class adds a toggle to a table + * with hidden columns that exposes the columns. Exposing the columns will + * likely break layouts, but it provides the user with a means to access + * data, which is a guiding principle of responsive design. + * + * @constructor Drupal.TableResponsive + * + * @param {HTMLElement} table */ function TableResponsive(table) { this.table = table; @@ -51,7 +62,13 @@ /** * Extend the TableResponsive function with a list of managed tables. */ - $.extend(TableResponsive, { + $.extend(TableResponsive, /** @lends Drupal.TableResponsive */{ + + /** + * Store all created instances. + * + * @type {Array.} + */ tables: [] }); @@ -61,24 +78,34 @@ * Columns are assumed to be hidden if their header has the class priority-low * or priority-medium. */ - $.extend(TableResponsive.prototype, { + $.extend(TableResponsive.prototype, /** @lends Drupal.TableResponsive# */{ + + /** + * @param {jQuery.Event} e + */ eventhandlerEvaluateColumnVisibility: function (e) { var pegged = parseInt(this.$link.data('pegged'), 10); var hiddenLength = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden').length; - // If the table has hidden columns, associate an action link with the table - // to show the columns. + // If the table has hidden columns, associate an action link with the + // table to show the columns. if (hiddenLength > 0) { this.$link.show().text(this.showText); } // When the toggle is pegged, its presence is maintained because the user - // has interacted with it. This is necessary to keep the link visible if the - // user adjusts screen size and changes the visibility of columns. + // has interacted with it. This is necessary to keep the link visible if + // the user adjusts screen size and changes the visibility of columns. if (!pegged && hiddenLength === 0) { this.$link.hide().text(this.hideText); } }, - // Toggle the visibility of columns classed with either 'priority-low' or - // 'priority-medium'. + + /** + * Toggle the visibility of columns based on their priority. + * + * Columns are classed with either 'priority-low' or 'priority-medium'. + * + * @param {jQuery.Event} e + */ eventhandlerToggleColumns: function (e) { e.preventDefault(); var self = this; @@ -110,10 +137,10 @@ var $cell = $(this); var properties = $cell.attr('style').split(';'); var newProps = []; - // The hide method adds display none to the element. The element should - // be returned to the same state it was in before the columns were - // revealed, so it is necessary to remove the display none - // value from the style attribute. + // The hide method adds display none to the element. The element + // should be returned to the same state it was in before the columns + // were revealed, so it is necessary to remove the display none value + // from the style attribute. var match = /^display\s*\:\s*none$/; for (var i = 0; i < properties.length; i++) { var prop = properties[i]; @@ -134,6 +161,7 @@ } } }); + // Make the TableResponsive object available in the Drupal namespace. Drupal.TableResponsive = TableResponsive; diff --git a/core/misc/tableselect.js b/core/misc/tableselect.js index 78e8c90..fc8e9f2 100644 --- a/core/misc/tableselect.js +++ b/core/misc/tableselect.js @@ -1,7 +1,17 @@ +/** + * @file + * Table select functionality. + */ + (function ($, Drupal) { "use strict"; + /** + * Initialize tableSelects. + * + * @type {Drupal~behavior} + */ Drupal.behaviors.tableSelect = { attach: function (context, settings) { // Select the inner-most table in case of nested tables. @@ -9,13 +19,18 @@ } }; + /** + * Callback used in {@link Drupal.behaviors.tableSelect}. + */ Drupal.tableSelect = function () { - // Do not add a "Select all" checkbox if there are no rows with checkboxes in the table + // Do not add a "Select all" checkbox if there are no rows with checkboxes + // in the table. if ($(this).find('td input[type="checkbox"]').length === 0) { return; } - // Keep track of the table, which checkbox is checked and alias the settings. + // Keep track of the table, which checkbox is checked and alias the + // settings. var table = this; var checkboxes; var lastChecked; @@ -25,6 +40,10 @@ // Update table's select-all checkbox (and sticky header's if available). $table.prev('table.sticky-header').addBack().find('th.select-all input[type="checkbox"]').each(function () { $(this).attr('title', state ? strings.selectNone : strings.selectAll); + + /** + * @this {HTMLElement} + */ this.checked = state; }); }; @@ -32,10 +51,20 @@ // Find all with class select-all, and insert the check all checkbox. $table.find('th.select-all').prepend($('').attr('title', strings.selectAll)).on('click', function (event) { if ($(event.target).is('input[type="checkbox"]')) { - // Loop through all checkboxes and set their state to the select all checkbox' state. + // Loop through all checkboxes and set their state to the select all + // checkbox' state. checkboxes.each(function () { + + /** + * @this {HTMLElement} + */ this.checked = event.target.checked; - // Either add or remove the selected class based on the state of the check all checkbox. + // Either add or remove the selected class based on the state of the + // check all checkbox. + + /** + * @this {HTMLElement} + */ $(this).closest('tr').toggleClass('selected', this.checked); }); // Update the title and the state of the check all box. @@ -45,18 +74,24 @@ // For each of the checkboxes within the table that are not disabled. checkboxes = $table.find('td input[type="checkbox"]:enabled').on('click', function (e) { - // Either add or remove the selected class based on the state of the check all checkbox. + // Either add or remove the selected class based on the state of the + // check all checkbox. + + /** + * @this {HTMLElement} + */ $(this).closest('tr').toggleClass('selected', this.checked); - // If this is a shift click, we need to highlight everything in the range. - // Also make sure that we are actually checking checkboxes over a range and - // that a checkbox has been checked or unchecked before. + // If this is a shift click, we need to highlight everything in the + // range. Also make sure that we are actually checking checkboxes + // over a range and that a checkbox has been checked or unchecked before. if (e.shiftKey && lastChecked && lastChecked !== e.target) { // We use the checkbox's parent TR to do our range searching. Drupal.tableSelectRange($(e.target).closest('tr')[0], $(lastChecked).closest('tr')[0], e.target.checked); } - // If all checkboxes are checked, make sure the select-all one is checked too, otherwise keep unchecked. + // If all checkboxes are checked, make sure the select-all one is checked + // too, otherwise keep unchecked. updateSelectAll((checkboxes.length === checkboxes.filter(':checked').length)); // Keep track of the last checked checkbox. @@ -68,6 +103,11 @@ updateSelectAll((checkboxes.length === checkboxes.filter(':checked').length)); }; + /** + * @param {HTMLElement} from + * @param {HTMLElement} to + * @param {bool} state + */ Drupal.tableSelectRange = function (from, to, state) { // We determine the looping mode based on the order of from and to. var mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling'; @@ -80,7 +120,8 @@ continue; } $i = $(i); - // Either add or remove the selected class based on the state of the target checkbox. + // Either add or remove the selected class based on the state of the + // target checkbox. $i.toggleClass('selected', state); $i.find('input[type="checkbox"]').prop('checked', state); diff --git a/core/misc/timezone.js b/core/misc/timezone.js index e7667b8..801d728 100644 --- a/core/misc/timezone.js +++ b/core/misc/timezone.js @@ -1,9 +1,16 @@ +/** + * @file + * Timezone detection. + */ + (function ($) { "use strict"; /** * Set the client's system time zone as default values of form fields. + * + * @type {Drupal~behavior} */ Drupal.behaviors.setTimezone = { attach: function (context, settings) { @@ -45,10 +52,11 @@ isDaylightSavingTime = 0; } - // Submit request to the system/timezone callback and set the form field - // to the response time zone. The client date is passed to the callback - // for debugging purposes. Submit a synchronous request to avoid database - // errors associated with concurrent requests during install. + // Submit request to the system/timezone callback and set the form + // field to the response time zone. The client date is passed to the + // callback for debugging purposes. Submit a synchronous request to + // avoid database errors associated with concurrent requests + // during install. var path = 'system/timezone/' + abbreviation + '/' + offsetNow + '/' + isDaylightSavingTime; $.ajax({ async: false, diff --git a/core/misc/vertical-tabs.js b/core/misc/vertical-tabs.js index e7f64dc..a381bdc 100644 --- a/core/misc/vertical-tabs.js +++ b/core/misc/vertical-tabs.js @@ -1,17 +1,31 @@ +/** + * @file + * Define vertical tabs functionality. + */ + +/** + * Triggers when form values inside a vertical tab changes. + * + * This is used to update the summary in vertical tabs in order to know what + * are the important fields' values. + * + * @event summaryUpdated + */ + (function ($) { "use strict"; /** - * This script transforms a set of details into a stack of vertical - * tabs. Another tab pane can be selected by clicking on the respective - * tab. + * This script transforms a set of details into a stack of vertical tabs. * * Each tab may have a summary which can be updated by another * script. For that to work, each details element has an associated * 'verticalTabCallback' (with jQuery.data() attached to the details), * which is called every time the user performs an update to a form * element inside the tab pane. + * + * @type {Drupal~behavior} */ Drupal.behaviors.verticalTabs = { attach: function (context) { @@ -25,7 +39,8 @@ var focusID = $this.find(':hidden.vertical-tabs__active-tab').val(); var tab_focus; - // Check if there are some details that can be converted to vertical-tabs + // Check if there are some details that can be converted to + // vertical-tabs. var $details = $this.find('> details'); if ($details.length === 0) { return; @@ -79,10 +94,17 @@ /** * The vertical tab object represents a single tab within a tab group. * - * @param settings - * An object with the following keys: - * - title: The name of the tab. - * - details: The jQuery object of the details element that is the tab pane. + * @constructor + * + * @param {object} settings + * @param {string} settings.title + * The name of the tab. + * @param {jQuery} settings.details + * The jQuery object of the details element that is the tab pane. + * + * @fires event:summaryUpdated + * + * @listens event:summaryUpdated */ Drupal.verticalTab = function (settings) { var self = this; @@ -114,6 +136,7 @@ }; Drupal.verticalTab.prototype = { + /** * Displays the tab's content pane. */ @@ -144,15 +167,17 @@ /** * Shows a vertical tab pane. + * + * @return {Drupal.verticalTab} */ tabShow: function () { // Display the tab. this.item.show(); // Show the vertical tabs. this.item.closest('.js-form-type-vertical-tabs').show(); - // Update .first marker for items. We need recurse from parent to retain the - // actual DOM element order as jQuery implements sortOrder, but not as public - // method. + // Update .first marker for items. We need recurse from parent to retain + // the actual DOM element order as jQuery implements sortOrder, but not + // as public method. this.item.parent().children('.vertical-tabs__menu-item').removeClass('first') .filter(':visible').eq(0).addClass('first'); // Display the details element. @@ -164,13 +189,15 @@ /** * Hides a vertical tab pane. + * + * @return {Drupal.verticalTab} */ tabHide: function () { // Hide this tab. this.item.hide(); - // Update .first marker for items. We need recurse from parent to retain the - // actual DOM element order as jQuery implements sortOrder, but not as public - // method. + // Update .first marker for items. We need recurse from parent to retain + // the actual DOM element order as jQuery implements sortOrder, but not + // as public method. this.item.parent().children('.vertical-tabs__menu-item').removeClass('first') .filter(':visible').eq(0).addClass('first'); // Hide the details element. @@ -191,10 +218,12 @@ /** * Theme function for a vertical tab. * - * @param settings + * @param {object} settings * An object with the following keys: - * - title: The name of the tab. - * @return + * @param {string} settings.title + * The name of the tab. + * + * @return {object} * This function has to return an object with at least these keys: * - item: The root tab jQuery element * - link: The anchor tag that acts as the clickable area of the tab diff --git a/core/modules/block/js/block.admin.js b/core/modules/block/js/block.admin.js index d301ade..b399dd0 100644 --- a/core/modules/block/js/block.admin.js +++ b/core/modules/block/js/block.admin.js @@ -1,3 +1,8 @@ +/** + * @file + * Block admin behaviors. + */ + (function ($, Drupal) { "use strict"; @@ -8,6 +13,8 @@ * Text search input: input.block-filter-text * Target element: input.block-filter-text[data-element] * Source text: .block-filter-text-source + * + * @type {Drupal~behavior} */ Drupal.behaviors.blockFilterByText = { attach: function (context, settings) { @@ -17,7 +24,10 @@ var $details; /** - * Hides the
    element for a category if it has no visible blocks. + * Hides the `
    ` element for a category if it has no visible blocks. + * + * @param {number} index + * @param {HTMLElement} element */ function hideCategoryDetails(index, element) { var $catDetails = $(element); @@ -26,12 +36,17 @@ /** * Filters the block list. + * + * @param {jQuery.Event} e */ function filterBlockList(e) { var query = $(e.target).val().toLowerCase(); /** * Shows or hides the block entry based on the query. + * + * @param {number} index + * @param {HTMLElement} block */ function showBlockEntry(index, block) { var $block = $(block); @@ -71,6 +86,8 @@ /** * Highlights the block that was just placed into the block listing. + * + * @type {Drupal~behavior} */ Drupal.behaviors.blockHighlightPlacement = { attach: function (context, settings) { diff --git a/core/modules/block/js/block.js b/core/modules/block/js/block.js index c3a4d0d..e5c0907 100644 --- a/core/modules/block/js/block.js +++ b/core/modules/block/js/block.js @@ -1,9 +1,16 @@ +/** + * @file + * Block behaviors. + */ + (function ($, window) { "use strict"; /** * Provide the summary information for the block settings vertical tabs. + * + * @type {Drupal~behavior} */ Drupal.behaviors.blockSettingsSummary = { attach: function () { @@ -46,6 +53,8 @@ * * This behavior is dependent on the tableDrag behavior, since it uses the * objects initialized in that behavior to update the row. + * + * @type {Drupal~behavior} */ Drupal.behaviors.blockDrag = { attach: function (context, settings) { @@ -55,8 +64,8 @@ } var table = $('#blocks'); - var tableDrag = Drupal.tableDrag.blocks; // Get the blocks tableDrag object. - + // Get the blocks tableDrag object. + var tableDrag = Drupal.tableDrag.blocks; // Add a handler for when a row is swapped, update empty regions. tableDrag.row.prototype.onSwap = function (swappedRow) { checkEmptyRegions(table, this); diff --git a/core/modules/block_content/js/block_content.js b/core/modules/block_content/js/block_content.js index de36d4b..1c9cea2 100644 --- a/core/modules/block_content/js/block_content.js +++ b/core/modules/block_content/js/block_content.js @@ -7,6 +7,9 @@ "use strict"; + /** + * @type {Drupal~behavior} + */ Drupal.behaviors.blockContentDetailsSummaries = { attach: function (context) { var $context = $(context); diff --git a/core/modules/book/book.js b/core/modules/book/book.js index df57547..e67c6a2 100644 --- a/core/modules/book/book.js +++ b/core/modules/book/book.js @@ -7,6 +7,9 @@ "use strict"; + /** + * @type {Drupal~behavior} + */ Drupal.behaviors.bookDetailsSummaries = { attach: function (context) { $(context).find('.book-outline-form').drupalSetSummary(function (context) { diff --git a/core/modules/ckeditor/js/ckeditor.admin.js b/core/modules/ckeditor/js/ckeditor.admin.js index ff75541..24ebfb4 100644 --- a/core/modules/ckeditor/js/ckeditor.admin.js +++ b/core/modules/ckeditor/js/ckeditor.admin.js @@ -2,12 +2,16 @@ * @file * CKEditor button and group configuration user interface. */ + (function ($, Drupal, _, CKEDITOR) { "use strict"; Drupal.ckeditor = Drupal.ckeditor || {}; + /** + * @type {Drupal~behavior} + */ Drupal.behaviors.ckeditorAdmin = { attach: function (context) { // Process the CKEditor configuration fragment once. @@ -69,13 +73,23 @@ /** * CKEditor configuration UI methods of Backbone objects. + * + * @namespace */ Drupal.ckeditor = { - // A hash of View instances. + /** + * A hash of View instances. + * + * @type {object} + */ views: {}, - // A hash of Model instances. + /** + * A hash of Model instances. + * + * @type {object} + */ models: {}, /** @@ -86,11 +100,11 @@ * placeholder, then a process is launched to name that group before the button * move is translated into configuration. * - * @param Backbone.View view + * @param {Backbone.View} view * The Backbone View that invoked this function. - * @param jQuery $button + * @param {jQuery} $button * A jQuery set that contains an li element that wraps a button element. - * @param function callback + * @param {function} callback * A callback to invoke after the button group naming modal dialog has been * closed. */ @@ -118,9 +132,9 @@ * Each row has a placeholder group at the end of the row. A user may not move * an existing button group past the placeholder group at the end of a row. * - * @param Backbone.View view + * @param {Backbone.View} view * The Backbone View that invoked this function. - * @param jQuery $group + * @param {jQuery} $group * A jQuery set that contains an li element that wraps a group of buttons. */ registerGroupMove: function (view, $group) { @@ -143,11 +157,11 @@ /** * Opens a Drupal dialog with a form for changing the title of a button group. * - * @param Backbone.View view + * @param {Backbone.View} view * The Backbone View that invoked this function. - * @param jQuery $group + * @param {jQuery} $group * A jQuery set that contains an li element that wraps a group of buttons. - * @param function callback + * @param {function} callback * A callback to invoke after the button group naming modal dialog has been * closed. */ @@ -157,10 +171,11 @@ /** * Validates the string provided as a button group title. * - * @param DOM form + * @param {HTMLElement} form * The form DOM element that contains the input with the new button group * title string. - * @return Boolean + * + * @return {bool} * Returns true when an error exists, otherwise returns false. */ function validateForm(form) { @@ -182,9 +197,9 @@ /** * Attempts to close the dialog; Validates user input. * - * @param String action + * @param {string} action * The dialog action chosen by the user: 'apply' or 'cancel'. - * @param DOM form + * @param {HTMLElement} form * The form DOM element that contains the input with the new button group * title string. */ @@ -203,9 +218,9 @@ /** * Applies a string as the name of a CKEditor button group. * - * @param jQuery $group + * @param {jQuery} $group * A jQuery set that contains an li element that wraps a group of buttons. - * @param String name + * @param {string} name * The new name of the CKEditor button group. */ function namePlaceholderGroup($group, name) { @@ -336,9 +351,10 @@ }; - /** * Automatically shows/hides settings of buttons-only CKEditor plugins. + * + * @type {Drupal~behavior} */ Drupal.behaviors.ckeditorAdminButtonPluginSettings = { attach: function (context) { @@ -411,7 +427,7 @@ /** * Themes a blank CKEditor row. * - * @return String + * @return {string} */ Drupal.theme.ckeditorRow = function () { return '
    • '; @@ -420,7 +436,7 @@ /** * Themes a blank CKEditor button group. * - * @return String + * @return {string} */ Drupal.theme.ckeditorToolbarGroup = function () { var group = ''; @@ -434,7 +450,7 @@ /** * Themes a form for changing the title of a CKEditor button group. * - * @return String + * @return {string} */ Drupal.theme.ckeditorButtonGroupNameForm = function () { return '
      '; @@ -443,7 +459,7 @@ /** * Themes a button that will toggle the button group names in active config. * - * @return String + * @return {string} */ Drupal.theme.ckeditorButtonGroupNamesToggle = function () { return ''; @@ -452,7 +468,7 @@ /** * Themes a button that will prompt the user to name a new button group. * - * @return String + * @return {string} */ Drupal.theme.ckeditorNewButtonGroup = function () { return '
    • '; diff --git a/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js b/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js index 44072df..8989717 100644 --- a/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js +++ b/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js @@ -1,9 +1,16 @@ +/** + * @file + * Ckeditor drupalimage admin behavior. + */ + (function ($, Drupal, drupalSettings) { "use strict"; /** * Provides the summary for the "drupalimage" plugin settings vertical tab. + * + * @type {Drupal~behavior} */ Drupal.behaviors.ckeditorDrupalImageSettingsSummary = { attach: function () { diff --git a/core/modules/ckeditor/js/ckeditor.js b/core/modules/ckeditor/js/ckeditor.js index 59f8b2e..8c1b51a 100644 --- a/core/modules/ckeditor/js/ckeditor.js +++ b/core/modules/ckeditor/js/ckeditor.js @@ -1,9 +1,25 @@ +/** + * @file + * Ckeditor JS API. + */ + (function (Drupal, debounce, CKEDITOR, $) { "use strict"; + /** + * @namespace + */ Drupal.editors.ckeditor = { + /** + * Editor attach callback. + * + * @param {HTMLElement} element + * @param {string} format + * + * @return {bool} + */ attach: function (element, format) { this._loadExternalPlugins(format); // Also pass settings that are Drupal-specific. @@ -26,6 +42,15 @@ return !!CKEDITOR.replace(element, format.editorSettings); }, + /** + * Editor detach callback. + * + * @param {HTMLElement} element + * @param {string} format + * @param {string} trigger + * + * @return {bool} + */ detach: function (element, format, trigger) { var editor = CKEDITOR.dom.element.get(element).getEditor(); if (editor) { @@ -40,6 +65,13 @@ return !!editor; }, + /** + * + * @param {HTMLElement} element + * @param {function} callback + * + * @return {bool} + */ onChange: function (element, callback) { var editor = CKEDITOR.dom.element.get(element).getEditor(); if (editor) { @@ -50,6 +82,15 @@ return !!editor; }, + /** + * + * @param {HTMLElement} element + * @param {object} format + * @param {string} mainToolbarId + * @param {string} floatedToolbarId + * + * @return {bool} + */ attachInlineEditor: function (element, format, mainToolbarId, floatedToolbarId) { this._loadExternalPlugins(format); // Also pass settings that are Drupal-specific. @@ -101,6 +142,9 @@ return !!CKEDITOR.inline(element, settings); }, + /** + * @param {object} format + */ _loadExternalPlugins: function (format) { var externalPlugins = format.editorSettings.drupalExternalPlugins; // Register and load additional CKEditor plugins as necessary. @@ -117,8 +161,11 @@ }; Drupal.ckeditor = { + /** * Variable storing the current dialog's save callback. + * + * @type {?function} */ saveCallback: null, @@ -128,16 +175,16 @@ * This dynamically loads jQuery UI (if necessary) using the Drupal AJAX * framework, then opens a dialog at the specified Drupal path. * - * @param editor + * @param {CKEditor} editor * The CKEditor instance that is opening the dialog. - * @param string url + * @param {string} url * The URL that contains the contents of the dialog. - * @param Object existingValues + * @param {object} existingValues * Existing values that will be sent via POST to the url for the dialog * contents. - * @param Function saveCallback + * @param {function} saveCallback * A function to be called upon saving the dialog. - * @param Object dialogSettings + * @param {object} dialogSettings * An object containing settings to be passed to the jQuery UI. */ openDialog: function (editor, url, existingValues, saveCallback, dialogSettings) { diff --git a/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js b/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js index 5fe814d..56933ea 100644 --- a/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js +++ b/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js @@ -1,3 +1,8 @@ +/** + * @file + * Ckeditor sylecombo admin behavior. + */ + (function ($, Drupal, drupalSettings) { "use strict"; @@ -9,6 +14,8 @@ * plugin settings change, to ensure that the corresponding feature metadata is * immediately updated — i.e. ensure that HTML tags and classes entered here are * known to be "required", which may affect filter settings. + * + * @type {Drupal~behavior} */ Drupal.behaviors.ckeditorStylesComboSettings = { attach: function (context) { @@ -46,10 +53,10 @@ * parsing works identically, but instead of failing on invalid styles, we * just ignore those. * - * @param String styles + * @param {string} styles * The "styles" setting. * - * @return array + * @return {Array} * An array containing the "stylesSet" configuration. */ _generateStylesSetSetting: function (styles) { @@ -93,6 +100,8 @@ /** * Provides the summary for the "stylescombo" plugin settings vertical tab. + * + * @type {Drupal~behavior} */ Drupal.behaviors.ckeditorStylesComboSettingsSummary = { attach: function () { diff --git a/core/modules/ckeditor/js/models/Model.js b/core/modules/ckeditor/js/models/Model.js index bdab6fd..9868c09 100644 --- a/core/modules/ckeditor/js/models/Model.js +++ b/core/modules/ckeditor/js/models/Model.js @@ -9,26 +9,58 @@ /** * Backbone model for the CKEditor toolbar configuration state. + * + * @constructor + * + * @augments Backbone.Model */ - Drupal.ckeditor.Model = Backbone.Model.extend({ - defaults: { - // The CKEditor configuration that is being manipulated through the UI. + Drupal.ckeditor.Model = Backbone.Model.extend(/** @lends Drupal.ckeditor.Model# */{ + + /** + * Default values. + * + * @type {object} + */ + defaults: /** @lends Drupal.ckeditor.Model# */{ + + /** + * The CKEditor configuration that is being manipulated through the UI. + */ activeEditorConfig: null, - // The textarea that contains the serialized representation of the active - // CKEditor configuration. + + /** + * The textarea that contains the serialized representation of the active + * CKEditor configuration. + */ $textarea: null, - // Tracks whether the active toolbar DOM structure has been changed. When - // true, activeEditorConfig needs to be updated, and when that is updated, - // $textarea will also be updated. + + /** + * Tracks whether the active toolbar DOM structure has been changed. When + * true, activeEditorConfig needs to be updated, and when that is updated, + * $textarea will also be updated. + */ isDirty: false, - // The configuration for the hidden CKEditor instance that is used to build - // the features metadata. + + /** + * The configuration for the hidden CKEditor instance that is used to build + * the features metadata. + */ hiddenEditorConfig: null, - // A hash, keyed by a feature name, that details CKEditor plugin features. + + /** + * A hash, keyed by a feature name, that details CKEditor plugin features. + */ featuresMetadata: null, - // Whether the button group names are currently visible. + + /** + * Whether the button group names are currently visible. + */ groupNamesVisible: false }, + + /** + * @method + */ sync: function () { // Push the settings into the textarea. this.get('$textarea').val(JSON.stringify(this.get('activeEditorConfig'))); diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js index fd18c5a..68eb7fe 100644 --- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js @@ -7,8 +7,12 @@ * uses to track where images are being used) * - use a Drupal-native dialog (that is in fact just an alterable Drupal form * like any other) instead of CKEditor's own dialogs. - * @see \Drupal\editor\Form\EditorImageDialog + * + * @see \Drupal\editor\Form\EditorImageDialog + * + * @ignore */ + (function ($, Drupal, CKEDITOR) { "use strict"; diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js index cbb6160..1dffb23 100644 --- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js @@ -5,8 +5,11 @@ * This alters the existing CKEditor image2 widget plugin, which is already * altered by the Drupal Image plugin, to: * - allow for the data-caption and data-align attributes to be set - * - mimic the upcasting behavior of the caption_filter filter + * - mimic the upcasting behavior of the caption_filter filter. + * + * @ignore */ + (function (CKEDITOR) { "use strict"; @@ -220,9 +223,10 @@ * Function will check first the passed element itself and then all its * children in DFS order. * - * @param CKEDITOR.htmlParser.element element - * @param String name - * @return CKEDITOR.htmlParser.element + * @param {CKEDITOR.htmlParser.element} element + * @param {string} name + * + * @return {CKEDITOR.htmlParser.element} */ function findElementByName(element, name) { if (element.name === name) { @@ -233,7 +237,8 @@ element.forEach(function (el) { if (el.name === name) { found = el; - return false; // Stop here. + // Stop here. + return false; } }, CKEDITOR.NODE_ELEMENT); return found; diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.js index 04d8aba..c26445b 100644 --- a/core/modules/ckeditor/js/plugins/drupallink/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupallink/plugin.js @@ -1,6 +1,8 @@ /** * @file * Drupal Link plugin. + * + * @ignore */ (function ($, Drupal, drupalSettings, CKEDITOR) { @@ -199,6 +201,7 @@ * * The following selection will all return the link element. * + * @example * li^nk * [link] * text[link] @@ -207,6 +210,8 @@ * [li]nk * * @param {CKEDITOR.editor} editor + * + * @return {?bool} */ function getSelectedLink(editor) { var selection = editor.getSelection(); diff --git a/core/modules/ckeditor/js/views/AuralView.js b/core/modules/ckeditor/js/views/AuralView.js index 5acd776..439e8dd 100644 --- a/core/modules/ckeditor/js/views/AuralView.js +++ b/core/modules/ckeditor/js/views/AuralView.js @@ -7,11 +7,11 @@ "use strict"; - /** - * Backbone View for CKEditor toolbar configuration; aural UX (output only). - */ - Drupal.ckeditor.AuralView = Backbone.View.extend({ + Drupal.ckeditor.AuralView = Backbone.View.extend(/** @lends Drupal.ckeditor.AuralView# */{ + /** + * @type {object} + */ events: { 'click .ckeditor-buttons a': 'announceButtonHelp', 'click .ckeditor-multiple-buttons a': 'announceSeparatorHelp', @@ -21,7 +21,11 @@ }, /** - * {@inheritdoc} + * Backbone View for CKEditor toolbar configuration; aural UX (output only). + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { // Announce the button and group positions when the model is no longer @@ -32,8 +36,8 @@ /** * Calls announce on buttons and groups when their position is changed. * - * @param Drupal.ckeditor.ConfigurationModel model - * @param Boolean isDirty + * @param {Drupal.ckeditor.ConfigurationModel} model + * @param {bool} isDirty * A model attribute that indicates if the changed toolbar configuration * has been stored or not. */ @@ -57,7 +61,7 @@ /** * Handles the focus event of elements in the active and available toolbars. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onFocus: function (event) { event.stopPropagation(); @@ -76,7 +80,7 @@ /** * Announces the current position of a button group. * - * @param jQuery $group + * @param {jQuery} $group * A jQuery set that contains an li element that wraps a group of buttons. */ announceButtonGroupPosition: function ($group) { @@ -106,7 +110,7 @@ /** * Announces current button position. * - * @param jQuery $button + * @param {jQuery} $button * A jQuery set that contains an li element that wraps a button. */ announceButtonPosition: function ($button) { @@ -166,7 +170,7 @@ /** * Provides help information when a button is clicked. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ announceButtonHelp: function (event) { var $link = $(event.currentTarget); @@ -194,7 +198,7 @@ /** * Provides help information when a separator is clicked. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ announceSeparatorHelp: function (event) { var $link = $(event.currentTarget); diff --git a/core/modules/ckeditor/js/views/ControllerView.js b/core/modules/ckeditor/js/views/ControllerView.js index 7d77943..125debf 100644 --- a/core/modules/ckeditor/js/views/ControllerView.js +++ b/core/modules/ckeditor/js/views/ControllerView.js @@ -7,15 +7,19 @@ "use strict"; - /** - * Backbone View acting as a controller for CKEditor toolbar configuration. - */ - Drupal.ckeditor.ControllerView = Backbone.View.extend({ + Drupal.ckeditor.ControllerView = Backbone.View.extend(/** @lends Drupal.ckeditor.ControllerView# */{ + /** + * @type {object} + */ events: {}, /** - * {@inheritdoc} + * Backbone View acting as a controller for CKEditor toolbar configuration. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.getCKEditorFeatures(this.model.get('hiddenEditorConfig'), this.disableFeaturesDisallowedByFilters.bind(this)); @@ -28,16 +32,18 @@ /** * Converts the active toolbar DOM structure to an object representation. * - * @param Drupal.ckeditor.ConfigurationModel model + * @param {Drupal.ckeditor.ConfigurationModel} model * The state model for the CKEditor configuration. - * @param Boolean isDirty + * @param {bool} isDirty * Tracks whether the active toolbar DOM structure has been changed. * isDirty is toggled back to false in this method. - * @param Object options + * @param {object} options * An object that includes: - * - Boolean broadcast: (optional) A flag that controls whether a - * CKEditorToolbarChanged event should be fired for configuration - * changes. + * @param {bool} [options.broadcast] + * A flag that controls whether a CKEditorToolbarChanged event should be + * fired for configuration changes. + * + * @fires event:CKEditorToolbarChanged */ parseEditorDOM: function (model, isDirty, options) { if (isDirty) { @@ -100,10 +106,10 @@ * must be provided that will receive a hash of Drupal.EditorFeature * features keyed by feature (button) name. * - * @param Object CKEditorConfig + * @param {object} CKEditorConfig * An object that represents the configuration settings for a CKEditor * editor component. - * @param Function callback + * @param {function} callback * A function to invoke when the instanceReady event is fired by the * CKEditor object. */ @@ -192,9 +198,10 @@ * Retrieves the feature for a given button from featuresMetadata. Returns * false if the given button is in fact a divider. * - * @param String button + * @param {string} button * The name of a CKEditor button. - * @return Object + * + * @return {object} * The feature metadata object for a button. */ getFeatureForButton: function (button) { @@ -218,7 +225,7 @@ /** * Checks buttons against filter settings; disables disallowed buttons. * - * @param Object features + * @param {object} features * A map of Drupal.EditorFeature objects. */ disableFeaturesDisallowedByFilters: function (features) { @@ -272,7 +279,7 @@ /** * Sets up broadcasting of CKEditor toolbar configuration changes. * - * @param jQuery $ckeditorToolbar + * @param {jQuery} $ckeditorToolbar * The active toolbar DOM element wrapped in jQuery. */ broadcastConfigurationChanges: function ($ckeditorToolbar) { @@ -329,14 +336,15 @@ /** * Returns the list of buttons from an editor configuration. * - * @param Object config + * @param {object} config * A CKEditor configuration object. - * @return Array + * + * @return {Array} * A list of buttons in the CKEditor configuration. */ getButtonList: function (config) { var buttons = []; - // Remove the rows + // Remove the rows. config = _.flatten(config); // Loop through the button groups and pull out the buttons. diff --git a/core/modules/ckeditor/js/views/KeyboardView.js b/core/modules/ckeditor/js/views/KeyboardView.js index 2a2868a..ecd088c 100644 --- a/core/modules/ckeditor/js/views/KeyboardView.js +++ b/core/modules/ckeditor/js/views/KeyboardView.js @@ -7,13 +7,14 @@ "use strict"; - /** - * Backbone View for CKEditor toolbar configuration; keyboard UX. - */ - Drupal.ckeditor.KeyboardView = Backbone.View.extend({ + Drupal.ckeditor.KeyboardView = Backbone.View.extend(/** @lends Drupal.ckeditor.KeyboardView# */{ /** - * {@inheritdoc} + * Backbone View for CKEditor toolbar configuration; keyboard UX. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { // Add keyboard arrow support. @@ -22,7 +23,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ render: function () { }, @@ -30,7 +31,7 @@ /** * Handles keypresses on a CKEditor configuration button. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onPressButton: function (event) { var upDownKeys = [ @@ -181,7 +182,7 @@ /** * Handles keypresses on a CKEditor configuration group. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onPressGroup: function (event) { var upDownKeys = [ diff --git a/core/modules/ckeditor/js/views/VisualView.js b/core/modules/ckeditor/js/views/VisualView.js index 95d56e7..b9779a2 100644 --- a/core/modules/ckeditor/js/views/VisualView.js +++ b/core/modules/ckeditor/js/views/VisualView.js @@ -1,16 +1,14 @@ /** * @file - * A Backbone View that provides the visual UX view of CKEditor toolbar configuration. + * A Backbone View that provides the visual UX view of CKEditor toolbar + * configuration. */ (function (Drupal, Backbone, $) { "use strict"; - /** - * Backbone View for CKEditor toolbar configuration; visual UX. - */ - Drupal.ckeditor.VisualView = Backbone.View.extend({ + Drupal.ckeditor.VisualView = Backbone.View.extend(/** @lends Drupal.ckeditor.VisualView# */{ events: { 'click .ckeditor-toolbar-group-name': 'onGroupNameClick', @@ -19,7 +17,11 @@ }, /** - * {@inheritdoc} + * Backbone View for CKEditor toolbar configuration; visual UX. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change:isDirty change:groupNamesVisible', this.render); @@ -32,7 +34,12 @@ }, /** - * {@inheritdoc} + * + * @param {*} model + * @param {string} [value] + * @param {object} changedAttributes + * + * @return {Drupal.ckeditor.VisualView} */ render: function (model, value, changedAttributes) { this.insertPlaceholders(); @@ -57,7 +64,7 @@ /** * Handles clicks to a button group name. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onGroupNameClick: function (event) { var $group = $(event.currentTarget).closest('.ckeditor-toolbar-group'); @@ -69,6 +76,8 @@ /** * Handles clicks on the button group names toggle button. + * + * @param {jQuery.Event} event */ onGroupNamesToggleClick: function (event) { this.model.set('groupNamesVisible', !this.model.get('groupNamesVisible')); @@ -78,17 +87,17 @@ /** * Prompts the user to provide a name for a new button group; inserts it. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onAddGroupButtonClick: function (event) { /** * Inserts a new button if the openGroupNameDialog function returns true. * - * @param Boolean success + * @param {bool} success * A flag that indicates if the user created a new group (true) or * canceled out of the dialog (false). - * @param jQuery $group + * @param {jQuery} $group * A jQuery DOM fragment that represents the new button group. It has * not been added to the DOM yet. */ @@ -110,8 +119,8 @@ /** * Handles jQuery Sortable stop sort of a button group. * - * @param jQuery.Event event - * @param Object ui + * @param {jQuery.Event} event + * @param {object} ui * A jQuery.ui.sortable argument that contains information about the * elements involved in the sort action. */ @@ -128,8 +137,8 @@ /** * Handles jQuery Sortable start sort of a button. * - * @param jQuery.Event event - * @param Object ui + * @param {jQuery.Event} event + * @param {object} ui * A jQuery.ui.sortable argument that contains information about the * elements involved in the sort action. */ @@ -143,8 +152,8 @@ /** * Handles jQuery Sortable stop sort of a button. * - * @param jQuery.Event event - * @param Object ui + * @param {jQuery.Event} event + * @param {object} ui * A jQuery.ui.sortable argument that contains information about the * elements involved in the sort action. */ diff --git a/core/modules/color/color.js b/core/modules/color/color.js index 4423d56..25c9d0c 100644 --- a/core/modules/color/color.js +++ b/core/modules/color/color.js @@ -7,6 +7,9 @@ "use strict"; + /** + * @type {Drupal~behavior} + */ Drupal.behaviors.color = { attach: function (context, settings) { var i; @@ -49,7 +52,8 @@ width.push(parseInt(gradient.css('width'), 10) / 10); // Add rows (or columns for horizontal gradients). // Each gradient line should have a height (or width for horizontal - // gradients) of 10px (because we divided the height/width by 10 above). + // gradients) of 10px (because we divided the height/width by 10 + // above). for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) { gradient.append('
      '); } @@ -82,11 +86,19 @@ /** * Shifts a given color, using a reference pair (ref in HSL). * - * This algorithm ensures relative ordering on the saturation and luminance - * axes is preserved, and performs a simple hue shift. + * This algorithm ensures relative ordering on the saturation and + * luminance axes is preserved, and performs a simple hue shift. * * It is also symmetrical. If: shift_color(c, a, b) === d, then * shift_color(d, b, a) === c. + * + * @function Drupal.color~shift_color + * + * @param {string} given + * @param {Array} ref1 + * @param {Array} ref2 + * + * @return {string} */ function shift_color(given, ref1, ref2) { var d; @@ -129,6 +141,11 @@ /** * Callback for Farbtastic when a new color is chosen. + * + * @param {HTMLElement} input + * @param {string} color + * @param {bool} propagate + * @param {bool} colorScheme */ function callback(input, color, propagate, colorScheme) { var matched; @@ -182,6 +199,8 @@ /** * Focuses Farbtastic on a particular field. + * + * @param {jQuery.Event} e */ function focus(e) { var input = e.target; @@ -203,7 +222,7 @@ // Initialize color fields. form.find('.js-color-palette input.form-text') .each(function () { - // Extract palette field name + // Extract palette field name. this.key = this.id.substring(13); // Link to color picker temporarily to initialize. diff --git a/core/modules/color/preview.js b/core/modules/color/preview.js index f121ade..956b53e 100644 --- a/core/modules/color/preview.js +++ b/core/modules/color/preview.js @@ -7,14 +7,26 @@ "use strict"; + /** + * @namespace + */ Drupal.color = { + + /** + * @param {Element} context + * @param {object} settings + * @param {HTMLFormElement} form + * @param {object} farb + * @param {number} height + * @param {number} width + */ callback: function (context, settings, form, farb, height, width) { var accum; var delta; // Solid background. form.find('.color-preview').css('backgroundColor', form.find('.color-palette input[name="palette[base]"]').val()); - // Text preview + // Text preview. form.find('#text').css('color', form.find('.color-palette input[name="palette[text]"]').val()); form.find('#text a, #text h2').css('color', form.find('.color-palette input[name="palette[link]"]').val()); diff --git a/core/modules/comment/comment-entity-form.js b/core/modules/comment/comment-entity-form.js index b41ab84..9e90979 100644 --- a/core/modules/comment/comment-entity-form.js +++ b/core/modules/comment/comment-entity-form.js @@ -7,6 +7,10 @@ "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.commentFieldsetSummaries = { attach: function (context) { var $context = $(context); diff --git a/core/modules/comment/js/comment-by-viewer.js b/core/modules/comment/js/comment-by-viewer.js index 0abea0d..4f597e0 100644 --- a/core/modules/comment/js/comment-by-viewer.js +++ b/core/modules/comment/js/comment-by-viewer.js @@ -1,12 +1,16 @@ /** + * @file * Attaches behaviors for the Comment module's "by-viewer" class. */ + (function ($, Drupal, drupalSettings) { "use strict"; /** * Add 'by-viewer' class to comments written by the current user. + * + * @type {Drupal~behavior} */ Drupal.behaviors.commentByViewer = { attach: function (context) { diff --git a/core/modules/comment/js/comment-new-indicator.js b/core/modules/comment/js/comment-new-indicator.js index b15e67c..8a2bf07 100644 --- a/core/modules/comment/js/comment-new-indicator.js +++ b/core/modules/comment/js/comment-new-indicator.js @@ -1,15 +1,19 @@ /** + * @file * Attaches behaviors for the Comment module's "new" indicator. * * May only be loaded for authenticated users, with the History module * installed. */ + (function ($, Drupal, window) { "use strict"; /** * Render "new" comment indicators wherever necessary. + * + * @type {Drupal~behavior} */ Drupal.behaviors.commentNewIndicator = { attach: function (context) { diff --git a/core/modules/comment/js/node-new-comments-link.js b/core/modules/comment/js/node-new-comments-link.js index 97c7871..fce15aa 100644 --- a/core/modules/comment/js/node-new-comments-link.js +++ b/core/modules/comment/js/node-new-comments-link.js @@ -1,15 +1,19 @@ /** + * @file * Attaches behaviors for the Comment module's "X new comments" link. * * May only be loaded for authenticated users, with the History module * installed. */ + (function ($, Drupal) { "use strict"; /** * Render "X new comments" links wherever necessary. + * + * @type {Drupal~behavior} */ Drupal.behaviors.nodeNewCommentsLink = { attach: function (context) { diff --git a/core/modules/content_translation/content_translation.admin.js b/core/modules/content_translation/content_translation.admin.js index 857cbd7..0071b6e 100644 --- a/core/modules/content_translation/content_translation.admin.js +++ b/core/modules/content_translation/content_translation.admin.js @@ -1,9 +1,16 @@ +/** + * @file + * Content Translation admin behaviors. + */ + (function ($, Drupal, drupalSettings) { "use strict"; /** * Forces applicable options to be checked as translatable. + * + * @type {Drupal~behavior} */ Drupal.behaviors.contentTranslationDependentOptions = { attach: function (context) { @@ -66,6 +73,8 @@ /** * Makes field translatability inherit bundle translatability. + * + * @type {Drupal~behavior} */ Drupal.behaviors.contentTranslation = { attach: function (context) { diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js index eccb54b..3694f45 100644 --- a/core/modules/contextual/js/contextual.js +++ b/core/modules/contextual/js/contextual.js @@ -33,12 +33,12 @@ } /** - * Initializes a contextual link: updates its DOM, sets up model and views + * Initializes a contextual link: updates its DOM, sets up model and views. * - * @param jQuery $contextual + * @param {jQuery} $contextual * A contextual links placeholder DOM element, containing the actual * contextual links as rendered by the server. - * @param string html + * @param {string} html * The server-side rendered HTML for this contextual link. */ function initContextual($contextual, html) { @@ -97,7 +97,7 @@ * * This only deals with two levels of nesting; deeper levels are not touched. * - * @param jQuery $contextual + * @param {jQuery} $contextual * A contextual links placeholder DOM element, containing the actual * contextual links as rendered by the server. */ @@ -138,6 +138,8 @@ * Events * Contextual triggers an event that can be used by other scripts. * - drupalContextualLinkAdded: Triggered when a contextual link is added. + * + * @type {Drupal~behavior} */ Drupal.behaviors.contextual = { attach: function (context) { @@ -206,23 +208,38 @@ } }; + /** + * @namespace + */ Drupal.contextual = { - // The Drupal.contextual.View instances associated with each list element of - // contextual links. + /** + * The Drupal.contextual.View instances associated with each list element of + * contextual links. + * + * @type {Array} + */ views: [], - // The Drupal.contextual.RegionView instances associated with each contextual - // region element. + /** + * The Drupal.contextual.RegionView instances associated with each contextual + * region element. + * + * @type {Array} + */ regionViews: [] }; - // A Backbone.Collection of Drupal.contextual.StateModel instances. + /** + * A Backbone.Collection of Drupal.contextual.StateModel instances. + * + * @type {Backbone.Collection} + */ Drupal.contextual.collection = new Backbone.Collection([], {model: Drupal.contextual.StateModel}); /** * A trigger is an interactive element often bound to a click handler. * - * @return String + * @return {string} * A string representing a DOM fragment. */ Drupal.theme.contextualTrigger = function () { diff --git a/core/modules/contextual/js/contextual.toolbar.js b/core/modules/contextual/js/contextual.toolbar.js index 4641890..c6b752c 100644 --- a/core/modules/contextual/js/contextual.toolbar.js +++ b/core/modules/contextual/js/contextual.toolbar.js @@ -14,9 +14,9 @@ }; /** - * Initializes a contextual link: updates its DOM, sets up model and views + * Initializes a contextual link: updates its DOM, sets up model and views. * - * @param DOM links + * @param {HTMLElement} context * A contextual links DOM element as rendered by the server. */ function initContextualToolbar(context) { @@ -45,6 +45,8 @@ /** * Attaches contextual's edit toolbar tab behavior. + * + * @type {Drupal~behavior} */ Drupal.behaviors.contextualToolbar = { attach: function (context) { @@ -54,8 +56,15 @@ } }; + /** + * @namespace + */ Drupal.contextualToolbar = { - // The Drupal.contextualToolbar.Model instance. + /** + * The Drupal.contextualToolbar.Model instance. + * + * @type {?Drupal.contextualToolbar.StateModel} + */ model: null }; diff --git a/core/modules/contextual/js/models/StateModel.js b/core/modules/contextual/js/models/StateModel.js index 24907b0..0396835 100644 --- a/core/modules/contextual/js/models/StateModel.js +++ b/core/modules/contextual/js/models/StateModel.js @@ -9,20 +9,58 @@ /** * Models the state of a contextual link's trigger, list & region. + * + * @constructor + * + * @augments Backbone.Model */ - Drupal.contextual.StateModel = Backbone.Model.extend({ + Drupal.contextual.StateModel = Backbone.Model.extend(/** @lends Drupal.contextual.StateModel# */{ - defaults: { - // The title of the entity to which these contextual links apply. + /** + * @type {object} + * + * @prop {string} title + * @prop {bool} regionIsHovered + * @prop {bool} hasFocus + * @prop {bool} isOpen + * @prop {bool} isLocked + */ + defaults: /** @lends Drupal.contextual.StateModel# */{ + + /** + * The title of the entity to which these contextual links apply. + * + * @type {string} + */ title: '', - // Represents if the contextual region is being hovered. + + /** + * Represents if the contextual region is being hovered. + * + * @type {bool} + */ regionIsHovered: false, - // Represents if the contextual trigger or options have focus. + + /** + * Represents if the contextual trigger or options have focus. + * + * @type {bool} + */ hasFocus: false, - // Represents if the contextual options for an entity are available to - // be selected (i.e. whether the list of options is visible). + + /** + * Represents if the contextual options for an entity are available to + * be selected (i.e. whether the list of options is visible). + * + * @type {bool} + */ isOpen: false, - // When the model is locked, the trigger remains active. + + /** + * When the model is locked, the trigger remains active. + * + * @type {bool} + */ isLocked: false }, @@ -30,6 +68,8 @@ * Opens or closes the contextual link. * * If it is opened, then also give focus. + * + * @return {Drupal.contextual.StateModel} */ toggleOpen: function () { var newIsOpen = !this.get('isOpen'); @@ -45,6 +85,8 @@ * * Does not call blur() because we want to allow a contextual link to have * focus, yet be closed for example when hovering. + * + * @return {Drupal.contextual.StateModel} */ close: function () { this.set('isOpen', false); @@ -55,6 +97,8 @@ * Gives focus to this contextual link. * * Also closes + removes focus from every other contextual link. + * + * @return {Drupal.contextual.StateModel} */ focus: function () { this.set('hasFocus', true); @@ -69,6 +113,8 @@ /** * Removes focus from this contextual link, unless it is open. + * + * @return {Drupal.contextual.StateModel} */ blur: function () { if (!this.get('isOpen')) { diff --git a/core/modules/contextual/js/toolbar/models/StateModel.js b/core/modules/contextual/js/toolbar/models/StateModel.js index 360b9fb..5fdb4d1 100644 --- a/core/modules/contextual/js/toolbar/models/StateModel.js +++ b/core/modules/contextual/js/toolbar/models/StateModel.js @@ -7,33 +7,62 @@ "use strict"; - /** - * Models the state of the edit mode toggle. - */ - Drupal.contextualToolbar.StateModel = Backbone.Model.extend({ + Drupal.contextualToolbar.StateModel = Backbone.Model.extend(/** @lends Drupal.contextualToolbar.StateModel# */{ - defaults: { - // Indicates whether the toggle is currently in "view" or "edit" mode. + /** + * @type {object} + * + * @prop isViewing + * @prop isVisible + * @prop contextualCount + * @prop tabbingContext + */ + defaults: /** @lends Drupal.contextualToolbar.StateModel# */{ + + /** + * Indicates whether the toggle is currently in "view" or "edit" mode. + * + * @type {bool} + */ isViewing: true, - // Indicates whether the toggle should be visible or hidden. Automatically - // calculated, depends on contextualCount. + + /** + * Indicates whether the toggle should be visible or hidden. Automatically + * calculated, depends on contextualCount. + * + * @type {bool} + */ isVisible: false, - // Tracks how many contextual links exist on the page. + + /** + * Tracks how many contextual links exist on the page. + * + * @type {number} + */ contextualCount: 0, - // A TabbingContext object as returned by Drupal.TabbingManager: the set - // of tabbable elements when edit mode is enabled. + + /** + * A TabbingContext object as returned by Drupal.TabbingManager: the set + * of tabbable elements when edit mode is enabled. + * + * @type {?*} + */ tabbingContext: null }, /** - * {@inheritdoc} + * Models the state of the edit mode toggle. + * + * @constructs + * + * @augments Backbone.Model * - * @param Object attrs - * @param Object options + * @param {object} attrs + * @param {object} options * An object with the following option: - * - Backbone.collection contextualCollection: the collection of - * Drupal.contextual.StateModel models that represent the contextual - * links on the page. + * @param {Backbone.collection} options.contextualCollection + * The collection of {@link Drupal.contextual.StateModel} models that + * represent the contextual links on the page. */ initialize: function (attrs, options) { // Respond to new/removed contextual links. @@ -57,9 +86,9 @@ /** * Tracks the number of contextual link models in the collection. * - * @param Drupal.contextual.StateModel contextualModel + * @param {Drupal.contextual.StateModel} contextualModel * The contextual links model that was added or removed. - * @param Backbone.Collection contextualCollection + * @param {Backbone.Collection} contextualCollection * The collection of contextual link models. */ countContextualLinks: function (contextualModel, contextualCollection) { @@ -69,9 +98,9 @@ /** * Lock newly added contextual links if edit mode is enabled. * - * @param Drupal.contextual.StateModel contextualModel + * @param {Drupal.contextual.StateModel} contextualModel * The contextual links model that was added. - * @param Backbone.Collection contextualCollection + * @param {Backbone.Collection} [contextualCollection] * The collection of contextual link models. */ lockNewContextualLinks: function (contextualModel, contextualCollection) { diff --git a/core/modules/contextual/js/toolbar/views/AuralView.js b/core/modules/contextual/js/toolbar/views/AuralView.js index 38f7363..1344df8 100644 --- a/core/modules/contextual/js/toolbar/views/AuralView.js +++ b/core/modules/contextual/js/toolbar/views/AuralView.js @@ -7,16 +7,23 @@ "use strict"; - /** - * Renders the aural view of the edit mode toggle (i.e.screen reader support). - */ - Drupal.contextualToolbar.AuralView = Backbone.View.extend({ + Drupal.contextualToolbar.AuralView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.AuralView# */{ - // Tracks whether the tabbing constraint announcement has been read once yet. + /** + * Tracks whether the tabbing constraint announcement has been read once yet. + * + * @type {bool} + */ announcedOnce: false, - /* - * {@inheritdoc} + /** + * Renders the aural view of the edit mode toggle (screen reader support). + * + * @constructs + * + * @augments Backbone.View + * + * @param {object} options */ initialize: function (options) { this.options = options; @@ -28,7 +35,9 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.contextualToolbar.AuralView} */ render: function () { // Render the state. @@ -39,11 +48,6 @@ /** * Limits tabbing to the contextual links and edit mode toolbar tab. - * - * @param Drupal.contextualToolbar.StateModel model - * A Drupal.contextualToolbar.StateModel model. - * @param bool isViewing - * The value of the isViewing attribute in the model. */ manageTabbing: function () { var tabbingContext = this.model.get('tabbingContext'); @@ -75,7 +79,7 @@ /** * Responds to esc and tab key press events. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onKeypress: function (event) { // The first tab key press is tracked so that an annoucement about tabbing diff --git a/core/modules/contextual/js/toolbar/views/VisualView.js b/core/modules/contextual/js/toolbar/views/VisualView.js index 41b095f..0aa71dd 100644 --- a/core/modules/contextual/js/toolbar/views/VisualView.js +++ b/core/modules/contextual/js/toolbar/views/VisualView.js @@ -7,13 +7,11 @@ "use strict"; - /** - * Renders the visual view of the edit mode toggle. Listens to mouse & touch. - * - * Handles edit mode toggle interactions. - */ - Drupal.contextualToolbar.VisualView = Backbone.View.extend({ + Drupal.contextualToolbar.VisualView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.VisualView# */{ + /** + * @return {object} + */ events: function () { // Prevents delay and simulated mouse events. var touchEndToClick = function (event) { @@ -30,7 +28,13 @@ }, /** - * {@inheritdoc} + * Renders the visual view of the edit mode toggle. Listens to mouse & touch. + * + * Handles edit mode toggle interactions. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change', this.render); @@ -38,7 +42,9 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.contextualToolbar.VisualView} */ render: function () { // Render the visibility. @@ -52,12 +58,12 @@ /** * Model change handler; persists the isViewing value to localStorage. * - * isViewing === true is the default, so only stores in localStorage when + * `isViewing === true` is the default, so only stores in localStorage when * it's not the default value (i.e. false). * - * @param Drupal.contextualToolbar.StateModel model - * A Drupal.contextualToolbar.StateModel model. - * @param bool isViewing + * @param {Drupal.contextualToolbar.StateModel} model + * A {@link Drupal.contextualToolbar.StateModel} model. + * @param {bool} isViewing * The value of the isViewing attribute in the model. */ persist: function (model, isViewing) { diff --git a/core/modules/contextual/js/views/AuralView.js b/core/modules/contextual/js/views/AuralView.js index 40aff22..407f151 100644 --- a/core/modules/contextual/js/views/AuralView.js +++ b/core/modules/contextual/js/views/AuralView.js @@ -7,13 +7,16 @@ "use strict"; - /** - * Renders the aural view of a contextual link (i.e. screen reader support). - */ - Drupal.contextual.AuralView = Backbone.View.extend({ + Drupal.contextual.AuralView = Backbone.View.extend(/** @lends Drupal.contextual.AuralView# */{ /** - * {@inheritdoc} + * Renders the aural view of a contextual link (i.e. screen reader support). + * + * @constructs + * + * @augments Backbone.View + * + * @param {object} options */ initialize: function (options) { this.options = options; @@ -28,7 +31,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ render: function () { var isOpen = this.model.get('isOpen'); diff --git a/core/modules/contextual/js/views/KeyboardView.js b/core/modules/contextual/js/views/KeyboardView.js index d6ebcec..4cf54ad 100644 --- a/core/modules/contextual/js/views/KeyboardView.js +++ b/core/modules/contextual/js/views/KeyboardView.js @@ -7,10 +7,11 @@ "use strict"; - /** - * Provides keyboard interaction for a contextual link. - */ - Drupal.contextual.KeyboardView = Backbone.View.extend({ + Drupal.contextual.KeyboardView = Backbone.View.extend(/** @lends Drupal.contextual.KeyboardView# */{ + + /** + * @type {object} + */ events: { 'focus .trigger': 'focus', 'focus .contextual-links a': 'focus', @@ -26,14 +27,22 @@ }, /** - * {@inheritdoc} + * Provides keyboard interaction for a contextual link. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { - // The timer is used to create a delay before dismissing the contextual - // links on blur. This is only necessary when keyboard users tab into - // contextual links without edit mode (i.e. without TabbingManager). - // That means that if we decide to disable tabbing of contextual links - // without edit mode, all this timer logic can go away. + /** + * The timer is used to create a delay before dismissing the contextual + * links on blur. This is only necessary when keyboard users tab into + * contextual links without edit mode (i.e. without TabbingManager). + * That means that if we decide to disable tabbing of contextual links + * without edit mode, all this timer logic can go away. + * + * @type {NaN|number} + */ this.timer = NaN; }, diff --git a/core/modules/contextual/js/views/RegionView.js b/core/modules/contextual/js/views/RegionView.js index 10c5c51..b64ee45 100644 --- a/core/modules/contextual/js/views/RegionView.js +++ b/core/modules/contextual/js/views/RegionView.js @@ -7,11 +7,11 @@ "use strict"; - /** - * Renders the visual view of a contextual region element. - */ - Drupal.contextual.RegionView = Backbone.View.extend({ + Drupal.contextual.RegionView = Backbone.View.extend(/** @lends Drupal.contextual.RegionView# */{ + /** + * @return {object} + */ events: function () { var mapping = { mouseenter: function () { this.model.set('regionIsHovered', true); }, @@ -27,14 +27,20 @@ }, /** - * {@inheritdoc} + * Renders the visual view of a contextual region element. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change:hasFocus', this.render); }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.contextual.RegionView} */ render: function () { this.$el.toggleClass('focus', this.model.get('hasFocus')); diff --git a/core/modules/contextual/js/views/VisualView.js b/core/modules/contextual/js/views/VisualView.js index c40b30a..8da3c19 100644 --- a/core/modules/contextual/js/views/VisualView.js +++ b/core/modules/contextual/js/views/VisualView.js @@ -7,11 +7,11 @@ "use strict"; - /** - * Renders the visual view of a contextual link. Listens to mouse & touch. - */ - Drupal.contextual.VisualView = Backbone.View.extend({ + Drupal.contextual.VisualView = Backbone.View.extend(/** @lends Drupal.contextual.VisualView# */{ + /** + * @return {object} + */ events: function () { // Prevents delay and simulated mouse events. var touchEndToClick = function (event) { @@ -32,14 +32,20 @@ }, /** - * {@inheritdoc} + * Renders the visual view of a contextual link. Listens to mouse & touch. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change', this.render); }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.contextual.VisualView} */ render: function () { var isOpen = this.model.get('isOpen'); diff --git a/core/modules/editor/js/editor.admin.js b/core/modules/editor/js/editor.admin.js index 4cbfdc7..553cbf5 100644 --- a/core/modules/editor/js/editor.admin.js +++ b/core/modules/editor/js/editor.admin.js @@ -11,69 +11,81 @@ "use strict"; + /** + * @namespace + */ Drupal.editorConfiguration = { /** - * Must be called by a specific text editor's configuration whenever a feature - * is added by the user. + * Must be called by a specific text editor's configuration whenever a + * feature is added by the user. * - * Triggers the drupalEditorFeatureAdded event on the document, which receives - * a Drupal.EditorFeature object. + * Triggers the drupalEditorFeatureAdded event on the document, which + * receives a Drupal.EditorFeature object. * - * @param Drupal.EditorFeature feature + * @param {Drupal.EditorFeature} feature * A text editor feature object. + * + * @fires event:drupalEditorFeatureAdded */ addedFeature: function (feature) { $(document).trigger('drupalEditorFeatureAdded', feature); }, /** - * Must be called by a specific text editor's configuration whenever a feature - * is removed by the user. + * Must be called by a specific text editor's configuration whenever a + * feature is removed by the user. * * Triggers the drupalEditorFeatureRemoved event on the document, which * receives a Drupal.EditorFeature object. * - * @param Drupal.EditorFeature feature + * @param {Drupal.EditorFeature} feature * A text editor feature object. + * + * @fires event:drupalEditorFeatureRemoved */ removedFeature: function (feature) { $(document).trigger('drupalEditorFeatureRemoved', feature); }, /** - * Must be called by a specific text editor's configuration whenever a feature - * is modified, i.e. has different rules. + * Must be called by a specific text editor's configuration whenever a + * feature is modified, i.e. has different rules. * - * For example when the "Bold" button is configured to use the tag instead - * of the tag. + * For example when the "Bold" button is configured to use the tag + * instead of the tag. * * Triggers the drupalEditorFeatureModified event on the document, which * receives a Drupal.EditorFeature object. * - * @param Drupal.EditorFeature feature + * @param {Drupal.EditorFeature} feature * A text editor feature object. + * + * @fires event:drupalEditorFeatureModified */ modifiedFeature: function (feature) { $(document).trigger('drupalEditorFeatureModified', feature); }, /** - * May be called by a specific text editor's configuration whenever a feature - * is being added, to check whether it would require the filter settings to be - * updated. + * May be called by a specific text editor's configuration whenever a + * feature is being added, to check whether it would require the filter + * settings to be updated. * - * The canonical use case is when a text editor is being enabled: preferably - * this would not cause the filter settings to be changed; rather, the default - * set of buttons (features) for the text editor should adjust itself to not - * cause filter setting changes. + * The canonical use case is when a text editor is being enabled: + * preferably + * this would not cause the filter settings to be changed; rather, the + * default set of buttons (features) for the text editor should adjust + * itself to not cause filter setting changes. * * Note: for filters to integrate with this functionality, it is necessary - * that they implement Drupal.filterSettingsForEditors[filterID].getRules(). + * that they implement + * Drupal.filterSettingsForEditors[filterID].getRules(). * - * @param Drupal.EditorFeature feature + * @param {Drupal.EditorFeature} feature * A text editor feature object. - * @return Boolean + * + * @return {bool} * Whether the given feature is allowed by the current filters. */ featureIsAllowedByFilters: function (feature) { @@ -102,22 +114,27 @@ * }; * * In this example, the given text editor feature resulted in the above - * universe, which shows that it must be allowed to generate the a, strong - * and img tags. For the a tag, it must be able to set the "href" attribute - * and the "external" class. For the strong tag, no further properties are - * required. For the img tag, the "src" attribute is required. - * The "tag" key is used to track whether that tag was explicitly allowed by - * one of the filter's rules. The "touchedByAllowedPropertyRule" key is used - * for state tracking that is essential for filterStatusAllowsFeature() to - * be able to reason: when all of a filter's rules have been applied, and - * none of the forbidden rules matched (which would have resulted in early - * termination) yet the universe has not been made empty (which would be the - * end result if everything in the universe were explicitly allowed), then - * this piece of state data enables us to determine whether a tag whose - * properties were not all explicitly allowed are in fact still allowed, - * because its tag was explicitly allowed and there were no filter rules - * applying "allowed tag property value" restrictions for this particular - * tag. + * universe, which shows that it must be allowed to generate the a, + * strong + * and img tags. For the a tag, it must be able to set the "href" + * attribute and the "external" class. For the strong tag, no further + * properties are required. For the img tag, the "src" attribute is + * required. The "tag" key is used to track whether that tag was + * explicitly allowed by one of the filter's rules. The + * "touchedByAllowedPropertyRule" key is used for state tracking that is + * essential for filterStatusAllowsFeature() to be able to reason: when + * all of a filter's rules have been applied, and none of the forbidden + * rules matched (which would have resulted in early termination) yet the + * universe has not been made empty (which would be the end result if + * everything in the universe were explicitly allowed), then this piece + * of state data enables us to determine whether a tag whose properties + * were not all explicitly allowed are in fact still allowed, because its + * tag was explicitly allowed and there were no filter rules applying + * "allowed tag property value" restrictions for this particular tag. + * + * @param {object} feature + * + * @return {object} * * @see findPropertyValueOnTag() * @see filterStatusAllowsFeature() @@ -151,9 +168,9 @@ continue; } - // Expand the existing universe, assume that each tags' property value - // is disallowed. If the filter rules allow everything in the feature's - // universe, then the feature is allowed. + // Expand the existing universe, assume that each tags' property + // value is disallowed. If the filter rules allow everything in the + // feature's universe, then the feature is allowed. for (var p = 0; p < properties.length; p++) { var property = properties[p]; for (var pv = 0; pv < featureRule.required[property].length; pv++) { @@ -169,6 +186,10 @@ /** * Provided a section of a feature or filter rule, checks if no property * values are defined for all properties: attributes, classes and styles. + * + * @param {object} section + * + * @return {bool} */ function emptyProperties(section) { return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0; @@ -178,6 +199,14 @@ * Calls findPropertyValueOnTag on the given tag for every property value * that is listed in the "propertyValues" parameter. Supports the wildcard * tag. + * + * @param {object} universe + * @param {string} tag + * @param {string} property + * @param {Array} propertyValues + * @param {bool} allowing + * + * @return {bool} */ function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) { // Detect the wildcard case. @@ -196,6 +225,13 @@ /** * Calls findPropertyValuesOnAllTags for all tags in the universe. + * + * @param {object} universe + * @param {string} property + * @param {Array} propertyValues + * @param {bool} allowing + * + * @return {bool} */ function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) { var atLeastOneFound = false; @@ -208,10 +244,18 @@ } /** - * Finds out if a specific property value (potentially containing wildcards) - * exists on the given tag. When the "allowing" parameter equals true, the - * universe will be updated if that specific property value exists. - * Returns true if found, false otherwise. + * Finds out if a specific property value (potentially containing + * wildcards) exists on the given tag. When the "allowing" parameter + * equals true, the universe will be updated if that specific property + * value exists. Returns true if found, false otherwise. + * + * @param {object} universe + * @param {string} tag + * @param {string} property + * @param {string} propertyValue + * @param {bool} allowing + * + * @return {bool} */ function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) { // If the tag does not exist in the universe, then it definitely can't @@ -258,6 +302,11 @@ /** * Deletes a tag from the universe if the tag itself and each of its * properties are marked as allowed. + * + * @param {object} universe + * @param {string} tag + * + * @return {bool} */ function deleteFromUniverseIfAllowed(universe, tag) { // Detect the wildcard case. @@ -273,6 +322,10 @@ /** * Calls deleteFromUniverseIfAllowed for all tags in the universe. + * + * @param {object} universe + * + * @return {bool} */ function deleteAllTagsFromUniverseIfAllowed(universe) { var atLeastOneDeleted = false; @@ -287,6 +340,11 @@ /** * Checks if any filter rule forbids either a tag or a tag property value * that exists in the universe. + * + * @param {object} universe + * @param {object} filterStatus + * + * @return {bool} */ function anyForbiddenFilterRuleMatches(universe, filterStatus) { var properties = ['attributes', 'styles', 'classes']; @@ -315,8 +373,8 @@ // … then iterate over all properties … for (var k = 0; k < properties.length; k++) { var property = properties[k]; - // … and return true if just one of the forbidden property values - // for this tag and property is listed in the universe. + // … and return true if just one of the forbidden property + // values for this tag and property is listed in the universe. if (findPropertyValuesOnTag(universe, tag, property, filterRule.restrictedTags.forbidden[property], false)) { return true; } @@ -329,10 +387,13 @@ } /** - * Applies every filter rule's explicit allowing of a tag or a tag property - * value to the universe. Whenever both the tag and all of its required - * property values are marked as explicitly allowed, they are deleted from - * the universe. + * Applies every filter rule's explicit allowing of a tag or a tag + * property value to the universe. Whenever both the tag and all of its + * required property values are marked as explicitly allowed, they are + * deleted from the universe. + * + * @param {object} universe + * @param {object} filterStatus */ function markAllowedTagsAndPropertyValues(universe, filterStatus) { var properties = ['attributes', 'styles', 'classes']; @@ -365,9 +426,10 @@ // … then iterate over all properties … for (var k = 0; k < properties.length; k++) { var property = properties[k]; - // … and try to delete this tag from the universe if just one of - // the allowed property values for this tag and property is listed - // in the universe. (Because everything might be allowed now.) + // … and try to delete this tag from the universe if just one + // of the allowed property values for this tag and property is + // listed in the universe. (Because everything might be allowed + // now.) if (findPropertyValuesOnTag(universe, tag, property, filterRule.restrictedTags.allowed[property], true)) { deleteFromUniverseIfAllowed(universe, tag); } @@ -383,6 +445,11 @@ * requirements and then checking whether anything in the filter prevents * that. * + * @param {object} filterStatus + * @param {object} feature + * + * @return {bool} + * * @see generateUniverseFromFeatureRequirements() */ function filterStatusAllowsFeature(filterStatus, feature) { @@ -391,8 +458,8 @@ return true; } - // A feature that specifies no rules has no HTML requirements and is hence - // allowed by definition. + // A feature that specifies no rules has no HTML requirements and is + // hence allowed by definition. if (feature.rules.length === 0) { return true; } @@ -418,17 +485,16 @@ // universe will become empty. markAllowedTagsAndPropertyValues(universe, filterStatus); - // If there was at least one filter rule allowing tags, then everything in - // the universe must be allowed for this feature to be allowed, and thus - // by now it must be empty. - // However, it is still possible that the filter allows the feature, due - // to no rules for allowing tag property values and/or rules for - // forbidding tag property values. For details: see the comments below. - // + // If there was at least one filter rule allowing tags, then everything + // in the universe must be allowed for this feature to be allowed, and + // thus by now it must be empty. However, it is still possible that the + // filter allows the feature, due to no rules for allowing tag property + // values and/or rules for forbidding tag property values. For details: + // see the comments below. // @see generateUniverseFromFeatureRequirements() if (_.some(_.pluck(filterStatus.rules, 'allow'))) { - // If the universe is empty, then everything was explicitly allowed and - // our job is done: this filter allows this feature! + // If the universe is empty, then everything was explicitly allowed + // and our job is done: this filter allows this feature! if (_.isEmpty(universe)) { return true; } @@ -440,13 +506,13 @@ return false; } // Every tag was explicitly allowed, but since the universe is not - // empty, one or more tag properties are disallowed. However, if only - // blacklisting of tag properties was applied to these tags, and no - // whitelisting was ever applied, then it's still fine: since none of - // the tag properties were blacklisted, we got to this point, and since - // no whitelisting was applied, it doesn't matter that the properties: - // this could never have happened anyway. - // It's only this late that we can know this for certain. + // empty, one or more tag properties are disallowed. However, if + // only blacklisting of tag properties was applied to these tags, + // and no whitelisting was ever applied, then it's still fine: + // since none of the tag properties were blacklisted, we got to + // this point, and since no whitelisting was applied, it doesn't + // matter that the properties: this could never have happened + // anyway. It's only this late that we can know this for certain. else { var tags = _.keys(universe); // Figure out if there was any rule applying whitelisting tag @@ -471,7 +537,8 @@ } } - // If any filter's current status forbids the editor feature, return false. + // If any filter's current status forbids the editor feature, return + // false. Drupal.filterConfiguration.update(); for (var filterID in Drupal.filterConfiguration.statuses) { if (Drupal.filterConfiguration.statuses.hasOwnProperty(filterID)) { @@ -487,25 +554,89 @@ }; /** + * Constructor for an editor feature HTML rule. + * + * Intended to be used in combination with {@link Drupal.EditorFeature}. + * + * A text editor feature rule object describes both: + * - required HTML tags, attributes, styles and classes: without these, the + * text editor feature is unable to function. It's possible that a + * - allowed HTML tags, attributes, styles and classes: these are optional + * in the strictest sense, but it is possible that the feature generates + * them. + * + * The structure can be very clearly seen below: there's a "required" and an + * "allowed" key. For each of those, there are objects with the "tags", + * "attributes", "styles" and "classes" keys. For all these keys the values + * are initialized to the empty array. List each possible value as an array + * value. Besides the "required" and "allowed" keys, there's an optional + * "raw" key: it allows text editor implementations to optionally pass in + * their raw representation instead of the Drupal-defined representation for + * HTML rules. + * + * @example + * tags: [''] + * attributes: ['href', 'alt'] + * styles: ['color', 'text-decoration'] + * classes: ['external', 'internal'] + * + * @constructor + * + * @see Drupal.EditorFeature + */ + Drupal.EditorFeatureHTMLRule = function () { + + /** + * + * @type {object} + * + * @prop {Array} tags + * @prop {Array} attributes + * @prop {Array} styles + * @prop {Array} classes + */ + this.required = {tags: [], attributes: [], styles: [], classes: []}; + + /** + * + * @type {object} + * + * @prop {Array} tags + * @prop {Array} attributes + * @prop {Array} styles + * @prop {Array} classes + */ + this.allowed = {tags: [], attributes: [], styles: [], classes: []}; + + /** + * + * @type {null} + */ + this.raw = null; + }; + + /** * A text editor feature object. Initialized with the feature name. * - * Contains a set of HTML rules (Drupal.EditorFeatureHTMLRule objects) that - * describe which HTML tags, attributes, styles and classes are required (i.e. - * essential for the feature to function at all) and which are allowed (i.e. the - * feature may generate this, but they're not essential). + * Contains a set of HTML rules ({@link Drupal.EditorFeatureHTMLRule} objects) + * that describe which HTML tags, attributes, styles and classes are required + * (i.e. essential for the feature to function at all) and which are allowed + * (i.e. the feature may generate this, but they're not essential). + * + * It is necessary to allow for multiple HTML rules per feature: with just + * one HTML rule per feature, there is not enough expressiveness to describe + * certain cases. For example: a "table" feature would probably require the + * `` tag, and might allow e.g. the "summary" attribute on that tag. + * However, the table feature would also require the `` and `
      ` tags, + * but it doesn't make sense to allow for a "summary" attribute on these tags. + * Hence these would need to be split in two separate rules. * - * It is necessary to allow for multiple HTML rules per feature: with just one - * HTML rule per feature, there is not enough expressiveness to describe certain - * cases. For example: a "table" feature would probably require the tag, - * and might allow for e.g. the "summary" attribute on that tag. However, the - * table feature would also require the and + * { + * tags: ['p', 'strong', 'a'], + * allow: true, + * restrictedTags: { + * tags: [], + * allowed: { attributes: [], styles: [], classes: [] }, + * forbidden: { attributes: [], styles: [], classes: [] } + * } + * } + * @example + * { + * tags: [], + * allow: null, + * restrictedTags: { + * tags: ['a'], + * allowed: { attributes: ['href'], styles: [], classes: ['external'] }, + * forbidden: { attributes: ['target'], styles: [], classes: [] } + * } + * } + * @example + * { + * tags: [], + * allow: null, + * restrictedTags: { + * tags: ['*'], + * allowed: { attributes: ['data-*'], styles: [], classes: [] }, + * forbidden: { attributes: [], styles: [], classes: [] } + * } + * } * - * Examples: - * - Whitelist the "p", "strong" and "a" HTML tags: - * { - * tags: ['p', 'strong', 'a'], - * allow: true, - * restrictedTags: { - * tags: [], - * allowed: { attributes: [], styles: [], classes: [] }, - * forbidden: { attributes: [], styles: [], classes: [] } - * } - * } - * - For the "a" HTML tag, only allow the "href" attribute and the "external" - * class and disallow the "target" attribute. - * { - * tags: [], - * allow: null, - * restrictedTags: { - * tags: ['a'], - * allowed: { attributes: ['href'], styles: [], classes: ['external'] }, - * forbidden: { attributes: ['target'], styles: [], classes: [] } - * } - * } - * - For all tags, allow the "data-*" attribute (that is, any attribute that - * begins with "data-"). - * { - * tags: [], - * allow: null, - * restrictedTags: { - * tags: ['*'], - * allowed: { attributes: ['data-*'], styles: [], classes: [] }, - * forbidden: { attributes: [], styles: [], classes: [] } - * } - * } + * @return {{ + * tags: Array, + * allow: null, + * restrictedTags: { + * tags: Array, + * allowed: {attributes: Array, styles: Array, classes: Array}, + * forbidden: {attributes: Array, styles: Array, classes: Array} + * } + * }} + * + * @see Drupal.FilterStatus */ Drupal.FilterHTMLRule = function () { return { @@ -664,21 +795,29 @@ /** * Tracks the configuration of all text filters in Drupal.FilterStatus objects * for Drupal.editorConfiguration.featureIsAllowedByFilters(). + * + * @namespace */ Drupal.filterConfiguration = { /** * Drupal.FilterStatus objects, keyed by filter ID. + * + * @type {Object.} */ statuses: {}, /** - * Live filter setting parsers, keyed by filter ID, for those filters that - * implement it. + * Live filter setting parsers. * - * Filters should load the implementing JavaScript on the filter configuration - * form and implement Drupal.filterSettings[filterID].getRules(), which should - * return an array of Drupal.FilterHTMLRule objects. + * Object keyed by filter ID, for those filters that implement it. + * + * Filters should load the implementing JavaScript on the filter + * configuration form and implement + * `Drupal.filterSettings[filterID].getRules()`, which should return an + * array of {@link Drupal.FilterHTMLRule} objects. + * + * @namespace */ liveSettingParsers: {}, @@ -686,7 +825,7 @@ * Updates all Drupal.FilterStatus objects to reflect the current state. * * Automatically checks whether a filter is currently enabled or not. To - * support more finegrained + * support more finegrained. * * If a filter implements a live setting parser, then that will be used to * keep the HTML rules for the Drupal.FilterStatus object up-to-date. @@ -709,6 +848,8 @@ /** * Initializes Drupal.filterConfiguration. + * + * @type {Drupal~behavior} */ Drupal.behaviors.initializeFilterConfiguration = { attach: function (context, settings) { @@ -719,7 +860,8 @@ var nameAttribute = $checkbox.attr('name'); // The filter's checkbox has a name attribute of the form - // "filters[][status]", parse "" from it. + // "filters[][status]", parse "" + // from it. var filterID = nameAttribute.substring(8, nameAttribute.indexOf(']')); // Create a Drupal.FilterStatus object to track the state (whether it's diff --git a/core/modules/editor/js/editor.dialog.js b/core/modules/editor/js/editor.dialog.js index 3ec2372..04b9403 100644 --- a/core/modules/editor/js/editor.dialog.js +++ b/core/modules/editor/js/editor.dialog.js @@ -1,4 +1,5 @@ /** + * @file * AJAX commands used by Editor module. */ @@ -9,10 +10,18 @@ /** * Command to save the contents of an editor-provided modal. * - * This command does not close the open modal. It should be followed by a call - * to Drupal.AjaxCommands.prototype.closeDialog. Editors that are integrated - * with dialogs must independently listen for an editor:dialogsave event to save - * the changes into the contents of their interface. + * This command does not close the open modal. It should be followed by a + * call to `Drupal.AjaxCommands.prototype.closeDialog`. Editors that are + * integrated with dialogs must independently listen for an + * `editor:dialogsave` event to save the changes into the contents of their + * interface. + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {Array} response.values + * @param {number} [status] + * + * @fires event:editor:dialogsave */ Drupal.AjaxCommands.prototype.editorDialogSave = function (ajax, response, status) { $(window).trigger('editor:dialogsave', [response.values]); diff --git a/core/modules/editor/js/editor.formattedTextEditor.js b/core/modules/editor/js/editor.formattedTextEditor.js index e624f67..ba40173 100644 --- a/core/modules/editor/js/editor.formattedTextEditor.js +++ b/core/modules/editor/js/editor.formattedTextEditor.js @@ -10,26 +10,52 @@ * JavaScript would use: * - Drupal.editors.magical.attachInlineEditor() */ + (function ($, Drupal, drupalSettings) { "use strict"; - Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend({ + /** + * Editor. + * + * @constructor + * + * @augments Drupal.quickedit.EditorView + */ + Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.editor# */{ - // The text format for this field. + /** + * The text format for this field. + * + * @type {?string} + */ textFormat: null, - // Indicates whether this text format has transformations. + /** + * Indicates whether this text format has transformations. + * + * @type {?bool} + */ textFormatHasTransformations: null, - // Stores a reference to the text editor object for this field. + /** + * Stores a reference to the text editor object for this field. + * + * @type {?Drupal.quickedit.EditorModel} + */ textEditor: null, - // Stores the textual DOM element that is being in-place edited. + /** + * Stores the textual DOM element that is being in-place edited. + * + * @type {?jQuery} + */ $textElement: null, /** - * {@inheritdoc} + * @constructs + * + * @param {object} options */ initialize: function (options) { Drupal.quickedit.EditorView.prototype.initialize.call(this, options); @@ -46,14 +72,19 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {jQuery} */ getEditedElement: function () { return this.$textElement; }, /** - * {@inheritdoc} + * @inheritdoc + * + * @param {object} fieldModel + * @param {string} state */ stateChange: function (fieldModel, state) { var editorModel = this.model; @@ -143,14 +174,16 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {object} */ getQuickEditUISettings: function () { return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: false}; }, /** - * {@inheritdoc} + * @inheritdoc */ revert: function () { this.$textElement.html(this.model.get('originalValue')); @@ -162,7 +195,7 @@ * More accurately: it re-filters formatted text to exclude transformation * filters used by the text format. * - * @param Function callback + * @param {function} callback * A callback function that will receive the untransformed text. * * @see \Drupal\editor\Ajax\GetUntransformedTextCommand diff --git a/core/modules/editor/js/editor.js b/core/modules/editor/js/editor.js index 1b26b0f..61f4b96 100644 --- a/core/modules/editor/js/editor.js +++ b/core/modules/editor/js/editor.js @@ -10,10 +10,10 @@ /** * Finds the text area field associated with the given text format selector. * - * @param jQuery $formatSelector + * @param {jQuery} $formatSelector * A text format selector DOM element. * - * @return DOM + * @return {HTMLElement} * The text area DOM element, if it was found. */ function findFieldForFormatSelector($formatSelector) { @@ -26,9 +26,9 @@ /** * Changes the text editor on a text area. * - * @param DOM field + * @param {HTMLElement} field * The text area DOM element. - * @param String newFormatID + * @param {string} newFormatID * The text format we're changing to; the text editor for the currently * active text format will be detached, and the text editor for the new text * format will be attached. @@ -58,7 +58,7 @@ /** * Handles changes in text format. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ function onTextFormatChange(event) { var $select = $(event.target); @@ -129,11 +129,15 @@ /** * Initialize an empty object for editors to place their attachment code. + * + * @namespace */ Drupal.editors = {}; /** * Enables editors on text_format elements. + * + * @type {Drupal~behavior} */ Drupal.behaviors.editor = { attach: function (context, settings) { @@ -214,6 +218,19 @@ } }; + /** + * Attach editor behaviors to the field. + * + * @param {HTMLElement} field + * The textarea DOM element. + * @param {object} format + * The text format that's being activated, from + * drupalSettings.editor.formats. + * + * @listens event:change + * + * @fires event:formUpdated + */ Drupal.editorAttach = function (field, format) { if (format.editor) { // HTML5 validation cannot ever work for WYSIWYG editors, because WYSIWYG @@ -240,6 +257,17 @@ } }; + /** + * Detach editor behaviors from the field. + * + * @param {HTMLElement} field + * The textarea DOM element. + * @param {object} format + * The text format that's being activated, from + * drupalSettings.editor.formats. + * @param {string} trigger + * Trigger value from the detach behavior. + */ Drupal.editorDetach = function (field, format, trigger) { if (format.editor) { // Restore the HTML5 validation "required" attribute if it was removed in @@ -261,13 +289,14 @@ /** * Filter away XSS attack vectors when switching text formats. * - * @param DOM field + * @param {HTMLElement} field * The textarea DOM element. - * @param Object format - * The text format that's being activated, from drupalSettings.editor.formats. - * @param String originalFormatID + * @param {object} format + * The text format that's being activated, from + * drupalSettings.editor.formats. + * @param {string} originalFormatID * The text format ID of the original text format. - * @param Function callback + * @param {function} callback * A callback to be called (with no parameters) after the field's value has * been XSS filtered. */ diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js index 9bdbe50..fceb94e 100644 --- a/core/modules/field_ui/field_ui.js +++ b/core/modules/field_ui/field_ui.js @@ -7,14 +7,17 @@ "use strict"; + /** + * @type {Drupal~behavior} + */ Drupal.behaviors.fieldUIFieldStorageAddForm = { attach: function (context) { var $form = $(context).find('#field-ui-field-storage-add-form').once('field_ui_add'); if ($form.length) { - // Add a few 'form-required' css classes here. We can not use the Form API - // '#required' property because both label elements for "add new" and - // "re-use existing" can never be filled and submitted at the same time. - // The actual validation will happen server-side. + // Add a few 'form-required' css classes here. We can not use the Form + // API '#required' property because both label elements for "add new" + // and "re-use existing" can never be filled and submitted at the same + // time. The actual validation will happen server-side. $form.find( '.form-item-label label,' + '.form-item-field-name label,' + @@ -52,6 +55,10 @@ } }; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.fieldUIDisplayOverview = { attach: function (context, settings) { $(context).find('table#field-display-overview').once('field-display-overview').each(function () { @@ -60,9 +67,17 @@ } }; + /** + * @namespace + */ Drupal.fieldUIOverview = { + /** * Attaches the fieldUIOverview behavior. + * + * @param {HTMLTableElement} table + * @param {object} rowsData + * @param {object} rowHandlers */ attach: function (table, rowsData, rowHandlers) { var tableDrag = Drupal.tableDrag[table.id]; @@ -79,7 +94,8 @@ var data = rowsData[row.id]; data.tableDrag = tableDrag; - // Create the row handler, make it accessible from the DOM row element. + // Create the row handler, make it accessible from the DOM row + // element. var rowHandler = new rowHandlers[data.rowHandler](row, data); $(row).data('fieldUIRowHandler', rowHandler); } @@ -140,9 +156,7 @@ * * Copied from block.js. * - * @param table - * The table DOM element. - * @param rowObject + * @param {HTMLElement} draggedRow * The tableDrag rowObject for the row being dragged. */ onSwap: function (draggedRow) { @@ -152,7 +166,8 @@ // If the dragged row is in this region, but above the message row, swap // it down one space. if ($this.prev('tr').get(0) === rowObject.group[rowObject.group.length - 1]) { - // Prevent a recursion problem when using the keyboard to move rows up. + // Prevent a recursion problem when using the keyboard to move rows + // up. if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) { rowObject.swap('after', this); } @@ -175,7 +190,7 @@ * The #ajax behavior is therefore not attached directly to the selects, but * triggered manually through a hidden #ajax 'Refresh' button. * - * @param rows + * @param {object} rows * A hash object, whose keys are the names of the rows to refresh (they * will receive the 'ajax-new-content' effect on the server side), and * whose values are the DOM element in the row that should get an Ajax @@ -210,6 +225,8 @@ /** * Row handlers for the 'Manage display' screen. + * + * @namespace */ Drupal.fieldUIDisplayOverview = {}; @@ -218,10 +235,14 @@ * * This handler is used for both fields and 'extra fields' rows. * - * @param row + * @constructor + * + * @param {HTMLTableRowElement} row * The row DOM element. - * @param data + * @param {object} data * Additional data to be populated in the constructed object. + * + * @return {Drupal.fieldUIDisplayOverview.field} */ Drupal.fieldUIDisplayOverview.field = function (row, data) { this.row = row; @@ -237,8 +258,11 @@ }; Drupal.fieldUIDisplayOverview.field.prototype = { + /** * Returns the region corresponding to the current form values of the row. + * + * @return {string} */ getRegion: function () { return (this.$pluginSelect.val() === 'hidden') ? 'hidden' : 'content'; @@ -247,16 +271,18 @@ /** * Reacts to a row being changed regions. * - * This function is called when the row is moved to a different region, as a + * This function is called when the row is moved to a different region, as + * a * result of either : - * - a drag-and-drop action (the row's form elements then probably need to be - * updated accordingly) + * - a drag-and-drop action (the row's form elements then probably need to + * be updated accordingly) * - user input in one of the form elements watched by the * Drupal.fieldUIOverview.onChange change listener. * - * @param region + * @param {string} region * The name of the new region for the row. - * @return + * + * @return {object} * A hash object indicating which rows should be Ajax-updated as a result * of the change, in the format expected by * Drupal.displayOverview.AJAXRefreshRows(). @@ -271,8 +297,9 @@ // if (region !== 'hidden') { if (region === 'content') { if (currentValue === 'hidden') { - // Restore the formatter back to the default formatter. Pseudo-fields do - // not have default formatters, we just return to 'visible' for those. + // Restore the formatter back to the default formatter. Pseudo-fields + // do not have default formatters, we just return to 'visible' for + // those. value = (typeof this.defaultPlugin !== 'undefined') ? this.defaultPlugin : this.$pluginSelect.find('option').val(); } } diff --git a/core/modules/file/file.js b/core/modules/file/file.js index 8f0c9db..73487e2 100644 --- a/core/modules/file/file.js +++ b/core/modules/file/file.js @@ -13,6 +13,8 @@ /** * Attach behaviors to managed file element upload fields. + * + * @type {Drupal~behavior} */ Drupal.behaviors.fileValidateAutoAttach = { attach: function (context, settings) { @@ -49,6 +51,8 @@ /** * Attach behaviors to managed file element upload fields. + * + * @type {Drupal~behavior} */ Drupal.behaviors.fileAutoUpload = { attach: function (context) { @@ -63,6 +67,8 @@ /** * Attach behaviors to the file upload and remove buttons. + * + * @type {Drupal~behavior} */ Drupal.behaviors.fileButtons = { attach: function (context) { @@ -79,6 +85,8 @@ /** * Attach behaviors to links within managed file elements. + * + * @type {Drupal~behavior} */ Drupal.behaviors.filePreviewLinks = { attach: function (context) { @@ -91,10 +99,17 @@ /** * File upload utility functions. + * + * @namespace */ Drupal.file = Drupal.file || { + /** * Client-side file input validation of file extensions. + * + * @name Drupal.file.validateExtension + * + * @param {jQuery.Event} event */ validateExtension: function (event) { event.preventDefault(); @@ -124,14 +139,24 @@ } } }, + /** * Trigger the upload_button mouse event to auto-upload as a managed file. + * + * @name Drupal.file.triggetUploadButton + * + * @param {jQuery.Event} event */ triggerUploadButton: function (event) { $(event.target).closest('.form-managed-file').find('.js-form-submit').trigger('mousedown'); }, + /** * Prevent file uploads when using buttons not intended to upload. + * + * @name Drupal.file.disableFields + * + * @param {jQuery.Event} event */ disableFields: function (event) { var $clickedButton = $(this).findOnce('ajax'); @@ -149,20 +174,26 @@ // Temporarily disable upload fields other than the one we're currently // working with. Filter out fields that are already disabled so that they - // do not get enabled when we re-enable these fields at the end of behavior - // processing. Re-enable in a setTimeout set to a relatively short amount - // of time (1 second). All the other mousedown handlers (like Drupal's Ajax - // behaviors) are executed before any timeout functions are called, so we - // don't have to worry about the fields being re-enabled too soon. - // @todo If the previous sentence is true, why not set the timeout to 0? + // do not get enabled when we re-enable these fields at the end of + // behavior processing. Re-enable in a setTimeout set to a relatively + // short amount of time (1 second). All the other mousedown handlers + // (like Drupal's Ajax behaviors) are executed before any timeout + // functions are called, so we don't have to worry about the fields being + // re-enabled too soon. @todo If the previous sentence is true, why not + // set the timeout to 0? var $fieldsToTemporarilyDisable = $('div.form-managed-file input.form-file').not($enabledFields).not(':disabled'); $fieldsToTemporarilyDisable.prop('disabled', true); setTimeout(function () { $fieldsToTemporarilyDisable.prop('disabled', false); }, 1000); }, + /** * Add progress bar support if possible. + * + * @name Drupal.file.progressBar + * + * @param {jQuery.Event} event */ progressBar: function (event) { var $clickedButton = $(this); @@ -183,8 +214,13 @@ $clickedButton.closest('div.form-managed-file').find('div.ajax-progress-bar').slideDown(); }, 500); }, + /** * Open links to files within forms in a new window. + * + * @name Drupal.file.openInNewWindow + * + * @param {jQuery.Event} event */ openInNewWindow: function (event) { event.preventDefault(); diff --git a/core/modules/filter/filter.admin.js b/core/modules/filter/filter.admin.js index ba650c8..e14634c 100644 --- a/core/modules/filter/filter.admin.js +++ b/core/modules/filter/filter.admin.js @@ -7,6 +7,10 @@ "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.filterStatus = { attach: function (context, settings) { var $context = $(context); diff --git a/core/modules/filter/filter.filter_html.admin.js b/core/modules/filter/filter.filter_html.admin.js index d8e36c4..0854cf8 100644 --- a/core/modules/filter/filter.filter_html.admin.js +++ b/core/modules/filter/filter.filter_html.admin.js @@ -7,12 +7,19 @@ "use strict"; - /** - * Implement a live setting parser to prevent text editors from automatically - * enabling buttons that are not allowed by this filter's configuration. - */ if (Drupal.filterConfiguration) { + + /** + * Implement a live setting parser to prevent text editors from automatically + * enabling buttons that are not allowed by this filter's configuration. + * + * @namespace + */ Drupal.filterConfiguration.liveSettingParsers.filter_html = { + + /** + * @return {Array} + */ getRules: function () { var currentValue = $('#edit-filters-filter-html-settings-allowed-html').val(); var rules = []; @@ -37,6 +44,12 @@ }; } + /** + * + * @type {Drupal~behavior} + * + * @todo Remove everything but 'attach' and 'detach' and make a proper object. + */ Drupal.behaviors.filterFilterHtmlUpdating = { // The form item contains the "Allowed HTML tags" setting. @@ -114,12 +127,12 @@ * The filter_html filter is only concerned with the required tags, not with * any properties, nor with each feature's "allowed" tags. * - * @param Array userAllowedTags + * @param {Array} userAllowedTags * The list of user-defined allowed tags. - * @param Object newFeatures + * @param {object} newFeatures * A list of Drupal.EditorFeature objects' rules, keyed by their name. * - * @return Array + * @return {Array} * A list of new allowed tags. */ _calculateAutoAllowedTags: function (userAllowedTags, newFeatures) { @@ -141,10 +154,10 @@ /** * Parses the value of this.$allowedHTMLFormItem. * - * @param String setting + * @param {string} setting * The string representation of the setting. e.g. "


      " * - * @return Array + * @return {Array} * The array representation of the setting. e.g. ['p', 'br', 'a'] */ _parseSetting: function (setting) { @@ -154,10 +167,10 @@ /** * Generates the value of this.$allowedHTMLFormItem. * - * @param Array setting + * @param {Array} tags * The array representation of the setting. e.g. ['p', 'br', 'a'] * - * @return Array + * @return {Array} * The string representation of the setting. e.g. "


      " */ _generateSetting: function (tags) { @@ -169,9 +182,10 @@ /** * Theme function for the filter_html update message. * - * @param Array tags + * @param {Array} tags * An array of the new tags that are to be allowed. - * @return + * + * @return {string} * The corresponding HTML. */ Drupal.theme.filterFilterHTMLUpdateMessage = function (tags) { diff --git a/core/modules/filter/filter.js b/core/modules/filter/filter.js index 38797e5..58b595f 100644 --- a/core/modules/filter/filter.js +++ b/core/modules/filter/filter.js @@ -9,6 +9,8 @@ /** * Displays the guidelines of the selected text format automatically. + * + * @type {Drupal~behavior} */ Drupal.behaviors.filterGuidelines = { attach: function (context) { diff --git a/core/modules/history/js/history.js b/core/modules/history/js/history.js index 5030790..cba7787 100644 --- a/core/modules/history/js/history.js +++ b/core/modules/history/js/history.js @@ -1,8 +1,10 @@ /** + * @file * JavaScript API for the History module, with client-side caching. * * May only be loaded for authenticated users, with the History module enabled. */ + (function ($, Drupal, drupalSettings, storage) { "use strict"; @@ -19,14 +21,17 @@ embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps; } + /** + * @namespace + */ Drupal.history = { /** * Fetch "last read" timestamps for the given nodes. * - * @param Array nodeIDs + * @param {Array} nodeIDs * An array of node IDs. - * @param Function callback + * @param {function} callback * A callback that is called after the requested timestamps were fetched. */ fetchTimestamps: function (nodeIDs, callback) { @@ -55,10 +60,10 @@ /** * Get the last read timestamp for the given node. * - * @param Number|String nodeID + * @param {number|string} nodeID * A node ID. * - * @return Number + * @return {number} * A UNIX timestamp. */ getLastRead: function (nodeID) { @@ -72,7 +77,7 @@ /** * Marks a node as read, store the last read timestamp in client-side storage. * - * @param Number|String nodeID + * @param {number|string} nodeID * A node ID. */ markAsRead: function (nodeID) { @@ -98,12 +103,12 @@ * Any content that was published before the oldest known reading also never * gets a "new" or "updated" indicator, because it must've been read already. * - * @param Number|String nodeID + * @param {number|string} nodeID * A node ID. - * @param Number contentTimestamp + * @param {number} contentTimestamp * The time at which some content (e.g. a comment) was published. * - * @return Boolean + * @return {bool} * Whether a server check is necessary for the given node and its timestamp. */ needsServerCheck: function (nodeID, contentTimestamp) { diff --git a/core/modules/history/js/mark-as-read.js b/core/modules/history/js/mark-as-read.js index b2ba518..848d351 100644 --- a/core/modules/history/js/mark-as-read.js +++ b/core/modules/history/js/mark-as-read.js @@ -1,8 +1,12 @@ /** + * @file * Marks the nodes listed in drupalSettings.history.nodesToMarkAsRead as read. * * Uses the History module JavaScript API. + * + * @see Drupal.history */ + (function (window, Drupal, drupalSettings) { "use strict"; diff --git a/core/modules/language/language.admin.js b/core/modules/language/language.admin.js index 0c14619..0a7c9e1 100644 --- a/core/modules/language/language.admin.js +++ b/core/modules/language/language.admin.js @@ -1,9 +1,16 @@ +/** + * @file + * Language admin behavior. + */ + (function ($, Drupal) { "use strict"; /** * Makes language negotiation inherit user interface negotiation. + * + * @type {Drupal~behavior} */ Drupal.behaviors.negotiationLanguage = { attach: function () { diff --git a/core/modules/locale/locale.admin.js b/core/modules/locale/locale.admin.js index 9c32c5e..c7bac21 100644 --- a/core/modules/locale/locale.admin.js +++ b/core/modules/locale/locale.admin.js @@ -1,9 +1,16 @@ +/** + * @file + * Locale admin behavior. + */ + (function ($, Drupal) { "use strict"; /** - * Marks changes of translations + * Marks changes of translations. + * + * @type {Drupal~behavior} */ Drupal.behaviors.localeTranslateDirty = { attach: function () { @@ -41,6 +48,8 @@ /** * Show/hide the description details on Available translation updates page. + * + * @type {Drupal~behavior} */ Drupal.behaviors.hideUpdateInformation = { attach: function (context, settings) { @@ -73,10 +82,20 @@ } }; - $.extend(Drupal.theme, { + $.extend(Drupal.theme, /** @lends Drupal.theme */{ + + /** + * + * @return {string} + */ localeTranslateChangedMarker: function () { return '*'; }, + + /** + * + * @return {string} + */ localeTranslateChangedWarning: function () { return '

      '; } diff --git a/core/modules/locale/locale.bulk.js b/core/modules/locale/locale.bulk.js index 80952d0..7ddafd0 100644 --- a/core/modules/locale/locale.bulk.js +++ b/core/modules/locale/locale.bulk.js @@ -1,3 +1,8 @@ +/** + * @file + * Locale behavior. + */ + (function ($, Drupal) { "use strict"; @@ -6,6 +11,8 @@ * Select the language code of an imported file based on its filename. * * This only works if the file name ends with "LANGCODE.po". + * + * @type {Drupal~behavior} */ Drupal.behaviors.importLanguageCodeSelector = { attach: function (context, settings) { diff --git a/core/modules/locale/locale.datepicker.js b/core/modules/locale/locale.datepicker.js index 4628eda..197fc42 100644 --- a/core/modules/locale/locale.datepicker.js +++ b/core/modules/locale/locale.datepicker.js @@ -9,6 +9,8 @@ /** * Attaches language support to the jQuery UI datepicker component. + * + * @type {Drupal~behavior} */ Drupal.behaviors.localeDatepicker = { attach: function (context, settings) { diff --git a/core/modules/locale/tests/locale_test.js b/core/modules/locale/tests/locale_test.js index 336d979..ba62b75 100644 --- a/core/modules/locale/tests/locale_test.js +++ b/core/modules/locale/tests/locale_test.js @@ -1,6 +1,8 @@ /** * @file * JavaScript for locale_test.module. + * + * @ignore */ Drupal.t("Standard Call t"); diff --git a/core/modules/menu_ui/menu_ui.admin.js b/core/modules/menu_ui/menu_ui.admin.js index 8c037c5..5120d6e 100644 --- a/core/modules/menu_ui/menu_ui.admin.js +++ b/core/modules/menu_ui/menu_ui.admin.js @@ -1,7 +1,16 @@ +/** + * @file + * Menu UI admin behaviors. + */ + (function ($) { "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.menuUiChangeParentItems = { attach: function (context, settings) { var $menu = $('#edit-menu').once('menu-parent'); diff --git a/core/modules/menu_ui/menu_ui.js b/core/modules/menu_ui/menu_ui.js index 3859b7a..2103952 100644 --- a/core/modules/menu_ui/menu_ui.js +++ b/core/modules/menu_ui/menu_ui.js @@ -1,7 +1,16 @@ +/** + * @file + * Menu UI behaviors. + */ + (function ($) { "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.menuUiDetailsSummaries = { attach: function (context) { $(context).find('.menu-link-form').drupalSetSummary(function (context) { @@ -18,6 +27,8 @@ /** * Automatically fill in a menu link title, if possible. + * + * @type {Drupal~behavior} */ Drupal.behaviors.menuUiLinkAutomaticTitle = { attach: function (context) { diff --git a/core/modules/node/content_types.js b/core/modules/node/content_types.js index c77a2c3..77ab168 100644 --- a/core/modules/node/content_types.js +++ b/core/modules/node/content_types.js @@ -7,6 +7,10 @@ "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.contentTypes = { attach: function (context) { var $context = $(context); diff --git a/core/modules/node/node.js b/core/modules/node/node.js index cf0f979..af2ed46 100644 --- a/core/modules/node/node.js +++ b/core/modules/node/node.js @@ -7,6 +7,10 @@ "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.nodeDetailsSummaries = { attach: function (context) { var $context = $(context); diff --git a/core/modules/node/node.preview.js b/core/modules/node/node.preview.js index 09ce7a0..d69cf3b 100644 --- a/core/modules/node/node.preview.js +++ b/core/modules/node/node.preview.js @@ -1,3 +1,8 @@ +/** + * @file + * Preview behaviors. + */ + (function ($, Drupal) { "use strict"; @@ -5,6 +10,8 @@ /** * Disabling all links (except local fragment identifiers such as href="#frag") * in node previews to prevent users from leaving the page. + * + * @type {Drupal~behavior} */ Drupal.behaviors.nodePreviewDestroyLinks = { attach: function (context) { @@ -52,6 +59,8 @@ /** * Switch view mode. + * + * @type {Drupal~behavior} */ Drupal.behaviors.nodePreviewSwitchViewMode = { attach: function (context) { @@ -64,6 +73,10 @@ } }; + /** + * + * @return {string} + */ Drupal.theme.nodePreviewModal = function () { return '

      ' + Drupal.t('Leaving the preview will cause unsaved changes to be lost. Are you sure you want to leave the preview?') + diff --git a/core/modules/path/path.js b/core/modules/path/path.js index aa91bb2..474cf87 100644 --- a/core/modules/path/path.js +++ b/core/modules/path/path.js @@ -6,6 +6,10 @@ "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.pathDetailsSummaries = { attach: function (context) { $(context).find('.path-form').drupalSetSummary(function (context) { diff --git a/core/modules/quickedit/js/editors/formEditor.js b/core/modules/quickedit/js/editors/formEditor.js index 8dc3fb5..ed20245 100644 --- a/core/modules/quickedit/js/editors/formEditor.js +++ b/core/modules/quickedit/js/editors/formEditor.js @@ -7,16 +7,32 @@ "use strict"; - Drupal.quickedit.editors.form = Drupal.quickedit.EditorView.extend({ + /** + * @constructor + * + * @augments Drupal.quickedit.EditorView + */ + Drupal.quickedit.editors.form = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.form# */{ - // Tracks the form container DOM element that is used while in-place editing. + /** + * Tracks the form container DOM element that is used while in-place editing. + * + * @type {?jQuery} + */ $formContainer: null, - // Holds the Drupal.ajax object + /** + * Holds the Drupal.Ajax object. + * + * @type {Drupal.Ajax} + */ formSaveAjax: null, /** - * {@inheritdoc} + * @inheritdoc + * + * @param {object} fieldModel + * @param {string} state */ stateChange: function (fieldModel, state) { var from = fieldModel.previous('state'); @@ -24,28 +40,36 @@ switch (to) { case 'inactive': break; + case 'candidate': if (from !== 'inactive') { this.removeForm(); } break; + case 'highlighted': break; + case 'activating': // If coming from an invalid state, then the form is already loaded. if (from !== 'invalid') { this.loadForm(); } break; + case 'active': break; + case 'changed': break; + case 'saving': this.save(); break; + case 'saved': break; + case 'invalid': this.showValidationErrors(); break; @@ -53,7 +77,9 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {object} */ getQuickEditUISettings: function () { return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: true}; @@ -155,7 +181,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ save: function () { var $formContainer = this.$formContainer; @@ -213,7 +239,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ showValidationErrors: function () { this.$formContainer diff --git a/core/modules/quickedit/js/editors/plainTextEditor.js b/core/modules/quickedit/js/editors/plainTextEditor.js index f380a40..98bf4bf 100644 --- a/core/modules/quickedit/js/editors/plainTextEditor.js +++ b/core/modules/quickedit/js/editors/plainTextEditor.js @@ -1,19 +1,25 @@ /** * @file - * contentEditable-based in-place editor for plain text content. + * ContentEditable-based in-place editor for plain text content. */ (function ($, _, Drupal) { "use strict"; - Drupal.quickedit.editors.plain_text = Drupal.quickedit.EditorView.extend({ + Drupal.quickedit.editors.plain_text = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.plain_text# */{ - // Stores the textual DOM element that is being in-place edited. + /** + * Stores the textual DOM element that is being in-place edited. + */ $textElement: null, /** - * {@inheritdoc} + * @constructs + * + * @augments Drupal.quickedit.EditorView + * + * @param {object} options */ initialize: function (options) { Drupal.quickedit.EditorView.prototype.initialize.call(this, options); @@ -32,7 +38,7 @@ } editorModel.set('originalValue', $.trim(this.$textElement.text())); - // Sets the state to 'changed' whenever the value changes + // Sets the state to 'changed' whenever the value changes. var previousText = editorModel.get('originalValue'); $textElement.on('keyup paste', function (event) { var currentText = $.trim($textElement.text()); @@ -45,14 +51,20 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {jQuery} */ getEditedElement: function () { return this.$textElement; }, /** - * {@inheritdoc} + * @inheritdoc + * + * @param {object} fieldModel + * @param {string} state + * @param {object} options */ stateChange: function (fieldModel, state, options) { var from = fieldModel.previous('state'); @@ -60,6 +72,7 @@ switch (to) { case 'inactive': break; + case 'candidate': if (from !== 'inactive') { this.$textElement.removeAttr('contenteditable'); @@ -68,8 +81,10 @@ this.removeValidationErrors(); } break; + case 'highlighted': break; + case 'activating': // Defer updating the field model until the current state change has // propagated, to not trigger a nested state change event. @@ -77,19 +92,24 @@ fieldModel.set('state', 'active'); }); break; + case 'active': this.$textElement.attr('contenteditable', 'true'); break; + case 'changed': break; + case 'saving': if (from === 'invalid') { this.removeValidationErrors(); } this.save(options); break; + case 'saved': break; + case 'invalid': this.showValidationErrors(); break; @@ -97,14 +117,16 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {object} */ getQuickEditUISettings: function () { return {padding: true, unifiedToolbar: false, fullWidthToolbar: false, popup: false}; }, /** - * {@inheritdoc} + * @inheritdoc */ revert: function () { this.$textElement.html(this.model.get('originalValue')); diff --git a/core/modules/quickedit/js/models/AppModel.js b/core/modules/quickedit/js/models/AppModel.js index 1682e7f..a94fecc 100644 --- a/core/modules/quickedit/js/models/AppModel.js +++ b/core/modules/quickedit/js/models/AppModel.js @@ -9,18 +9,47 @@ "use strict"; - Drupal.quickedit.AppModel = Backbone.Model.extend({ + /** + * @constructor + * + * @augments Backbone.Model + */ + Drupal.quickedit.AppModel = Backbone.Model.extend(/** @lends Drupal.quickedit.AppModel# */{ - defaults: { - // The currently state = 'highlighted' Drupal.quickedit.FieldModel, if - // any. - // @see Drupal.quickedit.FieldModel.states + /** + * @type {object} + * + * @prop {?Drupal.quickedit.FieldModel} highlightedField + * @prop {?Drupal.quickedit.FieldModel} activeField + * @prop {?Drupal.dialog~dialogDefinition} activeModal + */ + defaults: /** @lends Drupal.quickedit.AppModel# */{ + + /** + * The currently state = 'highlighted' Drupal.quickedit.FieldModel, if + * any. + * + * @type {?Drupal.quickedit.FieldModel} + * + * @see Drupal.quickedit.FieldModel.states + */ highlightedField: null, - // The currently state = 'active' Drupal.quickedit.FieldModel, if any. - // @see Drupal.quickedit.FieldModel.states + + /** + * The currently state = 'active' Drupal.quickedit.FieldModel, if any. + * + * @type {?Drupal.quickedit.FieldModel} + * + * @see Drupal.quickedit.FieldModel.states + */ activeField: null, - // Reference to a Drupal.dialog instance if a state change requires - // confirmation. + + /** + * Reference to a Drupal.dialog instance if a state change requires + * confirmation. + * + * @type {?Drupal.dialog~dialogDefinition} + */ activeModal: null } diff --git a/core/modules/quickedit/js/models/BaseModel.js b/core/modules/quickedit/js/models/BaseModel.js index 06112cf..22f6b2f 100644 --- a/core/modules/quickedit/js/models/BaseModel.js +++ b/core/modules/quickedit/js/models/BaseModel.js @@ -7,10 +7,16 @@ "use strict"; - Drupal.quickedit.BaseModel = Backbone.Model.extend({ + Drupal.quickedit.BaseModel = Backbone.Model.extend(/** @lends Drupal.quickedit.BaseModel# */{ /** - * {@inheritdoc} + * @constructs + * + * @augments Backbone.Model + * + * @param {object} options + * + * @return {Drupal.quickedit.BaseModel} */ initialize: function (options) { this.__initialized = true; @@ -18,7 +24,12 @@ }, /** - * {@inheritdoc} + * + * @param {object|string} key + * @param {*} val + * @param {*} [options] + * + * @return {*} */ set: function (key, val, options) { if (this.__initialized) { diff --git a/core/modules/quickedit/js/models/EditorModel.js b/core/modules/quickedit/js/models/EditorModel.js index 059b13e..bd2019a 100644 --- a/core/modules/quickedit/js/models/EditorModel.js +++ b/core/modules/quickedit/js/models/EditorModel.js @@ -9,16 +9,35 @@ "use strict"; - Drupal.quickedit.EditorModel = Backbone.Model.extend({ + /** + * @constructor + * + * @augments Backbone.Model + */ + Drupal.quickedit.EditorModel = Backbone.Model.extend(/** @lends Drupal.quickedit.EditorModel# */{ - defaults: { - // Not the full HTML representation of this field, but the "actual" - // original value of the field, stored by the used in-place editor, and - // in a representation that can be chosen by the in-place editor. + /** + * @prop {?null} originalValue + * @prop {?null} currentValue + * @prop {?null} validationErrors + */ + defaults: /** @lends Drupal.quickedit.EditorModel# */{ + + /** + * Not the full HTML representation of this field, but the "actual" + * original value of the field, stored by the used in-place editor, and + * in a representation that can be chosen by the in-place editor. + */ originalValue: null, - // Analogous to originalValue, but the current value. + + /** + * Analogous to originalValue, but the current value. + */ currentValue: null, - // Stores any validation errors to be rendered. + + /** + * Stores any validation errors to be rendered. + */ validationErrors: null } diff --git a/core/modules/quickedit/js/models/EntityModel.js b/core/modules/quickedit/js/models/EntityModel.js index 0760aed..010df9a 100644 --- a/core/modules/quickedit/js/models/EntityModel.js +++ b/core/modules/quickedit/js/models/EntityModel.js @@ -7,58 +7,100 @@ "use strict"; - Drupal.quickedit.EntityModel = Drupal.quickedit.BaseModel.extend({ + Drupal.quickedit.EntityModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.EntityModel# */{ - defaults: { - // The DOM element that represents this entity. It may seem bizarre to - // have a DOM element in a Backbone Model, but we need to be able to map - // entities in the DOM to EntityModels in memory. + /** + * @type {object} + */ + defaults: /** @lends Drupal.quickedit.EntityModel# */{ + + /** + * The DOM element that represents this entity. It may seem bizarre to + * have a DOM element in a Backbone Model, but we need to be able to map + * entities in the DOM to EntityModels in memory. + */ el: null, - // An entity ID, of the form "/", e.g. "node/1". + + /** + * An entity ID, of the form "/", e.g. "node/1". + */ entityID: null, - // An entity instance ID. The first instance of a specific entity (i.e. with - // a given entity ID) is assigned 0, the second 1, and so on. + + /** + * An entity instance ID. The first instance of a specific entity (i.e. with + * a given entity ID) is assigned 0, the second 1, and so on. + */ entityInstanceID: null, - // The unique ID of this entity instance on the page, of the form "/[entity instance ID]", e.g. "node/1[0]". + + /** + * The unique ID of this entity instance on the page, of the form `/[entity instance ID]`, e.g. `node/1[0]`. + */ id: null, - // The label of the entity. + + /** + * The label of the entity. + */ label: null, - // A Drupal.quickedit.FieldCollection for all fields of this entity. + + /** + * A Drupal.quickedit.FieldCollection for all fields of this entity. + */ fields: null, // The attributes below are stateful. The ones above will never change // during the life of a EntityModel instance. - - // Indicates whether this instance of this entity is currently being - // edited in-place. + /** + * Indicates whether this instance of this entity is currently being + * edited in-place. + */ isActive: false, - // Whether one or more fields have already been stored in - // PrivateTempStore. + + /** + * Whether one or more fields have already been stored in + * PrivateTempStore. + */ inTempStore: false, - // Whether one or more fields have already been stored in PrivateTempStore - // *or* the field that's currently being edited is in the 'changed' or a - // later state. In other words, this boolean indicates whether a "Save" - // button is necessary or not. + + /** + * Whether one or more fields have already been stored in PrivateTempStore + * *or* the field that's currently being edited is in the 'changed' or a + * later state. In other words, this boolean indicates whether a "Save" + * button is necessary or not. + */ isDirty: false, - // Whether the request to the server has been made to commit this entity. - // Used to prevent multiple such requests. + + /** + * Whether the request to the server has been made to commit this entity. + * Used to prevent multiple such requests. + */ isCommitting: false, - // The current processing state of an entity. + + /** + * The current processing state of an entity. + */ state: 'closed', - // The IDs of the fields whose new values have been stored in - // PrivateTempStore. We must store this on the EntityModel as well (even - // though it already is on the FieldModel) because when a field is - // rerendered, its FieldModel is destroyed and this allows us to - // transition it back to the proper state. + + /** + * The IDs of the fields whose new values have been stored in + * PrivateTempStore. We must store this on the EntityModel as well (even + * though it already is on the FieldModel) because when a field is + * rerendered, its FieldModel is destroyed and this allows us to + * transition it back to the proper state. + */ fieldsInTempStore: [], - // A flag the tells the application that this EntityModel must be reloaded - // in order to restore the original values to its fields in the client. + + /** + * A flag the tells the application that this EntityModel must be reloaded + * in order to restore the original values to its fields in the client. + */ reload: false }, /** - * {@inheritdoc} + * @constructs + * + * @augments Drupal.quickedit.BaseModel */ initialize: function () { this.set('fields', new Drupal.quickedit.FieldCollection()); @@ -77,10 +119,11 @@ /** * Updates FieldModels' states when an EntityModel change occurs. * - * @param Drupal.quickedit.EntityModel entityModel - * @param String state - * The state of the associated entity. One of Drupal.quickedit.EntityModel.states. - * @param Object options + * @param {Drupal.quickedit.EntityModel} entityModel + * @param {string} state + * The state of the associated entity. One of + * {@link Drupal.quickedit.EntityModel.states}. + * @param {object} options */ stateChange: function (entityModel, state, options) { var to = state; @@ -195,12 +238,12 @@ * * Helper function. * - * @param Drupal.quickedit.EntityModel entityModel + * @param {Drupal.quickedit.EntityModel} entityModel * The model of the entity for which a field's state attribute has changed. - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * The model of the field whose state attribute has changed. * - * @see fieldStateChange() + * @see Drupal.quickedit.EntityModel#fieldStateChange */ _updateInTempStoreAttributes: function (entityModel, fieldModel) { var current = fieldModel.get('state'); @@ -233,10 +276,11 @@ /** * Reacts to state changes in this entity's fields. * - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * The model of the field whose state attribute changed. - * @param String state - * The state of the associated field. One of Drupal.quickedit.FieldModel.states. + * @param {string} state + * The state of the associated field. One of + * {@link Drupal.quickedit.FieldModel.states}. */ fieldStateChange: function (fieldModel, state) { var entityModel = this; @@ -354,10 +398,10 @@ /** * Fires an AJAX request to the REST save URL for an entity. * - * @param options + * @param {object} options * An object of options that contains: - * - success: (optional) A function to invoke if the entity is success- - * fully saved. + * @param {function} [options.success] + * A function to invoke if the entity is successfully saved. */ save: function (options) { var entityModel = this; @@ -393,19 +437,22 @@ }, /** - * {@inheritdoc} * - * @param Object attrs + * @param {object} attrs * The attributes changes in the save or set call. - * @param Object options + * @param {object} options * An object with the following option: - * - String reason (optional): a string that conveys a particular reason - * to allow for an exceptional state change. - * - Array accept-field-states (optional) An array of strings that - * represent field states that the entities must be in to validate. For - * example, if accept-field-states is ['candidate', 'highlighted'], then - * all the fields of the entity must be in either of these two states - * for the save or set call to validate and proceed. + * @param {string} [options.reason] + * A string that conveys a particular reason to allow for an exceptional + * state change. + * @param {Array} options.accept-field-states + * An array of strings that represent field states that the entities must + * be in to validate. For example, if `accept-field-states` is + * `['candidate', 'highlighted']`, then all the fields of the entity must + * be in either of these two states for the save or set call to + * validate and proceed. + * + * @return {string} */ validate: function (attrs, options) { var acceptedFieldStates = options['accept-field-states'] || []; @@ -444,7 +491,18 @@ } }, - // Like @see AppView.acceptEditorStateChange() + /** + * + * @param {string} from + * @param {string} to + * @param {object} context + * @param {string} context.reason + * @param {bool} context.confirming + * + * @return {bool} + * + * @see Drupal.quickedit.AppView#acceptEditorStateChange + */ _acceptStateChange: function (from, to, context) { var accept = true; @@ -481,9 +539,11 @@ }, /** - * @param Array acceptedFieldStates - * @see validate() - * @return Boolean + * @param {Array} acceptedFieldStates + * + * @return {bool} + * + * @see Drupal.quickedit.EntityModel#validate */ _fieldsHaveAcceptableStates: function (acceptedFieldStates) { var accept = true; @@ -504,7 +564,7 @@ }, /** - * {@inheritdoc} + * @param {object} options */ destroy: function (options) { Drupal.quickedit.BaseModel.prototype.destroy.call(this, options); @@ -518,18 +578,20 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ sync: function () { // We don't use REST updates to sync. return; } - }, { + }, /** @lends Drupal.quickedit.EntityModel */{ /** * A list (sequence) of all possible states an entity can be in during * in-place editing. + * + * @enum */ states: [ // Initial state, like field's 'inactive' OR the user has just finished @@ -587,11 +649,12 @@ /** * Indicates whether the 'from' state comes before the 'to' state. * - * @param String from + * @param {string} from * One of Drupal.quickedit.EntityModel.states. - * @param String to + * @param {string} to * One of Drupal.quickedit.EntityModel.states. - * @return Boolean + * + * @return {bool} */ followsStateSequence: function (from, to) { return _.indexOf(this.states, from) < _.indexOf(this.states, to); @@ -599,7 +662,16 @@ }); - Drupal.quickedit.EntityCollection = Backbone.Collection.extend({ + /** + * @constructor + * + * @augments Backbone.Collection + */ + Drupal.quickedit.EntityCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.EntityCollection# */{ + + /** + * @type {Drupal.quickedit.EntityModel} + */ model: Drupal.quickedit.EntityModel }); diff --git a/core/modules/quickedit/js/models/FieldModel.js b/core/modules/quickedit/js/models/FieldModel.js index 6bd4e44..43fadab 100644 --- a/core/modules/quickedit/js/models/FieldModel.js +++ b/core/modules/quickedit/js/models/FieldModel.js @@ -7,62 +7,112 @@ "use strict"; - /** - * State of an in-place editable field in the DOM. - */ - Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend({ + Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.FieldModel# */{ + + /** + * @type {object} + */ + defaults: /** @lends Drupal.quickedit.FieldModel# */{ - defaults: { - // The DOM element that represents this field. It may seem bizarre to have - // a DOM element in a Backbone Model, but we need to be able to map fields - // in the DOM to FieldModels in memory. + /** + * The DOM element that represents this field. It may seem bizarre to have + * a DOM element in a Backbone Model, but we need to be able to map fields + * in the DOM to FieldModels in memory. + */ el: null, - // A field ID, of the form - // "////", e.g. - // "node/1/field_tags/und/full". + + /** + * A field ID, of the form + * `////` + * + * @example + * "node/1/field_tags/und/full" + */ fieldID: null, - // The unique ID of this field within its entity instance on the page, of - // the form "////[entity instance ID]", - // e.g. "node/1/field_tags/und/full[0]". + + /** + * The unique ID of this field within its entity instance on the page, of + * the form `////[entity instance ID]`. + * + * @example + * "node/1/field_tags/und/full[0]" + */ id: null, - // A Drupal.quickedit.EntityModel. Its "fields" attribute, which is a - // FieldCollection, is automatically updated to include this FieldModel. + + /** + * A Drupal.quickedit.EntityModel. Its "fields" attribute, which is a + * FieldCollection, is automatically updated to include this FieldModel. + */ entity: null, - // This field's metadata as returned by the QuickEditController::metadata(). + + /** + * This field's metadata as returned by the + * QuickEditController::metadata(). + */ metadata: null, - // Callback function for validating changes between states. Receives the - // previous state, new state, context, and a callback + + /** + * Callback function for validating changes between states. Receives the + * previous state, new state, context, and a callback. + */ acceptStateChange: null, - // A logical field ID, of the form - // "///", i.e. the fieldID without - // the view mode, to be able to identify other instances of the same field - // on the page but rendered in a different view mode. e.g. "node/1/field_tags/und". + + /** + * A logical field ID, of the form + * `///`, i.e. the fieldID without + * the view mode, to be able to identify other instances of the same + * field on the page but rendered in a different view mode. + * + * @example + * "node/1/field_tags/und". + */ logicalFieldID: null, // The attributes below are stateful. The ones above will never change // during the life of a FieldModel instance. - // In-place editing state of this field. Defaults to the initial state. - // Possible values: @see Drupal.quickedit.FieldModel.states. + /** + * In-place editing state of this field. Defaults to the initial state. + * Possible values: @see Drupal.quickedit.FieldModel.states. + */ state: 'inactive', - // The field is currently in the 'changed' state or one of the following - // states in which the field is still changed. + + /** + * The field is currently in the 'changed' state or one of the following + * states in which the field is still changed. + */ isChanged: false, - // Is tracked by the EntityModel, is mirrored here solely for decorative - // purposes: so that FieldDecorationView.renderChanged() can react to it. + + /** + * Is tracked by the EntityModel, is mirrored here solely for decorative + * purposes: so that FieldDecorationView.renderChanged() can react to it. + */ inTempStore: false, - // The full HTML representation of this field (with the element that has - // the data-quickedit-field-id as the outer element). Used to propagate - // changes from this field to other instances of the same field storage. + + /** + * The full HTML representation of this field (with the element that has + * the data-quickedit-field-id as the outer element). Used to propagate + * changes from this field to other instances of the same field storage. + */ html: null, - // An object containing the full HTML representations (values) of other view - // modes (keys) of this field, for other instances of this field displayed - // in a different view mode. + + /** + * An object containing the full HTML representations (values) of other + * view modes (keys) of this field, for other instances of this field + * displayed in a different view mode. + */ htmlForOtherViewModes: null }, /** - * {@inheritdoc} + * State of an in-place editable field in the DOM. + * + * @constructs + * + * @augments Drupal.quickedit.BaseModel + * + * @param {object} options */ initialize: function (options) { // Store the original full HTML representation of this field. @@ -79,7 +129,8 @@ }, /** - * {@inheritdoc} + * + * @param {object} options */ destroy: function (options) { if (this.get('state') !== 'inactive') { @@ -89,7 +140,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ sync: function () { // We don't use REST updates to sync. @@ -97,7 +148,22 @@ }, /** - * {@inheritdoc} + * + * @param {object} attrs + * The attributes changes in the save or set call. + * @param {object} options + * An object with the following option: + * @param {string} [options.reason] + * A string that conveys a particular reason to allow for an exceptional + * state change. + * @param {Array} options.accept-field-states + * An array of strings that represent field states that the entities must + * be in to validate. For example, if `accept-field-states` is + * `['candidate', 'highlighted']`, then all the fields of the entity must + * be in either of these two states for the save or set call to + * validate and proceed. + * + * @return {string} */ validate: function (attrs, options) { var current = this.get('state'); @@ -117,7 +183,7 @@ /** * Extracts the entity ID from this field's ID. * - * @return String + * @return {string} * An entity ID: a string of the format `/`. */ getEntityID: function () { @@ -127,7 +193,7 @@ /** * Extracts the view mode ID from this field's ID. * - * @return String + * @return {string} * A view mode ID. */ getViewMode: function () { @@ -137,16 +203,16 @@ /** * Find other instances of this field with different view modes. * - * @return Array + * @return {Array} * An array containing view mode IDs. */ findOtherViewModes: function () { var currentField = this; var otherViewModes = []; Drupal.quickedit.collections.fields - // Find all instances of fields that display the same logical field (same - // entity, same field, just a different instance and maybe a different - // view mode). + // Find all instances of fields that display the same logical field + // (same entity, same field, just a different instance and maybe a + // different view mode). .where({logicalFieldID: currentField.get('logicalFieldID')}) .forEach(function (field) { // Ignore the current field. @@ -164,11 +230,13 @@ return otherViewModes; } - }, { + }, /** @lends Drupal.quickedit.FieldModel */{ /** - * A list (sequence) of all possible states a field can be in during in-place - * editing. + * A list (sequence) of all possible states a field can be in during + * in-place editing. + * + * @enum */ states: [ // The field associated with this FieldModel is linked to an EntityModel; @@ -178,13 +246,14 @@ // This is both the initial (not yet in-place editing) and the end state ( // finished in-place editing). 'inactive', - // The user is in-place editing this entity, and this field is a candidate + // The user is in-place editing this entity, and this field is a + // candidate // for in-place editing. In-place editor should not // - Trigger: user. - // - Guarantees: entity is ready, in-place editor (EditorView) is associated - // with the field. - // - Expected behavior: visual indicators around the field indicate it is - // available for in-place editing, no in-place editor presented yet. + // - Guarantees: entity is ready, in-place editor (EditorView) is + // associated with the field. - Expected behavior: visual indicators + // around the field indicate it is available for in-place editing, no + // in-place editor presented yet. 'candidate', // User is highlighting this field. // - Trigger: user. @@ -192,13 +261,12 @@ // - Expected behavior: visual indicators to convey highlighting, in-place // editing toolbar shows field's label. 'highlighted', - // User has activated the in-place editing of this field; in-place editor is - // activating. - // - Trigger: user. - // - Guarantees: see 'candidate'. - // - Expected behavior: loading indicator, in-place editor is loading remote - // data (e.g. retrieve form from back-end). Upon retrieval of remote data, - // the in-place editor transitions the field's state to 'active'. + // User has activated the in-place editing of this field; in-place editor + // is activating. - Trigger: user. - Guarantees: see 'candidate'. - + // Expected behavior: loading indicator, in-place editor is loading + // remote data (e.g. retrieve form from back-end). Upon retrieval of + // remote data, the in-place editor transitions the field's state to + // 'active'. 'activating', // In-place editor has finished loading remote data; ready for use. // - Trigger: in-place editor. @@ -224,11 +292,12 @@ // - Trigger: in-place editor. // - Guarantees: see 'candidate' and 'active'. // - Expected behavior: transition back to 'candidate' state because the - // deed is done. Then: 1) transition to 'inactive' to allow the field to - // be rerendered, 2) destroy the FieldModel (which also destroys attached - // views like the EditorView), 3) replace the existing field HTML with the - // existing HTML and 4) attach behaviors again so that the field becomes - // available again for in-place editing. + // deed is done. Then: 1) transition to 'inactive' to allow the field + // to + // be rerendered, 2) destroy the FieldModel (which also destroys + // attached views like the EditorView), 3) replace the existing field + // HTML with the existing HTML and 4) attach behaviors again so that the + // field becomes available again for in-place editing. 'saved', // In-place editor has failed to saved the changed field: there were // validation errors. @@ -242,11 +311,12 @@ /** * Indicates whether the 'from' state comes before the 'to' state. * - * @param String from - * One of Drupal.quickedit.FieldModel.states. - * @param String to - * One of Drupal.quickedit.FieldModel.states. - * @return Boolean + * @param {string} from + * One of {@link Drupal.quickedit.FieldModel.states}. + * @param {string} to + * One of {@link Drupal.quickedit.FieldModel.states}. + * + * @return {bool} */ followsStateSequence: function (from, to) { return _.indexOf(this.states, from) < _.indexOf(this.states, to); @@ -254,7 +324,16 @@ }); - Drupal.quickedit.FieldCollection = Backbone.Collection.extend({ + /** + * @constructor + * + * @augments Backbone.Collection + */ + Drupal.quickedit.FieldCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.FieldCollection */{ + + /** + * @type {Drupal.quickedit.FieldModel} + */ model: Drupal.quickedit.FieldModel }); diff --git a/core/modules/quickedit/js/quickedit.js b/core/modules/quickedit/js/quickedit.js index 7dcb6cb..539fa56 100644 --- a/core/modules/quickedit/js/quickedit.js +++ b/core/modules/quickedit/js/quickedit.js @@ -5,7 +5,7 @@ * Everything happens asynchronously, to allow for: * - dynamically rendered contextual links * - asynchronously retrieved (and cached) per-field in-place editing metadata - * - asynchronous setup of in-place editable field and "Quick edit" link + * - asynchronous setup of in-place editable field and "Quick edit" link. * * To achieve this, there are several queues: * - fieldsMetadataQueue: fields whose metadata still needs to be fetched. @@ -63,6 +63,10 @@ */ var entityInstancesTracker = {}; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.quickedit = { attach: function (context) { // Initialize the Quick Edit app once per page load. @@ -114,10 +118,21 @@ } }; + /** + * + * @namespace + */ Drupal.quickedit = { - // A Drupal.quickedit.AppView instance. + + /** + * A {@link Drupal.quickedit.AppView} instance. + */ app: null, + /** + * @prop {Array.} entities + * @prop {Array.} fields + */ collections: { // All in-place editable entities (Drupal.quickedit.EntityModel) on the // page. @@ -126,29 +141,77 @@ fields: null }, - // In-place editors will register themselves in this object. + /** + * In-place editors will register themselves in this object. + * + * @namespace + */ editors: {}, - // Per-field metadata that indicates whether in-place editing is allowed, - // which in-place editor should be used, etc. + /** + * Per-field metadata that indicates whether in-place editing is allowed, + * which in-place editor should be used, etc. + * + * @namespace + */ metadata: { + /** + * + * @param {string} fieldID + * + * @return {bool} + */ has: function (fieldID) { return storage.getItem(this._prefixFieldID(fieldID)) !== null; }, + + /** + * + * @param {string} fieldID + * @param {object} metadata + */ add: function (fieldID, metadata) { storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata)); }, + + /** + * + * @param {string} fieldID + * @param {string} [key] + * @return {object|*} + */ get: function (fieldID, key) { var metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID))); return (typeof key === 'undefined') ? metadata : metadata[key]; }, + + /** + * + * @param {string} fieldID + * + * @return {string} + */ _prefixFieldID: function (fieldID) { return 'Drupal.quickedit.metadata.' + fieldID; }, + + /** + * + * @param {string} fieldID + * + * @return {string} + */ _unprefixFieldID: function (fieldID) { // Strip "Drupal.quickedit.metadata.", which is 26 characters long. return fieldID.substring(26); }, + + /** + * + * @param {string} fieldIDs + * + * @return {Array} + */ intersection: function (fieldIDs) { var prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID); var intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage)); @@ -176,6 +239,11 @@ /** * Detect contextual links on entities annotated by Quick Edit; queue these to * be processed. + * + * @param {jQuery.Event} event + * @param {object} data + * + * @listens event:drupalContextualLinkAdded */ $(document).on('drupalContextualLinkAdded', function (event, data) { if (data.$region.is('[data-quickedit-entity-id]')) { @@ -201,10 +269,11 @@ /** * Extracts the entity ID from a field ID. * - * @param String fieldID + * @param {string} fieldID * A field ID: a string of the format * `////`. - * @return String + * + * @return {string} * An entity ID: a string of the format `/`. */ function extractEntityID(fieldID) { @@ -214,7 +283,7 @@ /** * Initialize the Quick Edit app. * - * @param DOM bodyElement + * @param {HTMLElement} bodyElement * This document's body element. */ function initQuickEdit(bodyElement) { @@ -234,7 +303,7 @@ /** * Assigns the entity an instance ID. * - * @param DOM entityElement. + * @param {HTMLElement} entityElement * A Drupal Entity API entity's DOM element with a data-quickedit-entity-id * attribute. */ @@ -255,7 +324,7 @@ /** * Fetch the field's metadata; queue or initialize it (if EntityModel exists). * - * @param DOM fieldElement + * @param {HTMLElement} fieldElement * A Drupal Field API field's DOM element with a data-quickedit-field-id * attribute. */ @@ -308,13 +377,13 @@ /** * Initialize a field; create FieldModel. * - * @param DOM fieldElement + * @param {HTMLElement} fieldElement * The field's DOM element. - * @param String fieldID + * @param {string} fieldID * The field's ID. - * @param String entityID + * @param {string} entityID * The field's entity's ID. - * @param String entityInstanceID + * @param {string} entityInstanceID * The field's entity's instance ID. */ function initializeField(fieldElement, fieldID, entityID, entityInstanceID) { @@ -344,7 +413,7 @@ * * Fields whose metadata is missing are tracked at fieldsMetadataQueue. * - * @param Function callback + * @param {function} callback * A callback function that receives field elements whose metadata will just * have been fetched. */ @@ -381,9 +450,9 @@ * Loads missing in-place editor's attachments (JavaScript and CSS files). * * Missing in-place editors are those whose fields are actively being used on - * the page but don't have + * the page but don't have. * - * @param Function callback + * @param {function} callback * Callback function to be called when the missing in-place editors (if any) * have been inserted into the DOM. i.e. they may still be loading. */ @@ -429,7 +498,7 @@ /** * Attempts to set up a "Quick edit" link and corresponding EntityModel. * - * @param Object contextualLink + * @param {object} contextualLink * An object with the following properties: * - String entityID: a Quick Edit entity identifier, e.g. "node/1" or * "block_content/5". @@ -438,13 +507,13 @@ * instance of this entity). * - DOM el: element pointing to the contextual links placeholder for this * entity. - * - DOM region: element pointing to the contextual region for this entity. - * @return Boolean + * - DOM region: element pointing to the contextual region of this entity. + * + * @return {bool} * Returns true when a contextual the given contextual link metadata can be - * removed from the queue (either because the contextual link has been set up - * or because it is certain that in-place editing is not allowed for any of - * its fields). - * Returns false otherwise. + * removed from the queue (either because the contextual link has been set + * up or because it is certain that in-place editing is not allowed for any + * of its fields). Returns false otherwise. */ function initializeEntityContextualLink(contextualLink) { var metadata = Drupal.quickedit.metadata; @@ -540,7 +609,7 @@ * * Note: this will not delete an entity that is actively being in-place edited. * - * @param jQuery $context + * @param {jQuery} $context * The context within which to delete. */ function deleteContainedModelsAndQueues($context) { diff --git a/core/modules/quickedit/js/theme.js b/core/modules/quickedit/js/theme.js index a6d1d91..94af0d9 100644 --- a/core/modules/quickedit/js/theme.js +++ b/core/modules/quickedit/js/theme.js @@ -10,10 +10,11 @@ /** * Theme function for a "backstage" for the Quick Edit module. * - * @param Object settings - * An object with the following keys: - * - String id: the id to apply to the backstage. - * @return String + * @param {object} settings + * @param {string} settings.id + * the id to apply to the backstage. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditBackstage = function (settings) { @@ -25,10 +26,11 @@ /** * Theme function for a toolbar container of the Quick Edit module. * - * @param Object settings - * An object with the following keys: - * - String id: the id to apply to the toolbar container. - * @return String + * @param {object} settings + * @param {string} settings.id + * the id to apply to the backstage. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditEntityToolbar = function (settings) { @@ -47,11 +49,13 @@ /** * Theme function for a toolbar container of the Quick Edit module. * - * @param Object settings - * An object with the following keys: - * - String entityLabel: The title of the active entity. - * - String fieldLabel: The label of the highlighted or active field. - * @return String + * @param {object} settings + * @param {string} settings.entityLabel + * The title of the active entity. + * @param {string} settings.fieldLabel + * The label of the highlighted or active field. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditEntityToolbarLabel = function (settings) { @@ -61,7 +65,7 @@ /** * Element that defines a containing box of the placement of the entity toolbar. * - * @return String + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditEntityToolbarFence = function () { @@ -71,10 +75,11 @@ /** * Theme function for a toolbar container of the Quick Edit module. * - * @param settings - * An object with the following keys: - * - id: the id to apply to the toolbar container. - * @return + * @param {object} settings + * @param {string} settings.id + * The id to apply to the toolbar container. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditFieldToolbar = function (settings) { @@ -84,12 +89,15 @@ /** * Theme function for a toolbar toolgroup of the Quick Edit module. * - * @param Object settings - * An object with the following keys: - * - String id: (optional) the id of the toolgroup - * - String classes: the class of the toolgroup. - * - Array buttons: @see Drupal.theme.quickeditButtons(). - * @return String + * @param {object} settings + * @param {string} [settings.id] + * The id of the toolgroup. + * @param {string} settings.classes + * The class of the toolgroup. + * @param {Array} settings.buttons + * See {@link Drupal.theme.quickeditButtons}. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditToolgroup = function (settings) { @@ -112,13 +120,13 @@ * * Can be used for the buttons both in the toolbar toolgroups and in the modal. * - * @param Object settings - * An object with the following keys: - * - buttons: an array of objects with the following keys: - * - String type: the type of the button (defaults to 'button') - * - Array classes: the classes of the button. - * - String label: the label of the button. - * @return String + * @param {object} settings + * @param {Array} settings.buttons + * - String type: the type of the button (defaults to 'button') + * - Array classes: the classes of the button. + * - String label: the label of the button. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditButtons = function (settings) { @@ -146,11 +154,13 @@ /** * Theme function for a form container of the Quick Edit module. * - * @param Object settings - * An object with the following keys: - * - String id: the id to apply to the toolbar container. - * - String loadingMsg: The message to show while loading. - * @return String + * @param {object} settings + * @param {string} settings.id + * The id to apply to the toolbar container. + * @param {string} settings.loadingMsg + * The message to show while loading. + * + * @return {string} * The corresponding HTML. */ Drupal.theme.quickeditFormContainer = function (settings) { diff --git a/core/modules/quickedit/js/util.js b/core/modules/quickedit/js/util.js index e8a9643..a8bda3c 100644 --- a/core/modules/quickedit/js/util.js +++ b/core/modules/quickedit/js/util.js @@ -7,19 +7,37 @@ "use strict"; + /** + * @namespace + */ Drupal.quickedit.util = Drupal.quickedit.util || {}; + /** + * @namespace + */ Drupal.quickedit.util.constants = {}; + + /** + * + * @type {string} + */ Drupal.quickedit.util.constants.transitionEnd = "transitionEnd.quickedit webkitTransitionEnd.quickedit transitionend.quickedit msTransitionEnd.quickedit oTransitionEnd.quickedit"; /** * Converts a field id into a formatted url path. * - * @param String id - * The id of an editable field. For example, 'node/1/body/und/full'. - * @param String urlFormat - * The Controller route for field processing. For example, - * '/quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode'. + * @example + * Drupal.quickedit.util.buildUrl( + * 'node/1/body/und/full', + * '/quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode' + * ); + * + * @param {string} id + * The id of an editable field. + * @param {string} urlFormat + * The Controller route for field processing. + * + * @return {string} */ Drupal.quickedit.util.buildUrl = function (id, urlFormat) { var parts = id.split('/'); @@ -35,9 +53,9 @@ /** * Shows a network error modal dialog. * - * @param String title + * @param {string} title * The title to use in the modal dialog. - * @param String message + * @param {string} message * The message to use in the modal dialog. */ Drupal.quickedit.util.networkErrorModal = function (title, message) { @@ -65,6 +83,9 @@ networkErrorModal.showModal(); }; + /** + * @namespace + */ Drupal.quickedit.util.form = { /** @@ -73,20 +94,23 @@ * Leverages Drupal.ajax' ability to have scoped (per-instance) command * implementations to be able to call a callback. * - * @param Object options + * @param {object} options * An object with the following keys: - * - jQuery $el: (required) DOM element necessary for Drupal.ajax to - * perform AJAX commands. - * - String fieldID: (required) the field ID that uniquely identifies the - * field for which this form will be loaded. - * - Boolean nocssjs: (required) boolean indicating whether no CSS and JS - * should be returned (necessary when the form is invisible to the user). - * - Boolean reset: (required) boolean indicating whether the data stored - * for this field's entity in PrivateTempStore should be used or reset. - * @param Function callback - * A callback function that will receive the form to be inserted, as well as - * the ajax object, necessary if the callback wants to perform other AJAX - * commands. + * @param {jQuery} options.$el + * DOM element necessary for Drupal.ajax to perform AJAX commands. + * @param {string} options.fieldID + * The field ID that uniquely identifies the field for which this form + * will be loaded. + * @param {bool} options.nocssjs + * Boolean indicating whether no CSS and JS should be returned (necessary + * when the form is invisible to the user). + * @param {bool} options.reset + * Boolean indicating whether the data stored for this field's entity in + * PrivateTempStore should be used or reset. + * @param {function} callback + * A callback function that will receive the form to be inserted, as well + * as the ajax object, necessary if the callback wants to perform other + * Ajax commands. */ load: function (options, callback) { var fieldID = options.fieldID; @@ -115,20 +139,24 @@ callback(response.data, ajax); Drupal.ajax.instances[this.instanceIndex] = null; }; - // This will ensure our scoped quickeditFieldForm AJAX command gets called. + // This will ensure our scoped quickeditFieldForm AJAX command gets + // called. formLoaderAjax.execute(); }, /** * Creates a Drupal.ajax instance that is used to save a form. * - * @param Object options - * An object with the following keys: - * - nocssjs: (required) boolean indicating whether no CSS and JS should be - * returned (necessary when the form is invisible to the user). - * - other_view_modes: (required) array containing view mode IDs (of other - * instances of this field on the page). - * @return Drupal.ajax + * @param {object} options + * @param {bool} options.nocssjs + * Boolean indicating whether no CSS and JS should be returned (necessary + * when the form is invisible to the user). + * @param {Array.} options.other_view_modes + * Array containing view mode IDs (of other instances of this field on the + * page). + * @param {jQuery} $submit + * + * @return {Drupal.Ajax} * A Drupal.ajax instance. */ ajaxifySaving: function (options, $submit) { @@ -144,6 +172,11 @@ }, // Reimplement the success handler to ensure Drupal.attachBehaviors() does // not get called on the form. + + /** + * @param {Drupal.AjaxCommands~commandDefinition} response + * @param {number} [status] + */ success: function (response, status) { for (var i in response) { if (response.hasOwnProperty(i) && response[i].command && this.commands[response[i].command]) { @@ -161,9 +194,9 @@ /** * Cleans up the Drupal.ajax instance that is used to save the form. * - * @param Drupal.ajax ajax - * A Drupal.ajax instance that was returned by - * Drupal.quickedit.form.ajaxifySaving(). + * @param {Drupal.Ajax} ajax + * A Drupal.Ajax instance that was returned by + * {@link Drupal.quickedit.form.ajaxifySaving}. */ unajaxifySaving: function (ajax) { $(ajax.element).off('click.quickedit'); diff --git a/core/modules/quickedit/js/views/AppView.js b/core/modules/quickedit/js/views/AppView.js index 767aa16..514e301 100644 --- a/core/modules/quickedit/js/views/AppView.js +++ b/core/modules/quickedit/js/views/AppView.js @@ -18,16 +18,21 @@ // the original HTML. var reload = false; - Drupal.quickedit.AppView = Backbone.View.extend({ + Drupal.quickedit.AppView = Backbone.View.extend(/** @lends Drupal.quickedit.AppView# */{ /** - * {@inheritdoc} + * @constructs * - * @param Object options + * @augments Backbone.View + * + * @param {object} options * An object with the following keys: - * - Drupal.quickedit.AppModel model: the application state model - * - Drupal.quickedit.EntityCollection entitiesCollection: all on-page entities - * - Drupal.quickedit.FieldCollection fieldsCollection: all on-page fields + * @param {Drupal.quickedit.AppModel} options.model + * The application state model. + * @param {Drupal.quickedit.EntityCollection} options.entitiesCollection + * All on-page entities. + * @param {Drupal.quickedit.FieldCollection} options.fieldsCollection + * All on-page fields */ initialize: function (options) { // AppView's configuration for handling states. @@ -57,10 +62,11 @@ /** * Handles setup/teardown and state changes when the active entity changes. * - * @param Drupal.quickedit.EntityModel entityModel + * @param {Drupal.quickedit.EntityModel} entityModel * An instance of the EntityModel class. - * @param String state - * The state of the associated field. One of Drupal.quickedit.EntityModel.states. + * @param {string} state + * The state of the associated field. One of + * {@link Drupal.quickedit.EntityModel.states}. */ appStateChange: function (entityModel, state) { var app = this; @@ -86,6 +92,7 @@ entityModel.set('state', 'opening'); }); break; + case 'closed': entityToolbarView = entityModel.toolbarView; // First, tear down the in-place editors. @@ -112,14 +119,16 @@ * * This is what ensures that the app is in control of what happens. * - * @param String from + * @param {string} from * The previous state. - * @param String to + * @param {string} to * The new state. - * @param null|Object context + * @param {null|object} context * The context that is trying to trigger the state change. - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * The fieldModel to which this change applies. + * + * @return {bool} */ acceptEditorStateChange: function (from, to, context, fieldModel) { var accept = true; @@ -244,7 +253,7 @@ * * Must happen before the fieldModel's state is changed to 'candidate'. * - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * The field for which an in-place editor must be set up. */ setupEditor: function (fieldModel) { @@ -293,7 +302,7 @@ * * Must happen after the fieldModel's state is changed to 'inactive'. * - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * The field for which an in-place editor must be torn down. */ teardownEditor: function (fieldModel) { @@ -320,7 +329,9 @@ /** * Asks the user to confirm whether he wants to stop editing via a modal. * - * @see acceptEditorStateChange() + * @param {Drupal.quickedit.EntityModel} entityModel + * + * @see Drupal.quickedit.AppView#acceptEditorStateChange */ confirmEntityDeactivation: function (entityModel) { var that = this; @@ -391,9 +402,10 @@ /** * Reacts to field state changes; tracks global state. * - * @param Drupal.quickedit.FieldModel fieldModel - * @param String state - * The state of the associated field. One of Drupal.quickedit.FieldModel.states. + * @param {Drupal.quickedit.FieldModel} fieldModel + * @param {string} state + * The state of the associated field. One of + * {@link Drupal.quickedit.FieldModel.states}. */ editorStateChange: function (fieldModel, state) { var from = fieldModel.previous('state'); @@ -423,15 +435,15 @@ /** * Render an updated field (a field whose 'html' attribute changed). * - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * The FieldModel whose 'html' attribute changed. - * @param String html + * @param {string} html * The updated 'html' attribute. - * @param Object options + * @param {object} options * An object with the following keys: - * - Boolean propagation: whether this change to the 'html' attribute - * occurred because of the propagation of changes to another instance of - * this field. + * @param {bool} options.propagation + * Whether this change to the 'html' attribute occurred because of the + * propagation of changes to another instance of this field. */ renderUpdatedField: function (fieldModel, html, options) { // Get data necessary to rerender property before it is unavailable. @@ -485,17 +497,17 @@ /** * Propagates the changes to an updated field to all instances of that field. * - * @param Drupal.quickedit.FieldModel updatedField + * @param {Drupal.quickedit.FieldModel} updatedField * The FieldModel whose 'html' attribute changed. - * @param String html + * @param {string} html * The updated 'html' attribute. - * @param Object options + * @param {object} options * An object with the following keys: - * - Boolean propagation: whether this change to the 'html' attribute - * occurred because of the propagation of changes to another instance of - * this field. + * @param {bool} options.propagation + * Whether this change to the 'html' attribute occurred because of the + * propagation of changes to another instance of this field. * - * @see Drupal.quickedit.AppView.renderUpdatedField() + * @see Drupal.quickedit.AppView#renderUpdatedField */ propagateUpdatedField: function (updatedField, html, options) { // Don't propagate field updates that themselves were caused by propagation. @@ -538,7 +550,7 @@ * * This happens when a field was modified, saved and hence rerendered. * - * @param Drupal.quickedit.FieldModel fieldModel + * @param {Drupal.quickedit.FieldModel} fieldModel * A field that was just added to the collection of fields. */ rerenderedFieldToCandidate: function (fieldModel) { @@ -560,7 +572,7 @@ * EntityModel Collection change handler, called on change:isActive, enforces * a single active entity. * - * @param Drupal.quickedit.EntityModel + * @param {Drupal.quickedit.EntityModel} changedEntityModel * The entityModel instance whose active state has changed. */ enforceSingleActiveEntity: function (changedEntityModel) { diff --git a/core/modules/quickedit/js/views/ContextualLinkView.js b/core/modules/quickedit/js/views/ContextualLinkView.js index 6c50617..75036b1 100644 --- a/core/modules/quickedit/js/views/ContextualLinkView.js +++ b/core/modules/quickedit/js/views/ContextualLinkView.js @@ -7,8 +7,13 @@ "use strict"; - Drupal.quickedit.ContextualLinkView = Backbone.View.extend({ + Drupal.quickedit.ContextualLinkView = Backbone.View.extend(/** @lends Drupal.quickedit.ContextualLinkView# */{ + /** + * Define all events to listen to. + * + * @return {object} + */ events: function () { // Prevents delay and simulated mouse events. function touchEndToClick(event) { @@ -26,13 +31,18 @@ }, /** - * {@inheritdoc} + * @constructs * - * @param Object options + * @augments Backbone.View + * + * @param {object} options * An object with the following keys: - * - Drupal.quickedit.EntityModel model: the associated entity's model - * - Drupal.quickedit.AppModel appModel: the application state model - * - strings: the strings for the "Quick edit" link + * @param {Drupal.quickedit.EntityModel} options.model + * The associated entity's model. + * @param {Drupal.quickedit.AppModel} options.appModel + * The application state model. + * @param {object} options.strings + * The strings for the "Quick edit" link. */ initialize: function (options) { // Insert the text of the quick edit toggle. @@ -44,7 +54,11 @@ }, /** - * {@inheritdoc} + * + * @param {Drupal.quickedit.EntityModel} entityModel + * @param {bool} isActive + * + * @return {Drupal.quickedit.ContextualLinkView} */ render: function (entityModel, isActive) { this.$el.find('a').attr('aria-pressed', isActive); diff --git a/core/modules/quickedit/js/views/EditorView.js b/core/modules/quickedit/js/views/EditorView.js index e13b0a1..f78a697 100644 --- a/core/modules/quickedit/js/views/EditorView.js +++ b/core/modules/quickedit/js/views/EditorView.js @@ -7,36 +7,35 @@ "use strict"; - /** - * A base implementation that outlines the structure for in-place editors. - * - * Specific in-place editor implementations should subclass (extend) this View - * and override whichever method they deem necessary to override. - * - * Look at Drupal.quickedit.editors.form and - * Drupal.quickedit.editors.plain_text for examples. - * - * @see Drupal.quickedit.EditorModel - */ - Drupal.quickedit.EditorView = Backbone.View.extend({ + Drupal.quickedit.EditorView = Backbone.View.extend(/** @lends Drupal.quickedit.EditorView# */{ /** - * {@inheritdoc} + * A base implementation that outlines the structure for in-place editors. + * + * Specific in-place editor implementations should subclass (extend) this View + * and override whichever method they deem necessary to override. * * Typically you would want to override this method to set the originalValue * attribute in the FieldModel to such a value that your in-place editor can * revert to the original value when necessary. * - * If you override this method, you should call this method (the parent - * class' initialize()) first, like this: - * Drupal.quickedit.EditorView.prototype.initialize.call(this, options); + * @example

      + * Drupal.quickedit.EditorView.prototype.initialize.call(this, options); + * + * @constructs * - * For an example, @see Drupal.quickedit.editors.plain_text. + * @augments Backbone.View * - * @param Object options + * @param {object} options * An object with the following keys: - * - Drupal.quickedit.EditorModel model: the in-place editor state model - * - Drupal.quickedit.FieldModel fieldModel: the field model + * @param {Drupal.quickedit.EditorModel} options.model + * The in-place editor state model. + * @param {Drupal.quickedit.FieldModel} options.fieldModel + * The field model. + * + * @see Drupal.quickedit.EditorModel + * @see Drupal.quickedit.editors.plain_text */ initialize: function (options) { this.fieldModel = options.fieldModel; @@ -44,7 +43,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ remove: function () { // The el property is the field, which should not be removed. Remove the @@ -63,16 +62,18 @@ * e.g. using a WYSIWYG editor on a body field should happen on the DOM * element containing the text itself, not on the field wrapper. * - * For example, @see Drupal.quickedit.editors.plain_text. - * - * @return jQuery + * @return {jQuery} * A jQuery-wrapped DOM element. + * + * @see Drupal.quickedit.editors.plain_text */ getEditedElement: function () { return this.$el; }, /** + * + * @return {object} * Returns 3 Quick Edit UI settings that depend on the in-place editor: * - Boolean padding: indicates whether padding should be applied to the * edited element, to guarantee legibility of text. @@ -90,9 +91,10 @@ /** * Determines the actions to take given a change of state. * - * @param Drupal.quickedit.FieldModel fieldModel - * @param String state - * The state of the associated field. One of Drupal.quickedit.FieldModel.states. + * @param {Drupal.quickedit.FieldModel} fieldModel + * @param {string} state + * The state of the associated field. One of + * {@link Drupal.quickedit.FieldModel.states}. */ stateChange: function (fieldModel, state) { var from = fieldModel.previous('state'); @@ -102,19 +104,21 @@ // An in-place editor view will not yet exist in this state, hence // this will never be reached. Listed for sake of completeness. break; + case 'candidate': // Nothing to do for the typical in-place editor: it should not be // visible yet. - // Except when we come from the 'invalid' state, then we clean up. if (from === 'invalid') { this.removeValidationErrors(); } break; + case 'highlighted': // Nothing to do for the typical in-place editor: it should not be // visible yet. break; + case 'activating': // The user has indicated he wants to do in-place editing: if // something needs to be loaded (CSS/JavaScript/server data/…), then @@ -130,13 +134,16 @@ fieldModel.set('state', 'active'); }); break; + case 'active': // The user can now actually use the in-place editor. break; + case 'changed': // Nothing to do for the typical in-place editor. The UI will show an // indicator that the field has changed. break; + case 'saving': // When the user has indicated he wants to save his changes to this // field, this state will be entered. @@ -148,12 +155,14 @@ } this.save(); break; + case 'saved': // Nothing to do for the typical in-place editor. Immediately after // being saved, a field will go to the 'candidate' state, where it // should no longer be visible (after all, the field will then again // just be a *candidate* to be in-place edited). break; + case 'invalid': // The modified field value was attempted to be saved, but there were // validation errors. @@ -167,7 +176,6 @@ */ revert: function () { // A no-op by default; each editor should implement reverting itself. - // Note that if the in-place editor does not cause the FieldModel's // element to be modified, then nothing needs to happen. }, diff --git a/core/modules/quickedit/js/views/EntityDecorationView.js b/core/modules/quickedit/js/views/EntityDecorationView.js index 69a4767..e4607ef 100644 --- a/core/modules/quickedit/js/views/EntityDecorationView.js +++ b/core/modules/quickedit/js/views/EntityDecorationView.js @@ -7,26 +7,28 @@ "use strict"; - Drupal.quickedit.EntityDecorationView = Backbone.View.extend({ + Drupal.quickedit.EntityDecorationView = Backbone.View.extend(/** @lends Drupal.quickedit.EntityDecorationView# */{ /** - * {@inheritdoc} - * * Associated with the DOM root node of an editable entity. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change', this.render); }, /** - * {@inheritdoc} + * @inheritdoc */ render: function () { this.$el.toggleClass('quickedit-entity-active', this.model.get('isActive')); }, /** - * {@inheritdoc} + * @inheritdoc */ remove: function () { this.setElement(null); diff --git a/core/modules/quickedit/js/views/EntityToolbarView.js b/core/modules/quickedit/js/views/EntityToolbarView.js index 800311d..fffde11 100644 --- a/core/modules/quickedit/js/views/EntityToolbarView.js +++ b/core/modules/quickedit/js/views/EntityToolbarView.js @@ -7,10 +7,16 @@ "use strict"; - Drupal.quickedit.EntityToolbarView = Backbone.View.extend({ + Drupal.quickedit.EntityToolbarView = Backbone.View.extend(/** @lends Drupal.quickedit.EntityToolbarView# */{ + /** + * @type {?jQuery} + */ _fieldToolbarRoot: null, + /** + * @return {object} + */ events: function () { var map = { 'click button.action-save': 'onClickSave', @@ -21,7 +27,12 @@ }, /** - * {@inheritdoc} + * @constructs + * + * @augments Backbone.View + * + * @param {object} options + * @param {Drupal.quickedit.AppModel} options.appModel */ initialize: function (options) { var that = this; @@ -57,7 +68,9 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.quickedit.EntityToolbarView} */ render: function () { if (this.model.get('isActive')) { @@ -98,6 +111,7 @@ .removeAttr('disabled') .attr('aria-hidden', !isDirty); break; + // The changes to the fields of the entity are being committed. case 'committing': $button @@ -105,6 +119,7 @@ .text(Drupal.t('Saving')) .attr('disabled', 'disabled'); break; + default: $button.attr('aria-hidden', true); break; @@ -114,7 +129,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ remove: function () { // Remove additional DOM elements controlled by this View. @@ -130,7 +145,7 @@ /** * Repositions the entity toolbar on window scroll and resize. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ windowChangeHandler: function (event) { this.position(); @@ -139,15 +154,17 @@ /** * Determines the actions to take given a change of state. * - * @param Drupal.quickedit.FieldModel model - * @param String state - * The state of the associated field. One of Drupal.quickedit.FieldModel.states. + * @param {Drupal.quickedit.FieldModel} model + * @param {string} state + * The state of the associated field. One of + * {@link Drupal.quickedit.FieldModel.states}. */ fieldStateChange: function (model, state) { switch (state) { case 'active': this.render(); break; + case 'invalid': this.render(); break; @@ -157,8 +174,8 @@ /** * Uses the jQuery.ui.position() method to position the entity toolbar. * - * @param jQuery|DOM element - * (optional) The element against which the entity toolbar is positioned. + * @param {HTMLElement} [element] + * The element against which the entity toolbar is positioned. */ position: function (element) { clearTimeout(this.timer); @@ -169,7 +186,8 @@ var edge = (document.documentElement.dir === 'rtl') ? 'right' : 'left'; // A time unit to wait until the entity toolbar is repositioned. var delay = 0; - // Determines what check in the series of checks below should be evaluated + // Determines what check in the series of checks below should be + // evaluated. var check = 0; // When positioned against an active field that has padding, we should // ignore that padding when positioning the toolbar, to not unnecessarily @@ -186,11 +204,13 @@ // Position against a specific element. of = element; break; + case 1: // Position against a form container. activeField = Drupal.quickedit.app.model.get('activeField'); of = activeField && activeField.editorView && activeField.editorView.$formContainer && activeField.editorView.$formContainer.find('.quickedit-form'); break; + case 2: // Position against an active field. of = activeField && activeField.editorView && activeField.editorView.getEditedElement(); @@ -198,12 +218,14 @@ horizontalPadding = 5; } break; + case 3: // Position against a highlighted field. highlightedField = Drupal.quickedit.app.model.get('highlightedField'); of = highlightedField && highlightedField.editorView && highlightedField.editorView.getEditedElement(); delay = 250; break; + default: var fieldModels = this.model.get('fields').models; var topMostPosition = 1000000; @@ -230,18 +252,21 @@ * Invoked as the 'using' callback of jquery.ui.position() in * positionToolbar(). * - * @param Object suggested + * @param {*} view + * @param {object} suggested * A hash of top and left values for the position that should be set. It * can be forwarded to .css() or .animate(). - * @param Object info + * @param {object} info * The position and dimensions of both the 'my' element and the 'of' * elements, as well as calculations to their relative position. This * object contains the following properties: - * - Object element: A hash that contains information about the HTML - * element that will be positioned. Also known as the 'my' element. - * - Object target: A hash that contains information about the HTML - * element that the 'my' element will be positioned against. Also known - * as the 'of' element. + * @param {object} info.element + * A hash that contains information about the HTML element that will be + * positioned. Also known as the 'my' element. + * @param {object} info.target + * A hash that contains information about the HTML element that the + * 'my' element will be positioned against. Also known as the 'of' + * element. */ function refinePosition(view, suggested, info) { // Determine if the pointer should be on the top or bottom. @@ -317,7 +342,7 @@ /** * Set the model state to 'saving' when the save button is clicked. * - * @param jQuery event + * @param {jQuery.Event} event */ onClickSave: function (event) { event.stopPropagation(); @@ -329,7 +354,7 @@ /** * Sets the model state to candidate when the cancel button is clicked. * - * @param jQuery event + * @param {jQuery.Event} event */ onClickCancel: function (event) { event.preventDefault(); @@ -341,7 +366,7 @@ * * Without this, it may reposition itself, away from the user's cursor! * - * @param jQuery event + * @param {jQuery.Event} event */ onMouseenter: function (event) { clearTimeout(this.timer); @@ -349,6 +374,8 @@ /** * Builds the entity toolbar HTML; attaches to DOM; sets starting position. + * + * @return {jQuery} */ buildToolbarEl: function () { var $toolbar = $(Drupal.theme('quickeditEntityToolbar', { @@ -390,7 +417,7 @@ /** * Returns the DOM element that fields will attach their toolbars to. * - * @return jQuery + * @return {jQuery} * The DOM element that fields will attach their toolbars to. */ getToolbarRoot: function () { @@ -436,9 +463,9 @@ /** * Adds classes to a toolgroup. * - * @param String toolgroup + * @param {string} toolgroup * A toolgroup name. - * @param String classes + * @param {string} classes * A string of space-delimited class names that will be applied to the * wrapping element of the toolbar group. */ @@ -449,9 +476,9 @@ /** * Removes classes from a toolgroup. * - * @param String toolgroup + * @param {string} toolgroup * A toolgroup name. - * @param String classes + * @param {string} classes * A string of space-delimited class names that will be removed from the * wrapping element of the toolbar group. */ @@ -462,9 +489,10 @@ /** * Finds a toolgroup. * - * @param String toolgroup + * @param {string} toolgroup * A toolgroup name. - * @return jQuery + * + * @return {jQuery} * The toolgroup DOM element. */ _find: function (toolgroup) { @@ -474,7 +502,7 @@ /** * Shows a toolgroup. * - * @param String toolgroup + * @param {string} toolgroup * A toolgroup name. */ show: function (toolgroup) { diff --git a/core/modules/quickedit/js/views/FieldDecorationView.js b/core/modules/quickedit/js/views/FieldDecorationView.js index 49491da..689ef9d 100644 --- a/core/modules/quickedit/js/views/FieldDecorationView.js +++ b/core/modules/quickedit/js/views/FieldDecorationView.js @@ -7,10 +7,16 @@ "use strict"; - Drupal.quickedit.FieldDecorationView = Backbone.View.extend({ + Drupal.quickedit.FieldDecorationView = Backbone.View.extend(/** @lends Drupal.quickedit.FieldDecorationView# */{ + /** + * @type {null} + */ _widthAttributeIsEmpty: null, + /** + * @type {object} + */ events: { 'mouseenter.quickedit': 'onMouseEnter', 'mouseleave.quickedit': 'onMouseLeave', @@ -20,11 +26,14 @@ }, /** - * {@inheritdoc} + * @constructs * - * @param Object options + * @augments Backbone.View + * + * @param {object} options * An object with the following keys: - * - Drupal.quickedit.EditorView editorView: the editor object view. + * @param {Drupal.quickedit.EditorView} options.editorView + * The editor object view. */ initialize: function (options) { this.editorView = options.editorView; @@ -34,7 +43,7 @@ }, /** - * {@inheritdoc} + * @inheritdoc */ remove: function () { // The el property is the field, which should not be removed. Remove the @@ -46,8 +55,8 @@ /** * Determines the actions to take given a change of state. * - * @param Drupal.quickedit.FieldModel model - * @param String state + * @param {Drupal.quickedit.FieldModel} model + * @param {string} state * The state of the associated field. One of Drupal.quickedit.FieldModel.states. */ stateChange: function (model, state) { @@ -57,6 +66,7 @@ case 'inactive': this.undecorate(); break; + case 'candidate': this.decorate(); if (from !== 'inactive') { @@ -68,14 +78,17 @@ } this._unpad(); break; + case 'highlighted': this.startHighlight(); break; + case 'activating': // NOTE: this state is not used by every editor! It's only used by those // that need to interact with the server. this.prepareEdit(); break; + case 'active': if (from !== 'activating') { this.prepareEdit(); @@ -84,13 +97,17 @@ this._pad(); } break; + case 'changed': this.model.set('isChanged', true); break; + case 'saving': break; + case 'saved': break; + case 'invalid': break; } @@ -109,7 +126,7 @@ /** * Starts hover; transitions to 'highlight' state. * - * @param jQuery event + * @param {jQuery.Event} event */ onMouseEnter: function (event) { var that = this; @@ -120,7 +137,7 @@ /** * Stops hover; transitions to 'candidate' state. * - * @param jQuery event + * @param {jQuery.Event} event */ onMouseLeave: function (event) { var that = this; @@ -131,7 +148,7 @@ /** * Transition to 'activating' stage. * - * @param jQuery event + * @param {jQuery.Event} event */ onClick: function (event) { this.model.set('state', 'activating'); @@ -212,8 +229,8 @@ var self = this; // Add 5px padding for readability. This means we'll freeze the current - // width and *then* add 5px padding, hence ensuring the padding is added "on - // the outside". + // width and *then* add 5px padding, hence ensuring the padding is added + // "on the outside". // 1) Freeze the width (if it's not already set); don't use animations. if (this.$el[0].style.width === "") { this._widthAttributeIsEmpty = true; @@ -294,7 +311,9 @@ * Convert extraneous values and information into numbers ready for * subtraction. * - * @param DOM $e + * @param {jQuery} $e + * + * @return {object} */ _getPositionProperties: function ($e) { var p; @@ -314,10 +333,12 @@ }, /** - * Replaces blank or 'auto' CSS "position: " values with "0px". + * Replaces blank or 'auto' CSS `position: ` values with "0px". + * + * @param {string} [pos] + * The value for a CSS position declaration. * - * @param String pos - * (optional) The value for a CSS position declaration. + * @return {string} */ _replaceBlankPosition: function (pos) { if (pos === 'auto' || !pos) { diff --git a/core/modules/quickedit/js/views/FieldToolbarView.js b/core/modules/quickedit/js/views/FieldToolbarView.js index 24676ed..b43d7f2 100644 --- a/core/modules/quickedit/js/views/FieldToolbarView.js +++ b/core/modules/quickedit/js/views/FieldToolbarView.js @@ -7,22 +7,44 @@ "use strict"; - Drupal.quickedit.FieldToolbarView = Backbone.View.extend({ + Drupal.quickedit.FieldToolbarView = Backbone.View.extend(/** @lends Drupal.quickedit.FieldToolbarView# */{ - // The edited element, as indicated by EditorView.getEditedElement(). + /** + * The edited element, as indicated by EditorView.getEditedElement. + * + * @type {?jQuery} + */ $editedElement: null, - // A reference to the in-place editor. + /** + * A reference to the in-place editor. + * + * @type {?Drupal.quickedit.EditorView} + */ editorView: null, + /** + * @type {?string} + */ _id: null, /** - * {@inheritdoc} + * @constructs + * + * @augments Backbone.View + * + * @param {object} options + * @param {jQuery} options.$editedElement + * @param {Drupal.quickedit.EditorView} options.editorView */ initialize: function (options) { this.$editedElement = options.$editedElement; this.editorView = options.editorView; + + /** + * + * @type {jQuery} + */ this.$root = this.$el; // Generate a DOM-compatible ID for the form container DOM element. @@ -32,7 +54,9 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.quickedit.FieldToolbarView} */ render: function () { // Render toolbar and set it as the view's element. @@ -49,8 +73,8 @@ /** * Determines the actions to take given a change of state. * - * @param Drupal.quickedit.FieldModel model - * @param String state + * @param {Drupal.quickedit.FieldModel} model + * @param {string} state * The state of the associated field. One of Drupal.quickedit.FieldModel.states. */ stateChange: function (model, state) { @@ -59,6 +83,7 @@ switch (to) { case 'inactive': break; + case 'candidate': // Remove the view's existing element if we went to the 'activating' // state or later, because it will be recreated. Not doing this would @@ -68,8 +93,10 @@ this.setElement(); } break; + case 'highlighted': break; + case 'activating': this.render(); @@ -81,14 +108,19 @@ this.insertWYSIWYGToolGroups(); } break; + case 'active': break; + case 'changed': break; + case 'saving': break; + case 'saved': break; + case 'invalid': break; } @@ -120,7 +152,7 @@ * * Only used to make sane hovering behavior possible. * - * @return String + * @return {string} * A string that can be used as the ID for this toolbar's container. */ getId: function () { @@ -132,7 +164,7 @@ * * Used to provide an abstraction for any WYSIWYG editor to plug in. * - * @return String + * @return {string} * A string that can be used as the ID. */ getFloatedWysiwygToolgroupId: function () { @@ -144,7 +176,7 @@ * * Used to provide an abstraction for any WYSIWYG editor to plug in. * - * @return String + * @return {string} * A string that can be used as the ID. */ getMainWysiwygToolgroupId: function () { @@ -154,9 +186,10 @@ /** * Finds a toolgroup. * - * @param String toolgroup + * @param {string} toolgroup * A toolgroup name. - * @return jQuery + * + * @return {jQuery} */ _find: function (toolgroup) { return this.$el.find('.quickedit-toolgroup.' + toolgroup); @@ -165,7 +198,7 @@ /** * Shows a toolgroup. * - * @param String toolgroup + * @param {string} toolgroup * A toolgroup name. */ show: function (toolgroup) { diff --git a/core/modules/simpletest/simpletest.js b/core/modules/simpletest/simpletest.js index b1c63de..6630cc4 100644 --- a/core/modules/simpletest/simpletest.js +++ b/core/modules/simpletest/simpletest.js @@ -1,9 +1,16 @@ +/** + * @file + * Simpletest behaviors. + */ + (function ($) { "use strict"; /** * Collapses table rows followed by group rows on the test listing page. + * + * @type {Drupal~behavior} */ Drupal.behaviors.simpleTestGroupCollapse = { attach: function (context) { @@ -25,6 +32,8 @@ /** * Toggles test checkboxes to match the group checkbox. + * + * @type {Drupal~behavior} */ Drupal.behaviors.simpleTestSelectAll = { attach: function (context) { @@ -64,6 +73,8 @@ * Text search input: input.table-filter-text * Target table: input.table-filter-text[data-table] * Source text: .table-filter-text-source + * + * @type {Drupal~behavior} */ Drupal.behaviors.simpletestTableFilterByText = { attach: function (context) { diff --git a/core/modules/statistics/statistics.js b/core/modules/statistics/statistics.js index 35bba66..f892dd4 100644 --- a/core/modules/statistics/statistics.js +++ b/core/modules/statistics/statistics.js @@ -1,3 +1,8 @@ +/** + * @file + * Statistics functionality. + */ + (function ($, Drupal, drupalSettings) { "use strict"; diff --git a/core/modules/system/system.js b/core/modules/system/system.js index 31096f9..e052c3f 100644 --- a/core/modules/system/system.js +++ b/core/modules/system/system.js @@ -1,3 +1,8 @@ +/** + * @file + * System behaviors. + */ + (function ($, Drupal, drupalSettings) { "use strict"; @@ -9,6 +14,8 @@ * When a field is filled out, apply its value to other fields that will likely * use the same value. In the installer this is used to populate the * administrator email address with the same value as the site email address. + * + * @type {Drupal~behavior} */ Drupal.behaviors.copyFieldValue = { attach: function (context) { @@ -34,12 +41,13 @@ $('#' + ids.join(', #')).removeOnce('copy-field-values').off('blur'); } }, + /** * Event handler that fill the target element with the specified value. * - * @param e + * @param {jQuery.Event} e * Event object. - * @param value + * @param {string} value * Custom value from jQuery trigger. */ valueTargetCopyHandler: function (e, value) { @@ -48,10 +56,13 @@ $target.val(value); } }, + /** * Handler for a Blur event on a source field. * * This event handler will trigger a 'value:copy' event on all dependent fields. + * + * @param {jQuery.Event} e */ valueSourceBlurHandler: function (e) { var value = $(e.target).val(); diff --git a/core/modules/system/system.modules.js b/core/modules/system/system.modules.js index 45ddf3b..49a9879 100644 --- a/core/modules/system/system.modules.js +++ b/core/modules/system/system.modules.js @@ -1,3 +1,8 @@ +/** + * @file + * Module page behaviors. + */ + (function ($, Drupal) { "use strict"; @@ -11,6 +16,8 @@ * Text search input: input.table-filter-text * Target table: input.table-filter-text[data-table] * Source text: .table-filter-text-source + * + * @type {Drupal~behavior} */ Drupal.behaviors.tableFilterByText = { attach: function (context, settings) { diff --git a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js index 28364a2..39bf0bb 100644 --- a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js +++ b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js @@ -1,2 +1,6 @@ - -/* This file is for testing asset file inclusion, no contents are necessary. */ +/** + * @file + * This file is for testing asset file inclusion, no contents are necessary. + * + * @ignore + */ diff --git a/core/modules/taxonomy/taxonomy.js b/core/modules/taxonomy/taxonomy.js index 8ffe502..fb4c0ce 100644 --- a/core/modules/taxonomy/taxonomy.js +++ b/core/modules/taxonomy/taxonomy.js @@ -1,3 +1,8 @@ +/** + * @file + * Taxonomy behaviors. + */ + (function ($) { "use strict"; @@ -7,12 +12,15 @@ * * This behavior is dependent on the tableDrag behavior, since it uses the * objects initialized in that behavior to update the row. + * + * @type {Drupal~behavior} */ Drupal.behaviors.termDrag = { attach: function (context, settings) { var backStep = settings.taxonomy.backStep; var forwardStep = settings.taxonomy.forwardStep; - var tableDrag = Drupal.tableDrag.taxonomy; // Get the blocks tableDrag object. + // Get the blocks tableDrag object. + var tableDrag = Drupal.tableDrag.taxonomy; var $table = $('#taxonomy'); var rows = $table.find('tr').length; diff --git a/core/modules/text/text.js b/core/modules/text/text.js index 7360e44..82fdf2a 100644 --- a/core/modules/text/text.js +++ b/core/modules/text/text.js @@ -1,9 +1,16 @@ +/** + * @file + * Text behaviors. + */ + (function ($) { "use strict"; /** * Auto-hide summary textarea if empty and show hide and unhide links. + * + * @type {Drupal~behavior} */ Drupal.behaviors.textSummary = { attach: function (context, settings) { diff --git a/core/modules/toolbar/js/escapeAdmin.js b/core/modules/toolbar/js/escapeAdmin.js index 0544a40..f47b8b9 100644 --- a/core/modules/toolbar/js/escapeAdmin.js +++ b/core/modules/toolbar/js/escapeAdmin.js @@ -1,8 +1,8 @@ /** * @file - * * Replaces the home link in toolbar with a back to site link. */ + (function ($, Drupal, drupalSettings) { "use strict"; @@ -24,6 +24,8 @@ * * Back to site link points to the last non-administrative page the user visited * within the same browser tab. + * + * @type {Drupal~behavior} */ Drupal.behaviors.escapeAdmin = { attach: function () { diff --git a/core/modules/toolbar/js/models/MenuModel.js b/core/modules/toolbar/js/models/MenuModel.js index 07bc942..5f302bb 100644 --- a/core/modules/toolbar/js/models/MenuModel.js +++ b/core/modules/toolbar/js/models/MenuModel.js @@ -9,9 +9,23 @@ /** * Backbone Model for collapsible menus. + * + * @constructor + * + * @augments Backbone.Model */ - Drupal.toolbar.MenuModel = Backbone.Model.extend({ - defaults: { + Drupal.toolbar.MenuModel = Backbone.Model.extend(/** @lends Drupal.toolbar.MenuModel# */{ + + /** + * @type {object} + * + * @prop {object} subtrees + */ + defaults: /** @lends Drupal.toolbar.MenuModel# */{ + + /** + * @type {object} + */ subtrees: {} } }); diff --git a/core/modules/toolbar/js/models/ToolbarModel.js b/core/modules/toolbar/js/models/ToolbarModel.js index 77433bf..a3bad56 100644 --- a/core/modules/toolbar/js/models/ToolbarModel.js +++ b/core/modules/toolbar/js/models/ToolbarModel.js @@ -9,43 +9,123 @@ /** * Backbone model for the toolbar. + * + * @constructor + * + * @augments Backbone.Model */ - Drupal.toolbar.ToolbarModel = Backbone.Model.extend({ - defaults: { - // The active toolbar tab. All other tabs should be inactive under - // normal circumstances. It will remain active across page loads. The - // active item is stored as an ID selector e.g. '#toolbar-item--1'. + Drupal.toolbar.ToolbarModel = Backbone.Model.extend(/** @lends Drupal.toolbar.ToolbarModel# */{ + + /** + * @type {object} + * + * @prop activeTab + * @prop activeTray + * @prop isOriented + * @prop isFixed + * @prop areSubtreesLoaded + * @prop isViewportOverflowConstrained + * @prop orientation + * @prop locked + * @prop isTrayToggleVisible + * @prop height + * @prop offsets + */ + defaults: /** @lends Drupal.toolbar.ToolbarModel# */{ + + /** + * The active toolbar tab. All other tabs should be inactive under + * normal circumstances. It will remain active across page loads. The + * active item is stored as an ID selector e.g. '#toolbar-item--1'. + * + * @type {?string} + */ activeTab: null, - // Represents whether a tray is open or not. Stored as an ID selector e.g. - // '#toolbar-item--1-tray'. + + /** + * Represents whether a tray is open or not. Stored as an ID selector e.g. + * '#toolbar-item--1-tray'. + * + * @type {?string} + */ activeTray: null, - // Indicates whether the toolbar is displayed in an oriented fashion, - // either horizontal or vertical. + + /** + * Indicates whether the toolbar is displayed in an oriented fashion, + * either horizontal or vertical. + * + * @type {bool} + */ isOriented: false, - // Indicates whether the toolbar is positioned absolute (false) or fixed - // (true). + + /** + * Indicates whether the toolbar is positioned absolute (false) or fixed + * (true). + * + * @type {bool} + */ isFixed: false, - // Menu subtrees are loaded through an AJAX request only when the Toolbar - // is set to a vertical orientation. + + /** + * Menu subtrees are loaded through an AJAX request only when the Toolbar + * is set to a vertical orientation. + * + * @type {bool} + */ areSubtreesLoaded: false, - // If the viewport overflow becomes constrained, isFixed must be true so - // that elements in the trays aren't lost off-screen and impossible to - // get to. + + /** + * If the viewport overflow becomes constrained, isFixed must be true so + * that elements in the trays aren't lost off-screen and impossible to + * get to. + * + * @type {bool} + */ isViewportOverflowConstrained: false, - // The orientation of the active tray. + + /** + * The orientation of the active tray. + * + * @type {string} + */ orientation: 'vertical', - // A tray is locked if a user toggled it to vertical. Otherwise a tray - // will switch between vertical and horizontal orientation based on the - // configured breakpoints. The locked state will be maintained across page - // loads. + + /** + * A tray is locked if a user toggled it to vertical. Otherwise a tray + * will switch between vertical and horizontal orientation based on the + * configured breakpoints. The locked state will be maintained across page + * loads. + * + * @type {bool} + */ locked: false, - // Indicates whether the tray orientation toggle is visible. + + /** + * Indicates whether the tray orientation toggle is visible. + * + * @type {bool} + */ isTrayToggleVisible: false, - // The height of the toolbar. + + /** + * The height of the toolbar. + * + * @type {?number} + */ height: null, - // The current viewport offsets determined by Drupal.displace(). The - // offsets suggest how a module might position is components relative to - // the viewport. + + /** + * The current viewport offsets determined by Drupal.displace(). The + * offsets suggest how a module might position is components relative to + * the viewport. + * + * @type {object} + * + * @prop {number} top + * @prop {number} right + * @prop {number} bottom + * @prop {number} left + */ offsets: { top: 0, right: 0, @@ -55,7 +135,12 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @param {object} attributes + * @param {object} options + * + * @return {string} */ validate: function (attributes, options) { // Prevent the orientation being set to horizontal if it is locked, unless diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js index efbad03..cd44b7f 100644 --- a/core/modules/toolbar/js/toolbar.js +++ b/core/modules/toolbar/js/toolbar.js @@ -1,8 +1,8 @@ /** - * @file toolbar.js - * + * @file * Defines the behavior of the Drupal administration toolbar. */ + (function ($, Drupal, drupalSettings) { "use strict"; @@ -33,9 +33,10 @@ * point directly to a resource or toggle the visibility of a tray. * * Modules register tabs with hook_toolbar(). + * + * @type {Drupal~behavior} */ Drupal.behaviors.toolbar = { - attach: function (context) { // Verify that the user agent understands media queries. Complex admin // toolbar layouts require media query support. @@ -138,16 +139,30 @@ /** * Toolbar methods of Backbone objects. + * + * @namespace */ Drupal.toolbar = { - // A hash of View instances. + /** + * A hash of View instances. + * + * @type {object.} + */ views: {}, - // A hash of Model instances. + /** + * A hash of Model instances. + * + * @type {object.} + */ models: {}, - // A hash of MediaQueryList objects tracked by the toolbar. + /** + * A hash of MediaQueryList objects tracked by the toolbar. + * + * @type {object.} + */ mql: {}, /** @@ -155,13 +170,18 @@ * * A deferred object that is resolved by an inlined JavaScript callback. * - * JSONP callback. + * @type {jQuery.Deferred} + * * @see toolbar_subtrees_jsonp(). */ setSubtrees: new $.Deferred(), /** * Respond to configured narrow media query changes. + * + * @param {Drupal.toolbar.ToolbarModel} model + * @param {string} label + * @param {object} mql */ mediaQueryChangeHandler: function (model, label, mql) { switch (label) { @@ -177,11 +197,13 @@ model.set({'orientation': 'vertical'}, {validate: true}); } break; + case 'toolbar.standard': model.set({ 'isFixed': mql.matches }); break; + case 'toolbar.wide': model.set({ 'orientation': ((mql.matches) ? 'horizontal' : 'vertical') @@ -192,6 +214,7 @@ 'isTrayToggleVisible': mql.matches }); break; + default: break; } @@ -201,7 +224,7 @@ /** * A toggle is an interactive element often bound to a click handler. * - * @return {String} + * @return {string} * A string representing a DOM fragment. */ Drupal.theme.toolbarOrientationToggle = function () { diff --git a/core/modules/toolbar/js/toolbar.menu.js b/core/modules/toolbar/js/toolbar.menu.js index c14d0d4..bebced9 100644 --- a/core/modules/toolbar/js/toolbar.menu.js +++ b/core/modules/toolbar/js/toolbar.menu.js @@ -1,8 +1,11 @@ /** + * @file * Builds a nested accordion widget. * * Invoke on an HTML list element with the jQuery plugin pattern. - * - For example, $('.menu').drupalToolbarMenu(); + * + * @example + * $('.menu').drupalToolbarMenu(); */ (function ($, Drupal, drupalSettings) { @@ -77,7 +80,7 @@ // Adjust the toggle text. $toggle .find('.action') - // Expand Structure, Collapse Structure + // Expand Structure, Collapse Structure. .text((switcher) ? ui.handleClose : ui.handleOpen); } @@ -121,7 +124,7 @@ * @param {jQuery} $lists * A jQuery object of ul elements. * - * @param {Integer} level + * @param {number} level * The current level number to be assigned to the list elements. */ function markListLevels($lists, level) { @@ -175,7 +178,12 @@ /** * A toggle is an interactive element often bound to a click handler. * - * @return {String} + * @param {object} options + * @param {string} options.class + * @param {string} options.action + * @param {string} options.text + * + * @return {string} * A string representing a DOM fragment. */ Drupal.theme.toolbarMenuItemToggle = function (options) { diff --git a/core/modules/toolbar/js/views/BodyVisualView.js b/core/modules/toolbar/js/views/BodyVisualView.js index 00abcf2..7499646 100644 --- a/core/modules/toolbar/js/views/BodyVisualView.js +++ b/core/modules/toolbar/js/views/BodyVisualView.js @@ -7,20 +7,21 @@ "use strict"; - /** - * Adjusts the body element with the toolbar position and dimension changes. - */ - Drupal.toolbar.BodyVisualView = Backbone.View.extend({ + Drupal.toolbar.BodyVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.BodyVisualView# */{ /** - * {@inheritdoc} + * Adjusts the body element with the toolbar position and dimension changes. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change:orientation change:offsets change:activeTray change:isOriented change:isFixed change:isViewportOverflowConstrained', this.render); }, /** - * {@inheritdoc} + * @inheritdoc */ render: function () { var $body = $('body'); diff --git a/core/modules/toolbar/js/views/MenuVisualView.js b/core/modules/toolbar/js/views/MenuVisualView.js index d90b6d0..29e56ea 100644 --- a/core/modules/toolbar/js/views/MenuVisualView.js +++ b/core/modules/toolbar/js/views/MenuVisualView.js @@ -7,19 +7,21 @@ "use strict"; - /** - * Backbone View for collapsible menus. - */ - Drupal.toolbar.MenuVisualView = Backbone.View.extend({ + Drupal.toolbar.MenuVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.MenuVisualView# */{ + /** - * {@inheritdoc} + * Backbone View for collapsible menus. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change:subtrees', this.render); }, /** - * {@inheritdoc} + * @inheritdoc */ render: function () { var subtrees = this.model.get('subtrees'); diff --git a/core/modules/toolbar/js/views/ToolbarAuralView.js b/core/modules/toolbar/js/views/ToolbarAuralView.js index 9a2183e..d5f6f19 100644 --- a/core/modules/toolbar/js/views/ToolbarAuralView.js +++ b/core/modules/toolbar/js/views/ToolbarAuralView.js @@ -7,13 +7,17 @@ "use strict"; - /** - * Backbone view for the aural feedback of the toolbar. - */ - Drupal.toolbar.ToolbarAuralView = Backbone.View.extend({ + Drupal.toolbar.ToolbarAuralView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarAuralView# */{ /** - * {@inheritdoc} + * Backbone view for the aural feedback of the toolbar. + * + * @constructs + * + * @augments Backbone.View + * + * @param {object} options + * @param {object} options.strings */ initialize: function (options) { this.strings = options.strings; @@ -25,8 +29,8 @@ /** * Announces an orientation change. * - * @param Drupal.Toolbar.ToolbarModel model - * @param String orientation + * @param {Drupal.Toolbar.ToolbarModel} model + * @param {string} orientation * The new value of the orientation attribute in the model. */ onOrientationChange: function (model, orientation) { @@ -38,8 +42,8 @@ /** * Announces a changed active tray. * - * @param Drupal.Toolbar.ToolbarModel model - * @param Element orientation + * @param {Drupal.toolbar.ToolbarModel} model + * @param {HTMLElement} tray * The new value of the tray attribute in the model. */ onActiveTrayChange: function (model, tray) { diff --git a/core/modules/toolbar/js/views/ToolbarVisualView.js b/core/modules/toolbar/js/views/ToolbarVisualView.js index f30bde3..3459404 100644 --- a/core/modules/toolbar/js/views/ToolbarVisualView.js +++ b/core/modules/toolbar/js/views/ToolbarVisualView.js @@ -7,11 +7,11 @@ "use strict"; - /** - * Backbone view for the toolbar element. Listens to mouse & touch. - */ - Drupal.toolbar.ToolbarVisualView = Backbone.View.extend({ + Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarVisualView# */{ + /** + * @return {object} + */ events: function () { // Prevents delay and simulated mouse events. var touchEndToClick = function (event) { @@ -28,7 +28,14 @@ }, /** - * {@inheritdoc} + * Backbone view for the toolbar element. Listens to mouse & touch. + * + * @constructs + * + * @augments Backbone.View + * + * @param {object} options + * @param {object} options.strings */ initialize: function (options) { this.strings = options.strings; @@ -48,7 +55,9 @@ }, /** - * {@inheritdoc} + * @inheritdoc + * + * @return {Drupal.toolbar.ToolbarVisualView} */ render: function () { this.updateTabs(); @@ -81,7 +90,7 @@ /** * Responds to a toolbar tab click. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onTabClick: function (event) { // If this tab has a tray associated with it, it is considered an @@ -101,7 +110,7 @@ /** * Toggles the orientation of a toolbar tray. * - * @param jQuery.Event event + * @param {jQuery.Event} event */ onOrientationToggleClick: function (event) { var orientation = this.model.get('orientation'); diff --git a/core/modules/tour/js/tour.js b/core/modules/tour/js/tour.js index 08e5688..d4f8bca 100644 --- a/core/modules/tour/js/tour.js +++ b/core/modules/tour/js/tour.js @@ -14,12 +14,14 @@ * * It uses the query string for: * - tour: When ?tour=1 is present, the tour will start automatically - * after the page has loaded. + * after the page has loaded. * - tips: Pass ?tips=class in the url to filter the available tips to - * the subset which match the given class. + * the subset which match the given class. * - * Example: - * http://example.com/foo?tour=1&tips=bar + * @example + * http://example.com/foo?tour=1&tips=bar + * + * @type {Drupal~behavior} */ Drupal.behaviors.tour = { attach: function (context) { @@ -35,7 +37,8 @@ .on('change:isActive', function (model, isActive) { $(document).trigger((isActive) ? 'drupalTourStarted' : 'drupalTourStopped'); }) - // Initialization: check whether a tour is available on the current page. + // Initialization: check whether a tour is available on the current + // page. .set('tour', $(context).find('ol#tour')); // Start the tour immediately if toggled via query string. @@ -46,31 +49,72 @@ } }; - Drupal.tour = Drupal.tour || {models: {}, views: {}}; + /** + * @namespace + */ + Drupal.tour = Drupal.tour || { + + /** + * @namespace Drupal.tour.models + */ + models: {}, + + /** + * @namespace Drupal.tour.views + */ + views: {} + }; /** * Backbone Model for tours. + * + * @constructor + * + * @augments Backbone.Model */ - Drupal.tour.models.StateModel = Backbone.Model.extend({ - defaults: { - // Indicates whether the Drupal root window has a tour. + Drupal.tour.models.StateModel = Backbone.Model.extend(/** @lends Drupal.tour.models.StateModel# */{ + + /** + * @type {object} + */ + defaults: /** @lends Drupal.tour.models.StateModel# */{ + + /** + * Indicates whether the Drupal root window has a tour. + * + * @type {Array} + */ tour: [], - // Indicates whether the tour is currently running. + + /** + * Indicates whether the tour is currently running. + * + * @type {bool} + */ isActive: false, - // Indicates which tour is the active one (necessary to cleanly stop). + + /** + * Indicates which tour is the active one (necessary to cleanly stop). + * + * @type {Array} + */ activeTour: [] } }); - /** - * Handles edit mode toggle interactions. - */ - Drupal.tour.views.ToggleTourView = Backbone.View.extend({ + Drupal.tour.views.ToggleTourView = Backbone.View.extend(/** @lends Drupal.tour.views.ToggleTourView# */{ + /** + * @type {object} + */ events: {'click': 'onClick'}, /** - * Implements Backbone Views' initialize(). + * Handles edit mode toggle interactions. + * + * @constructs + * + * @augments Backbone.View */ initialize: function () { this.listenTo(this.model, 'change:tour change:isActive', this.render); @@ -78,7 +122,9 @@ }, /** - * Implements Backbone Views' render(). + * @inheritdoc + * + * @return {Drupal.tour.views.ToggleTourView} */ render: function () { // Render the visibility. @@ -103,7 +149,8 @@ $tour.joyride({ autoStart: true, postRideCallback: function () { that.model.set('isActive', false); }, - template: { // HTML segments for tip layout + // HTML segments for tip layout. + template: { link: '×', button: '' } @@ -119,6 +166,8 @@ /** * Toolbar tab click event handler; toggles isActive. + * + * @param {jQuery.Event} event */ onClick: function (event) { this.model.set('isActive', !this.model.get('isActive')); @@ -129,8 +178,8 @@ /** * Gets the tour. * - * @return jQuery - * A jQuery element pointing to a
        containing tour items. + * @return {jQuery} + * A jQuery element pointing to a `
          ` containing tour items. */ _getTour: function () { return this.model.get('tour'); @@ -139,7 +188,7 @@ /** * Gets the relevant document as a jQuery element. * - * @return jQuery + * @return {jQuery} * A jQuery element pointing to the document within which a tour would be * started given the current state. */ @@ -148,22 +197,21 @@ }, /** - * Removes tour items for elements that don't have matching page elements or - * are explicitly filtered out via the 'tips' query string. + * Removes tour items for elements that don't have matching page elements. * - * Example: - * http://example.com/foo?tips=bar + * Or that are explicitly filtered out via the 'tips' query string. * - * The above will filter out tips that do not have a matching page element or - * don't have the "bar" class. + * @example
      + * http://example.com/foo?tips=bar * - * @param jQuery $tour - * A jQuery element pointing to a
        containing tour items. - * @param jQuery $document + * @param {jQuery} $tour + * A jQuery element pointing to a `
          ` containing tour items. + * @param {jQuery} $document * A jQuery element pointing to the document within which the elements * should be sought. * - * @see _getDocument() + * @see Drupal.tour.views.ToggleTourView#_getDocument() */ _removeIrrelevantTourItems: function ($tour, $document) { var removals = false; diff --git a/core/modules/user/user.js b/core/modules/user/user.js index 615689c..e988f74 100644 --- a/core/modules/user/user.js +++ b/core/modules/user/user.js @@ -1,10 +1,17 @@ +/** + * @file + * User behaviors. + */ + (function ($, Drupal, drupalSettings) { "use strict"; /** - * Attach handlers to evaluate the strength of any password fields and to check - * that its confirmation is correct. + * Attach handlers to evaluate the strength of any password fields and to + * check that its confirmation is correct. + * + * @type {Drupal~behavior} */ Drupal.behaviors.password = { attach: function (context, settings) { @@ -60,7 +67,8 @@ $passwordSuggestions.html(result.message); } - // Only show the description box if a weakness exists in the password. + // Only show the description box if a weakness exists in the + // password. $passwordSuggestions.toggle(result.strength !== 100); // Adjust the length of the strength indicator. @@ -94,6 +102,11 @@ * Evaluate the strength of a user's password. * * Returns the estimated strength and the relevant output message. + * + * @param {string} password + * @param {object} translate + * + * @return {object} */ Drupal.evaluatePasswordStrength = function (password, translate) { var indicatorText; @@ -107,8 +120,8 @@ var hasNumbers = /[0-9]/.test(password); var hasPunctuation = /[^a-zA-Z0-9]/.test(password); - // If there is a username edit box on the page, compare password to that, otherwise - // use value from the database. + // If there is a username edit box on the page, compare password to that, + // otherwise use value from the database. var $usernameBox = $('input.username'); var username = ($usernameBox.length > 0) ? $usernameBox.val() : translate.username; @@ -162,7 +175,8 @@ strength = 5; } - // Based on the strength, work out what text should be shown by the password strength meter. + // Based on the strength, work out what text should be shown by the + // password strength meter. if (strength < 60) { indicatorText = translate.weak; indicatorClass = 'is-weak'; diff --git a/core/modules/user/user.permissions.js b/core/modules/user/user.permissions.js index 6e473fa..4db8454 100644 --- a/core/modules/user/user.permissions.js +++ b/core/modules/user/user.permissions.js @@ -1,9 +1,16 @@ +/** + * @file + * User permission page behaviors. + */ + (function ($) { "use strict"; /** * Shows checked and disabled checkboxes for inherited permissions. + * + * @type {Drupal~behavior} */ Drupal.behaviors.permissions = { attach: function (context) { diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js index ae373bb..50be456 100644 --- a/core/modules/views/js/ajax_view.js +++ b/core/modules/views/js/ajax_view.js @@ -2,12 +2,16 @@ * @file * Handles AJAX fetching of views, including filter submission and response. */ + (function ($, Drupal, drupalSettings) { "use strict"; /** - * Attaches the AJAX behavior to Views exposed filter forms and key View links. + * Attaches the AJAX behavior to Views exposed filter forms and key View + * links. + * + * @type {Drupal~behavior} */ Drupal.behaviors.ViewsAjaxView = {}; Drupal.behaviors.ViewsAjaxView.attach = function () { @@ -21,11 +25,23 @@ } }; + /** + * @namespace + */ Drupal.views = {}; + + /** + * @type {object.} + */ Drupal.views.instances = {}; /** * Javascript object for a certain view. + * + * @constructor + * + * @param {object} settings + * @param {string} settings.view_dom_id */ Drupal.views.ajaxView = function (settings) { var selector = '.view-dom-id-' + settings.view_dom_id; @@ -34,7 +50,8 @@ // Retrieve the path to use for views' ajax. var ajax_path = drupalSettings.views.ajax_path; - // If there are multiple views this might've ended up showing up multiple times. + // If there are multiple views this might've ended up showing up multiple + // times. if (ajax_path.constructor.toString().indexOf("Array") !== -1) { ajax_path = ajax_path[0]; } @@ -45,7 +62,8 @@ // Remove the question mark and Drupal path component if any. queryString = queryString.slice(1).replace(/q=[^&]+&?|&?render=[^&]+/, ''); if (queryString !== '') { - // If there is a '?' in ajax_path, clean url are on and & should be used to add parameters. + // If there is a '?' in ajax_path, clean url are on and & should be + // used to add parameters. queryString = ((/\?/.test(ajax_path)) ? '&' : '?') + queryString; } } @@ -86,6 +104,9 @@ this.refreshViewAjax = Drupal.ajax(self_settings); }; + /** + * @method + */ Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () { var button = $('input[type=submit], input[type=image]', this.$exposed_form); button = button[0]; @@ -97,6 +118,12 @@ this.exposedFormAjax = Drupal.ajax(self_settings); }; + /** + * @return {bool} + * If there is at least one parent with a view class return false. + * + * @todo remove .size() replace with .length. + */ Drupal.views.ajaxView.prototype.filterNestedViews = function () { // If there is at least one parent with a view class, this view // is nested (e.g., an attachment). Bail. @@ -113,6 +140,9 @@ /** * Attach the ajax behavior to a singe link. + * + * @param {string} id + * @param {HTMLElement} link */ Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function (id, link) { var $link = $(link); @@ -136,6 +166,12 @@ this.pagerAjax = Drupal.ajax(self_settings); }; + /** + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {string} response.selector + */ Drupal.AjaxCommands.prototype.viewsScrollTop = function (ajax, response) { // Scroll to the top of the view. This will allow users // to browse newly loaded content after e.g. clicking a pager @@ -149,7 +185,7 @@ while ($(scrollTarget).scrollTop() === 0 && $(scrollTarget).parent()) { scrollTarget = $(scrollTarget).parent(); } - // Only scroll upward + // Only scroll upward. if (offset.top - 10 < $(scrollTarget).scrollTop()) { $(scrollTarget).animate({scrollTop: (offset.top - 10)}, 500); } diff --git a/core/modules/views/js/base.js b/core/modules/views/js/base.js index d28b21d..d428229 100644 --- a/core/modules/views/js/base.js +++ b/core/modules/views/js/base.js @@ -2,14 +2,22 @@ * @file * Some basic behaviors and utility functions for Views. */ + (function ($, Drupal, drupalSettings) { "use strict"; + /** + * @namespace + */ Drupal.Views = {}; /** * Helper function to parse a querystring. + * + * @param {string} query + * + * @return {object} */ Drupal.Views.parseQueryString = function (query) { var args = {}; @@ -31,6 +39,11 @@ /** * Helper function to return a view's arguments based on a path. + * + * @param {string} href + * @param {string} viewPath + * + * @return {object} */ Drupal.Views.parseViewArgs = function (href, viewPath) { var returnObj = {}; @@ -46,12 +59,16 @@ /** * Strip off the protocol plus domain from an href. + * + * @param {string} href + * + * @return {string} */ Drupal.Views.pathPortion = function (href) { // Remove e.g. http://example.com if present. var protocol = window.location.protocol; if (href.substring(0, protocol.length) === protocol) { - // 2 is the length of the '//' that normally follows the protocol + // 2 is the length of the '//' that normally follows the protocol. href = href.substring(href.indexOf('/', protocol.length + 2)); } return href; @@ -59,6 +76,10 @@ /** * Return the Drupal path portion of an href. + * + * @param {string} href + * + * @return {string} */ Drupal.Views.getPath = function (href) { href = Drupal.Views.pathPortion(href); diff --git a/core/modules/views/js/views-contextual.js b/core/modules/views/js/views-contextual.js index e5eb14b..e0fe169 100644 --- a/core/modules/views/js/views-contextual.js +++ b/core/modules/views/js/views-contextual.js @@ -2,10 +2,15 @@ * @file * Javascript related to contextual links. */ + (function ($) { "use strict"; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsContextualLinks = { attach: function (context) { var id = $('body').attr('data-views-page-contextual-id'); diff --git a/core/modules/views/tests/modules/views_test_data/views_cache.test.js b/core/modules/views/tests/modules/views_test_data/views_cache.test.js index 8dd17c1..4089267 100644 --- a/core/modules/views/tests/modules/views_test_data/views_cache.test.js +++ b/core/modules/views/tests/modules/views_test_data/views_cache.test.js @@ -1,5 +1,8 @@ /** * @file * Just a placeholder file for the test. + * * @see ViewsCacheTest::testHeaderStorage + * + * @ignore */ diff --git a/core/modules/views_ui/js/ajax.js b/core/modules/views_ui/js/ajax.js index 492b63e..3079bad 100644 --- a/core/modules/views_ui/js/ajax.js +++ b/core/modules/views_ui/js/ajax.js @@ -2,15 +2,30 @@ * @file * Handles AJAX submission and response in Views UI. */ + (function ($, Drupal, drupalSettings) { "use strict"; + /** + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {string} response.selector + * @param {number} [status] + */ Drupal.AjaxCommands.prototype.viewsHighlight = function (ajax, response, status) { $('.hilited').removeClass('hilited'); $(response.selector).addClass('hilited'); }; + /** + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {bool} response.changed + * @param {number} [status] + */ Drupal.AjaxCommands.prototype.viewsShowButtons = function (ajax, response, status) { $('div.views-edit-view div.form-actions').removeClass('js-hide'); if (response.changed) { @@ -18,12 +33,26 @@ } }; + /** + * + * @param {Drupal.Ajax} [ajax] + * @param {object} [response] + * @param {number} [status] + */ Drupal.AjaxCommands.prototype.viewsTriggerPreview = function (ajax, response, status) { if ($('input#edit-displays-live-preview').is(':checked')) { $('#preview-submit').trigger('click'); } }; + /** + * + * @param {Drupal.Ajax} [ajax] + * @param {object} response + * @param {string} response.siteName + * @param {string} response.title + * @param {number} [status] + */ Drupal.AjaxCommands.prototype.viewsReplaceTitle = function (ajax, response, status) { var doc = document; // For the element, make a best-effort attempt to replace the page @@ -40,7 +69,9 @@ }; /** - * Get rid of irritating tabledrag messages + * Get rid of irritating tabledrag messages. + * + * @return {Array} */ Drupal.theme.tableDragChangedWarning = function () { return []; @@ -48,6 +79,8 @@ /** * Trigger preview when the "live preview" checkbox is checked. + * + * @type {Drupal~behavior} */ Drupal.behaviors.livePreview = { attach: function (context) { @@ -61,6 +94,8 @@ /** * Sync preview display. + * + * @type {Drupal~behavior} */ Drupal.behaviors.syncPreviewDisplay = { attach: function (context) { @@ -74,6 +109,10 @@ } }; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsAjax = { collapseReplaced: false, attach: function (context, settings) { diff --git a/core/modules/views_ui/js/dialog.views.js b/core/modules/views_ui/js/dialog.views.js index bdfd185..6f22e7f 100644 --- a/core/modules/views_ui/js/dialog.views.js +++ b/core/modules/views_ui/js/dialog.views.js @@ -1,3 +1,8 @@ +/** + * @file + * Views dialog behaviors. + */ + (function ($, Drupal, drupalSettings) { "use strict"; @@ -25,6 +30,10 @@ } } + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsModalContent = { attach: function (context) { $('body').once('viewsDialog').on('dialogContentResize.viewsDialog', '.ui-dialog-content', handleDialogResize); diff --git a/core/modules/views_ui/js/views-admin.js b/core/modules/views_ui/js/views-admin.js index e1d0750..89b4299 100644 --- a/core/modules/views_ui/js/views-admin.js +++ b/core/modules/views_ui/js/views-admin.js @@ -2,14 +2,20 @@ * @file * Some basic behaviors and utility functions for Views UI. */ + (function ($, Drupal, drupalSettings) { "use strict"; + /** + * @namespace + */ Drupal.viewsUi = {}; /** * Improve the user experience of the views edit interface. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsUiEditView = { attach: function () { @@ -22,8 +28,10 @@ }; /** - * In the add view wizard, use the view name to prepopulate form fields such as - * page title and menu link. + * In the add view wizard, use the view name to prepopulate form fields such + * as page title and menu link. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsUiAddView = { attach: function (context) { @@ -33,8 +41,8 @@ var replace = '-'; var suffix; - // The page title, block title, and menu link fields can all be prepopulated - // with the view name - no regular expression needed. + // The page title, block title, and menu link fields can all be + // prepopulated with the view name - no regular expression needed. var $fields = $context.find('[id^="edit-page-title"], [id^="edit-block-title"], [id^="edit-page-link-properties-title"]'); if ($fields.length) { if (!this.fieldsFiller) { @@ -42,10 +50,11 @@ } else { // After an AJAX response, this.fieldsFiller will still have event - // handlers bound to the old version of the form fields (which don't exist - // anymore). The event handlers need to be unbound and then rebound to the - // new markup. Note that jQuery.live is difficult to make work in this - // case because the IDs of the form fields change on every AJAX response. + // handlers bound to the old version of the form fields (which don't + // exist anymore). The event handlers need to be unbound and then + // rebound to the new markup. Note that jQuery.live is difficult to + // make work in this case because the IDs of the form fields change + // on every AJAX response. this.fieldsFiller.rebind($fields); } } @@ -61,8 +70,8 @@ } } - // Populate the RSS feed field with a URLified version of the view name, and - // an .xml suffix (to make it unique). + // Populate the RSS feed field with a URLified version of the view name, + // and an .xml suffix (to make it unique). var $feedField = $context.find('[id^="edit-page-feed-properties-path"]'); if ($feedField.length) { if (!this.feedFiller) { @@ -81,38 +90,75 @@ * * Prepopulates a form field based on the view name. * - * @param $target + * @constructor + * + * @param {jQuery} $target * A jQuery object representing the form field or fields to prepopulate. - * @param exclude - * (optional) A regular expression representing characters to exclude from + * @param {bool} [exclude=false] + * A regular expression representing characters to exclude from * the target field. - * @param replace - * (optional) A string to use as the replacement value for disallowed - * characters. - * @param suffix - * (optional) A suffix to append at the end of the target field content. + * @param {string} [replace=''] + * A string to use as the replacement value for disallowed characters. + * @param {string} [suffix=''] + * A suffix to append at the end of the target field content. */ Drupal.viewsUi.FormFieldFiller = function ($target, exclude, replace, suffix) { + + /** + * + * @type {jQuery} + */ this.source = $('#edit-label'); + + /** + * + * @type {jQuery} + */ this.target = $target; + + /** + * + * @type {bool} + */ this.exclude = exclude || false; + + /** + * + * @type {string} + */ this.replace = replace || ''; + + /** + * + * @type {string} + */ this.suffix = suffix || ''; // Create bound versions of this instance's object methods to use as event - // handlers. This will let us easily unbind those specific handlers later on. - // NOTE: jQuery.proxy will not work for this because it assumes we want only - // one bound version of an object method, whereas we need one version per - // object instance. + // handlers. This will let us easily unbind those specific handlers later + // on. NOTE: jQuery.proxy will not work for this because it assumes we want + // only one bound version of an object method, whereas we need one version + // per object instance. var self = this; + + /** + * + * @return {*} + */ this.populate = function () { return self._populate.call(self); }; + + /** + * + * @return {*} + */ this.unbind = function () { return self._unbind.call(self); }; this.bind(); // Object constructor; no return value. }; - $.extend(Drupal.viewsUi.FormFieldFiller.prototype, { + $.extend(Drupal.viewsUi.FormFieldFiller.prototype, /** @lends Drupal.viewsUi.FormFieldFiller# */{ + /** * Bind the form-filling behavior. */ @@ -126,6 +172,8 @@ /** * Get the source form field value as altered by the passed-in parameters. + * + * @return {string} */ getTransliterated: function () { var from = this.source.val(); @@ -157,7 +205,9 @@ }, /** - * Bind event handlers to the new form fields, after they're replaced via AJAX. + * Bind event handlers to new form fields, after they're replaced via Ajax. + * + * @param {jQuery} $fields */ rebind: function ($fields) { this.target = $fields; @@ -165,6 +215,10 @@ } }); + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.addItemForm = { attach: function (context) { var $context = $(context); @@ -174,21 +228,45 @@ $form = $context.find('form[id^="views-ui-add-handler-form"]'); } if ($form.once('views-ui-add-handler-form').length) { - // If we we have an unprocessed views-ui-add-handler-form, let's instantiate. + // If we we have an unprocessed views-ui-add-handler-form, let's + // instantiate. new Drupal.viewsUi.AddItemForm($form); } } }; + /** + * + * @constructor + * + * @param {jQuery} $form + */ Drupal.viewsUi.AddItemForm = function ($form) { + + /** + * + * @type {jQuery} + */ this.$form = $form; this.$form.find('.views-filterable-options :checkbox').on('click', $.proxy(this.handleCheck, this)); - // Find the wrapper of the displayed text. + + /** + * Find the wrapper of the displayed text. + */ this.$selected_div = this.$form.find('.views-selected-options').parent(); this.$selected_div.hide(); + + /** + * + * @type {Array} + */ this.checkedItems = []; }; + /** + * + * @param {jQuery.Event} event + */ Drupal.viewsUi.AddItemForm.prototype.handleCheck = function (event) { var $target = $(event.target); var label = $.trim($target.next().text()); @@ -199,7 +277,8 @@ } else { var position = $.inArray(label, this.checkedItems); - // Delete the item from the list and make sure that the list doesn't have undefined items left. + // Delete the item from the list and make sure that the list doesn't have + // undefined items left. for (var i = 0; i < this.checkedItems.length; i++) { if (i === position) { this.checkedItems.splice(i, 1); @@ -226,9 +305,12 @@ }; /** - * The input field items that add displays must be rendered as <input> elements. - * The following behavior detaches the <input> elements from the DOM, wraps them - * in an unordered list, then appends them to the list of tabs. + * The input field items that add displays must be rendered as `<input>` + * elements. The following behavior detaches the `<input>` elements from the + * DOM, wraps them in an unordered list, then appends them to the list of + * tabs. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsUiRenderAddViewButton = { attach: function (context) { @@ -242,10 +324,9 @@ var $displayButtons = $menu.nextAll('input.add-display').detach(); $displayButtons.appendTo($addDisplayDropdown.find('.action-list')).wrap('<li>') .parent().eq(0).addClass('first').end().eq(-1).addClass('last'); - // Remove the 'Add ' prefix from the button labels since they're being placed - // in an 'Add' dropdown. - // @todo This assumes English, but so does $addDisplayDropdown above. Add - // support for translation. + // Remove the 'Add ' prefix from the button labels since they're being + // placed in an 'Add' dropdown. @todo This assumes English, but so does + // $addDisplayDropdown above. Add support for translation. $displayButtons.each(function () { var label = $(this).val(); if (label.substr(0, 4) === 'Add ') { @@ -254,7 +335,7 @@ }); $addDisplayDropdown.appendTo($menu); - // Add the click handler for the add display button + // Add the click handler for the add display button. $menu.find('li.add > a').on('click', function (event) { event.preventDefault(); var $trigger = $(this); @@ -278,14 +359,22 @@ }; /** - * @note [@jessebeach] I feel like the following should be a more generic function and - * not written specifically for this UI, but I'm not sure where to put it. + * @param {jQuery} $trigger + * + * + * @note [@jessebeach] I feel like the following should be a more generic + * function and not written specifically for this UI, but I'm not sure + * where to put it. */ Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu = function ($trigger) { $trigger.parent().toggleClass('open'); $trigger.next().slideToggle('fast'); }; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsUiSearchOptions = { attach: function (context) { var $context = $(context); @@ -305,17 +394,33 @@ * Constructor for the viewsUi.OptionsSearch object. * * The OptionsSearch object filters the available options on a form according - * to the user's search term. Typing in "taxonomy" will show only those options - * containing "taxonomy" in their label. + * to the user's search term. Typing in "taxonomy" will show only those + * options containing "taxonomy" in their label. + * + * @constructor + * + * @param {jQuery} $form */ Drupal.viewsUi.OptionsSearch = function ($form) { + + /** + * + * @type {jQuery} + */ this.$form = $form; - // Add a keyup handler to the search box. + + /** + * Add a keyup handler to the search box. + */ this.$searchBox = this.$form.find('#edit-override-controls-options-search'); this.$searchBox.on('keyup', $.proxy(this.handleKeyup, this)); - // Get a list of option labels and their corresponding divs and maintain it - // in memory, so we have as little overhead as possible at keyup time. + + /** + * Get a list of option labels and their corresponding divs and maintain it + * in memory, so we have as little overhead as possible at keyup time. + */ this.options = this.getOptions(this.$form.find('.filterable-option')); + // Restripe on initial loading. this.handleKeyup(); // Trap the ENTER key in the search box so that it doesn't submit the form. @@ -326,13 +431,16 @@ }); }; - $.extend(Drupal.viewsUi.OptionsSearch.prototype, { + $.extend(Drupal.viewsUi.OptionsSearch.prototype, /** @lends Drupal.viewsUi.OptionsSearch# */{ + /** * Assemble a list of all the filterable options on the form. * - * @param $allOptions - * A $ object representing the rows of filterable options to be + * @param {jQuery} $allOptions + * A jQuery object representing the rows of filterable options to be * shown and hidden depending on the user's search terms. + * + * @return {Array} */ getOptions: function ($allOptions) { var $label; @@ -357,15 +465,18 @@ }, /** - * Keyup handler for the search box that hides or shows the relevant options. + * Keyup handler for the search box that hides or shows the relevant + * options. + * + * @param {jQuery.Event} event */ handleKeyup: function (event) { var found; var option; var zebraClass; - // Determine the user's search query. The search text has been converted to - // lowercase. + // Determine the user's search query. The search text has been converted + // to lowercase. var search = this.$searchBox.val().toLowerCase(); var words = search.split(' '); var wordsLength = words.length; @@ -402,6 +513,10 @@ } }); + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsUiPreview = { attach: function (context) { // Only act on the edit view form. @@ -410,9 +525,10 @@ return; } - // If the display has no contextual filters, hide the form where you enter - // the contextual filters for the live preview. If it has contextual filters, - // show the form. + // If the display has no contextual filters, hide the form where you + // enter + // the contextual filters for the live preview. If it has contextual + // filters, show the form. var $contextualFilters = $contextualFiltersBucket.find('.views-display-setting a'); if ($contextualFilters.length) { $('#preview-args').parent().show(); @@ -428,6 +544,10 @@ } }; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsUiRearrangeFilter = { attach: function (context) { // Only act on the rearrange filter form. @@ -445,19 +565,49 @@ /** * Improve the UI of the rearrange filters dialog box. + * + * @constructor + * + * @param {jQuery} $table + * @param {jQuery} $operator */ Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) { - // Keep a reference to the <table> being altered and to the div containing - // the filter groups operator dropdown (if it exists). + + /** + * Keep a reference to the `<table>` being altered and to the div containing + * the filter groups operator dropdown (if it exists). + */ this.table = $table; + + /** + * + * @type {jQuery} + */ this.operator = $operator; + + /** + * + * @type {bool} + */ this.hasGroupOperator = this.operator.length > 0; - // Keep a reference to all draggable rows within the table. + /** + * Keep a reference to all draggable rows within the table. + * + * @type {jQuery} + */ this.draggableRows = $table.find('.draggable'); - // Keep a reference to the buttons for adding and removing filter groups. + /** + * Keep a reference to the buttons for adding and removing filter groups. + * + * @type {jQuery} + */ this.addGroupButton = $('input#views-add-group'); + + /** + * @type {jQuery} + */ this.removeGroupButtons = $table.find('input.views-remove-group'); // Add links that duplicate the functionality of the (hidden) add and remove @@ -467,14 +617,17 @@ // When there is a filter groups operator dropdown on the page, create // duplicates of the dropdown between each pair of filter groups. if (this.hasGroupOperator) { + /** + * @type {jQuery} + */ this.dropdowns = this.duplicateGroupsOperator(); this.syncGroupsOperators(); } - // Add methods to the tableDrag instance to account for operator cells (which - // span multiple rows), the operator labels next to each filter (e.g., "And" - // or "Or"), the filter groups, and other special aspects of this tableDrag - // instance. + // Add methods to the tableDrag instance to account for operator cells + // (which span multiple rows), the operator labels next to each filter + // (e.g., "And" or "Or"), the filter groups, and other special aspects of + // this tableDrag instance. this.modifyTableDrag(); // Initialize the operator labels (e.g., "And" or "Or") that are displayed @@ -486,36 +639,37 @@ .on('change.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); // Bind handlers so that when a "Remove" link is clicked, we: - // - Update the rowspans of cells containing an operator dropdown (since they - // need to change to reflect the number of rows in each group). - // - Redraw the operator labels next to the filters in the group (since the - // filter that is currently displayed last in each group is not supposed to - // have a label display next to it). + // - Update the rowspans of cells containing an operator dropdown (since + // they need to change to reflect the number of rows in each group). - + // Redraw the operator labels next to the filters in the group (since the + // filter that is currently displayed last in each group is not supposed to + // have a label display next to it). $table.find('a.views-groups-remove-link') .once('views-rearrange-filter-handler') .on('click.views-rearrange-filter-handler', $.proxy(this, 'updateRowspans')) .on('click.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); }; - $.extend(Drupal.viewsUi.RearrangeFilterHandler.prototype, { + $.extend(Drupal.viewsUi.RearrangeFilterHandler.prototype, /** @lends Drupal.viewsUi.RearrangeFilterHandler# */{ + /** * Insert links that allow filter groups to be added and removed. */ insertAddRemoveFilterGroupLinks: function () { - // Insert a link for adding a new group at the top of the page, and make it - // match the action link styling used in a typical page.html.twig. Since - // Drupal does not provide a theme function for this markup this is the best - // we can do. + // Insert a link for adding a new group at the top of the page, and make + // it match the action link styling used in a typical page.html.twig. + // Since Drupal does not provide a theme function for this markup this is + // the best we can do. $('<ul class="action-links"><li><a id="views-add-group-link" href="#">' + this.addGroupButton.val() + '</a></li></ul>') .prependTo(this.table.parent()) - // When the link is clicked, dynamically click the hidden form button for - // adding a new filter group. + // When the link is clicked, dynamically click the hidden form button + // for adding a new filter group. .once('views-rearrange-filter-handler') .on('click.views-rearrange-filter-handler', $.proxy(this, 'clickAddGroupButton')); - // Find each (visually hidden) button for removing a filter group and insert - // a link next to it. + // Find each (visually hidden) button for removing a filter group and + // insert a link next to it. var length = this.removeGroupButtons.length; var i; for (i = 0; i < length; i++) { @@ -532,11 +686,13 @@ /** * Dynamically click the button that adds a new filter group. + * + * @param {jQuery.Event} event */ clickAddGroupButton: function (event) { // Due to conflicts between Drupal core's AJAX system and the Views AJAX - // system, the only way to get this to work seems to be to trigger both the - // mousedown and submit events. + // system, the only way to get this to work seems to be to trigger both + // the mousedown and submit events. this.addGroupButton .trigger('mousedown') .trigger('submit'); @@ -546,7 +702,7 @@ /** * Dynamically click a button for removing a filter group. * - * @param event + * @param {jQuery.Event} event * Event being triggered, with event.data.buttonId set to the ID of the * form button that should be clicked. */ @@ -561,6 +717,8 @@ /** * Move the groups operator so that it's between the first two groups, and * duplicate it between any subsequent groups. + * + * @return {jQuery} */ duplicateGroupsOperator: function () { var dropdowns; @@ -574,7 +732,8 @@ this.operator.find('label').add('div.description').addClass('visually-hidden'); this.operator.find('select').addClass('form-select'); - // Keep a list of the operator dropdowns, so we can sync their behavior later. + // Keep a list of the operator dropdowns, so we can sync their behavior + // later. dropdowns = this.operator; // Move the operator to a new row just above the second group. @@ -582,7 +741,8 @@ this.operator.find('label').add('div.description').addClass('visually-hidden'); this.operator.find('select').addClass('form-select'); - // Keep a list of the operator dropdowns, so we can sync their behavior later. + // Keep a list of the operator dropdowns, so we can sync their behavior + // later. dropdowns = this.operator; // Move the operator to a new row just above the second group. @@ -612,7 +772,8 @@ */ syncGroupsOperators: function () { if (this.dropdowns.length < 2) { - // We only have one dropdown (or none at all), so there's nothing to sync. + // We only have one dropdown (or none at all), so there's nothing to + // sync. return; } @@ -623,6 +784,8 @@ * Click handler for the operators that appear between filter groups. * * Forces all operator dropdowns to have the same value. + * + * @param {jQuery.Event} event */ operatorChangeHandler: function (event) { var $target = $(event.target); @@ -632,6 +795,9 @@ operators.val($target.val()); }, + /** + * @method + */ modifyTableDrag: function () { var tableDrag = Drupal.tableDrag['views-rearrange-filters']; var filterHandler = this; @@ -639,19 +805,22 @@ /** * Override the row.onSwap method from tabledrag.js. * - * When a row is dragged to another place in the table, several things need - * to occur. - * - The row needs to be moved so that it's within one of the filter groups. - * - The operator cells that span multiple rows need their rowspan attributes - * updated to reflect the number of rows in each group. - * - The operator labels that are displayed next to each filter need to be + * When a row is dragged to another place in the table, several things + * need to occur. + * - The row needs to be moved so that it's within one of the filter + * groups. + * - The operator cells that span multiple rows need their rowspan + * attributes updated to reflect the number of rows in each group. + * - The operator labels that are displayed next to each filter need to + * be * redrawn, to account for the row's new location. */ tableDrag.row.prototype.onSwap = function () { if (filterHandler.hasGroupOperator) { - // Make sure the row that just got moved (this.group) is inside one of - // the filter groups (i.e. below an empty marker row or a draggable). If - // it isn't, move it down one. + // Make sure the row that just got moved (this.group) is inside one + // of + // the filter groups (i.e. below an empty marker row or a draggable). + // If it isn't, move it down one. var thisRow = $(this.group); var previousRow = thisRow.prev('tr'); if (previousRow.length && !previousRow.hasClass('group-message') && !previousRow.hasClass('draggable')) { @@ -672,22 +841,22 @@ * Override the onDrop method from tabledrag.js. */ tableDrag.onDrop = function () { - // If the tabledrag change marker (i.e., the "*") has been inserted inside - // a row after the operator label (i.e., "And" or "Or") rearrange the items - // so the operator label continues to appear last. + // If the tabledrag change marker (i.e., the "*") has been inserted + // inside a row after the operator label (i.e., "And" or "Or") + // rearrange the items so the operator label continues to appear last. var changeMarker = $(this.oldRowElement).find('.tabledrag-changed'); if (changeMarker.length) { - // Search for occurrences of the operator label before the change marker, - // and reverse them. + // Search for occurrences of the operator label before the change + // marker, and reverse them. var operatorLabel = changeMarker.prevAll('.views-operator-label'); if (operatorLabel.length) { operatorLabel.insertAfter(changeMarker); } } - // Make sure the "group" dropdown is properly updated when rows are dragged - // into an empty filter group. This is borrowed heavily from the block.js - // implementation of tableDrag.onDrop(). + // Make sure the "group" dropdown is properly updated when rows are + // dragged into an empty filter group. This is borrowed heavily from + // the block.js implementation of tableDrag.onDrop(). var groupRow = $(this.rowObject.element).prevAll('tr.group-message').get(0); var groupName = groupRow.className.replace(/([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); var groupField = $('select.views-group-select', this.rowObject.element); @@ -704,29 +873,31 @@ */ redrawOperatorLabels: function () { for (var i = 0; i < this.draggableRows.length; i++) { - // Within the row, the operator labels are displayed inside the first table - // cell (next to the filter name). + // Within the row, the operator labels are displayed inside the first + // table cell (next to the filter name). var $draggableRow = $(this.draggableRows[i]); var $firstCell = $draggableRow.find('td').eq(0); if ($firstCell.length) { // The value of the operator label ("And" or "Or") is taken from the - // first operator dropdown we encounter, going backwards from the current - // row. This dropdown is the one associated with the current row's filter - // group. + // first operator dropdown we encounter, going backwards from the + // current row. This dropdown is the one associated with the current + // row's filter group. var operatorValue = $draggableRow.prevAll('.views-group-title').find('option:selected').html(); var operatorLabel = '<span class="views-operator-label">' + operatorValue + '</span>'; // If the next visible row after this one is a draggable filter row, // display the operator label next to the current row. (Checking for - // visibility is necessary here since the "Remove" links hide the removed - // row but don't actually remove it from the document). + // visibility is necessary here since the "Remove" links hide the + // removed row but don't actually remove it from the document). var $nextRow = $draggableRow.nextAll(':visible').eq(0); var $existingOperatorLabel = $firstCell.find('.views-operator-label'); if ($nextRow.hasClass('draggable')) { - // If an operator label was already there, replace it with the new one. + // If an operator label was already there, replace it with the new + // one. if ($existingOperatorLabel.length) { $existingOperatorLabel.replaceWith(operatorLabel); } - // Otherwise, append the operator label to the end of the table cell. + // Otherwise, append the operator label to the end of the table + // cell. else { $firstCell.append(operatorLabel); } @@ -744,7 +915,8 @@ }, /** - * Update the rowspan attribute of each cell containing an operator dropdown. + * Update the rowspan attribute of each cell containing an operator + * dropdown. */ updateRowspans: function () { var $row; @@ -768,7 +940,8 @@ $operatorCell.attr('rowspan', 2); } else if ($row.hasClass('draggable') && $row.is(':visible')) { - // We've found a visible filter row, so we now know the group isn't empty. + // We've found a visible filter row, so we now know the group isn't + // empty. draggableCount++; $currentEmptyRow.removeClass('group-empty').addClass('group-populated'); // The operator cell should span all draggable rows, plus the title. @@ -780,6 +953,8 @@ /** * Add a select all checkbox, which checks each checkbox at once. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsFilterConfigSelectAll = { attach: function (context) { @@ -807,6 +982,8 @@ /** * Remove icon class from elements that are themed as buttons or dropbuttons. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsRemoveIconClass = { attach: function (context) { @@ -816,6 +993,8 @@ /** * Change "Expose filter" buttons into checkboxes. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsUiCheckboxify = { attach: function (context, settings) { @@ -831,6 +1010,8 @@ /** * Change the default widget to select the default group according to the * selected widget for the exposed group. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsUiChangeDefaultWidget = { attach: function () { @@ -856,9 +1037,12 @@ }; /** - * Attaches an expose filter button to a checkbox that triggers its click event. + * Attaches an expose filter button to a checkbox that triggers its click + * event. + * + * @constructor * - * @param button + * @param {HTMLElement} button * The DOM object representing the button to be checkboxified. */ Drupal.viewsUi.Checkboxifier = function (button) { @@ -875,6 +1059,8 @@ /** * When the checkbox is checked or unchecked, simulate a button press. + * + * @param {jQuery.Event} e */ Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function (e) { this.$button @@ -884,6 +1070,8 @@ /** * Change the Apply button text based upon the override select state. + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewsUiOverrideSelect = { attach: function (context) { @@ -919,11 +1107,15 @@ } }; + /** + * + * @type {Drupal~behavior} + */ Drupal.behaviors.viewsUiHandlerRemoveLink = { attach: function (context) { var $context = $(context); - // Handle handler deletion by looking for the hidden checkbox and hiding the - // row. + // Handle handler deletion by looking for the hidden checkbox and hiding + // the row. $context.find('a.views-remove-link').once('views').on('click', function (event) { var id = $(this).attr('id').replace('views-remove-link-', ''); $context.find('#views-row-' + id).hide(); @@ -931,8 +1123,8 @@ event.preventDefault(); }); - // Handle display deletion by looking for the hidden checkbox and hiding the - // row. + // Handle display deletion by looking for the hidden checkbox and hiding + // the row. $context.find('a.display-remove-link').once('display').on('click', function (event) { var id = $(this).attr('id').replace('display-remove-link-', ''); $context.find('#display-row-' + id).hide(); diff --git a/core/modules/views_ui/js/views_ui.listing.js b/core/modules/views_ui/js/views_ui.listing.js index 0374891..4c13d79 100644 --- a/core/modules/views_ui/js/views_ui.listing.js +++ b/core/modules/views_ui/js/views_ui.listing.js @@ -1,3 +1,8 @@ +/** + * @file + * Views listing behaviors. + */ + (function ($, Drupal) { "use strict"; @@ -8,6 +13,8 @@ * Text search input: input.views-filter-text * Target table: input.views-filter-text[data-table] * Source text: .views-table-filter-text-source + * + * @type {Drupal~behavior} */ Drupal.behaviors.viewTableFilterByText = { attach: function (context, settings) {
      tags, but it doesn't make - * sense to allow for a "summary" attribute on these tags. Hence these would - * need to be split in two separate rules. + * HTML rules must be added with the `addHTMLRule()` method. A feature that + * has zero HTML rules does not create or modify HTML. * - * HTML rules must be added with the addHTMLRule() method. A feature that has - * zero HTML rules does not create or modify HTML. + * @constructor * - * @param String name + * @param {string} name * The name of the feature. * * @see Drupal.EditorFeatureHTMLRule @@ -518,7 +649,7 @@ /** * Adds a HTML rule to the list of HTML rules for this feature. * - * @param Drupal.editorFeatureHTMLRule rule + * @param {Drupal.EditorFeatureHTMLRule} rule * A text editor feature HTML rule. */ Drupal.EditorFeature.prototype.addHTMLRule = function (rule) { @@ -526,66 +657,53 @@ }; /** - * Constructor for an editor feature HTML rule. Intended to be used in - * combination with Drupal.EditorFeature. - * - * A text editor feature rule object describes both - * - required HTML tags, attributes, styles and classes: without these, the - * text editor feature is unable to function. It's possible that a - * - allowed HTML tags, attributes, styles and classes: these are optional in - * the strictest sense, but it is possible that the feature generates them. - * - * The structure can be very clearly seen below: there's a "required" and an - * "allowed" key. For each of those, there are objects with the "tags", - * "attributes", "styles" and "classes" keys. For all these keys the values are - * initialized to the empty array. List each possible value as an array value. - * Besides the "required" and "allowed" keys, there's an optional "raw" key: it - * allows text editor implementations to optionally pass in their raw - * representation instead of the Drupal-defined representation for HTML rules. - * - * Examples: - * - tags: [''] - * - attributes: ['href', 'alt'] - * - styles: ['color', 'text-decoration'] - * - classes: ['external', 'internal'] - */ - Drupal.EditorFeatureHTMLRule = function () { - this.required = {tags: [], attributes: [], styles: [], classes: []}; - this.allowed = {tags: [], attributes: [], styles: [], classes: []}; - this.raw = null; - }; - - /** - * Constructor for a text filter status object. Initialized with the filter ID. + * Text filter status object. Initialized with the filter ID. * * Indicates whether the text filter is currently active (enabled) or not. * - * Contains a set of HTML rules (Drupal.FilterHTMLRule objects) that describe - * which HTML tags are allowed or forbidden. They can also describe for a set of - * tags (or all tags) which attributes, styles and classes are allowed and which - * are forbidden. + * Contains a set of HTML rules ({@link Drupal.FilterHTMLRule} objects) that + * describe which HTML tags are allowed or forbidden. They can also describe + * for a set of tags (or all tags) which attributes, styles and classes are + * allowed and which are forbidden. * - * It is necessary to allow for multiple HTML rules per feature, for analogous - * reasons as Drupal.EditorFeature. + * It is necessary to allow for multiple HTML rules per feature, for + * analogous reasons as {@link Drupal.EditorFeature}. * - * HTML rules must be added with the addHTMLRule() method. A filter that has + * HTML rules must be added with the `addHTMLRule()` method. A filter that has * zero HTML rules does not disallow any HTML. * - * @param String name + * @constructor + * + * @param {string} name * The name of the feature. * * @see Drupal.FilterHTMLRule */ Drupal.FilterStatus = function (name) { + + /** + * + * @type {string} + */ this.name = name; + + /** + * + * @type {bool} + */ this.active = false; + + /** + * + * @type {Array.} + */ this.rules = []; }; /** * Adds a HTML rule to the list of HTML rules for this filter. * - * @param Drupal.FilterHTMLRule rule + * @param {Drupal.FilterHTMLRule} rule * A text filter HTML rule. */ Drupal.FilterStatus.prototype.addHTMLRule = function (rule) { @@ -593,59 +711,72 @@ }; /** - * A text filter HTML rule object. Intended to be used in combination with - * Drupal.FilterStatus. + * A text filter HTML rule object. + * + * Intended to be used in combination with {@link Drupal.FilterStatus}. * - * A text filter rule object describes + * A text filter rule object describes: * 1. allowed or forbidden tags: (optional) whitelist or blacklist HTML tags - * 2. restricted tag properties: (optional) whitelist or blacklist attributes, - * styles and classes on a set of HTML tags. + * 2. restricted tag properties: (optional) whitelist or blacklist + * attributes, styles and classes on a set of HTML tags. * * Typically, each text filter rule object does either 1 or 2, not both. * * The structure can be very clearly seen below: - * 1. use the "tags" key to list HTML tags, and set the "allow" key to either - * true (to allow these HTML tags) or false (to forbid these HTML tags). If - * you leave the "tags" key's default value (the empty array), no - * restrictions are applied. - * 2. all nested within the "restrictedTags" key: use the "tags" subkey to list - * HTML tags to which you want to apply property restrictions, then use the - * "allowed" subkey to whitelist specific property values, and similarly use - * the "forbidden" subkey to blacklist specific property values. + * 1. use the "tags" key to list HTML tags, and set the "allow" key to + * either true (to allow these HTML tags) or false (to forbid these HTML + * tags). If you leave the "tags" key's default value (the empty array), + * no restrictions are applied. + * 2. all nested within the "restrictedTags" key: use the "tags" subkey to + * list HTML tags to which you want to apply property restrictions, then + * use the "allowed" subkey to whitelist specific property values, and + * similarly use the "forbidden" subkey to blacklist specific property + * values. + * + * @example
      Whitelist the "p", "strong" and "a" HTML tags.For the "a" HTML tag, only allow the "href" attribute + * and the "external" class and disallow the "target" attribute.For all tags, allow the "data-*" attribute (that is, any + * attribute that begins with "data-").If you override this method, you should call this + * method (the parent class' initialize()) first.This will filter out tips that do not have a matching + * page element or don't have the "bar" class.