2015-06-08 14:04:39 +00:00
/ * *
2022-09-09 06:26:42 +00:00
* @ 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 ,
loadjs ,
{ isFocusable , tabbable } ,
) {
/ * *
* Attaches the Ajax behavior to each Ajax form element .
*
* @ type { Drupal ~ behavior }
*
* @ prop { Drupal ~ behaviorAttach } attach
* Initialize all { @ link Drupal . Ajax } objects declared in
* ` drupalSettings.ajax ` or initialize { @ link Drupal . Ajax } objects from
* DOM elements having the ` use-ajax-submit ` or ` use-ajax ` css class .
* @ prop { Drupal ~ behaviorDetach } detach
* During ` unload ` remove all { @ link Drupal . Ajax } objects related to
* the removed content .
* /
2014-01-27 21:41:32 +00:00
Drupal . behaviors . AJAX = {
2021-12-18 06:12:16 +00:00
attach ( context , settings ) {
2014-01-27 21:41:32 +00:00
function loadAjaxBehavior ( base ) {
2021-12-18 06:12:16 +00:00
const elementSettings = settings . ajax [ base ] ;
2017-12-03 16:16:29 +00:00
if ( typeof elementSettings . selector === 'undefined' ) {
2021-12-18 06:12:16 +00:00
elementSettings . selector = ` # ${ base } ` ;
2014-01-27 21:41:32 +00:00
}
2022-09-09 06:26:42 +00:00
// Use jQuery selector instead of a native selector for
// backwards compatibility.
once ( 'drupal-ajax' , $ ( elementSettings . selector ) ) . forEach ( ( el ) => {
2021-08-10 10:05:02 +00:00
elementSettings . element = el ;
2017-12-03 16:16:29 +00:00
elementSettings . base = base ;
Drupal . ajax ( elementSettings ) ;
2014-01-27 21:41:32 +00:00
} ) ;
2009-08-17 07:12:16 +00:00
}
2022-09-09 06:26:42 +00:00
// Load all Ajax behaviors specified in the settings.
2022-11-24 13:59:45 +00:00
Object . keys ( settings . ajax || { } ) . forEach ( loadAjaxBehavior ) ;
2022-09-09 06:26:42 +00:00
2017-10-16 22:15:27 +00:00
Drupal . ajax . bindAjaxLinks ( document . body ) ;
2022-09-09 06:26:42 +00:00
// This class means to submit the form to the action using Ajax.
once ( 'ajax' , '.use-ajax-submit' ) . forEach ( ( el ) => {
2021-12-18 06:12:16 +00:00
const elementSettings = { } ;
2022-09-09 06:26:42 +00:00
// Ajax submits specified in this manner automatically submit to the
// normal form action.
2021-08-10 10:05:02 +00:00
elementSettings . url = $ ( el . form ) . attr ( 'action' ) ;
2022-09-09 06:26:42 +00:00
// Form submit button clicks need to tell the form what was clicked so
// it gets passed in the POST request.
2017-12-03 16:16:29 +00:00
elementSettings . setClick = true ;
2022-09-09 06:26:42 +00:00
// Form buttons use the 'click' event rather than mousedown.
2017-12-03 16:16:29 +00:00
elementSettings . event = 'click' ;
2022-09-09 06:26:42 +00:00
// Clicked form buttons look better with the throbber than the progress
// bar.
elementSettings . progress = { type : 'throbber' } ;
2021-08-10 10:05:02 +00:00
elementSettings . base = el . id ;
elementSettings . element = el ;
2022-09-09 06:26:42 +00:00
2017-12-03 16:16:29 +00:00
Drupal . ajax ( elementSettings ) ;
2014-01-27 21:41:32 +00:00
} ) ;
2016-02-22 02:20:13 +00:00
} ,
2021-12-18 06:12:16 +00:00
detach ( context , settings , trigger ) {
2016-02-22 02:20:13 +00:00
if ( trigger === 'unload' ) {
2022-09-09 06:26:42 +00:00
Drupal . ajax . expired ( ) . forEach ( ( instance ) => {
// Set this to null and allow garbage collection to reclaim
// the memory.
2016-02-22 02:20:13 +00:00
Drupal . ajax . instances [ instance . instanceIndex ] = null ;
} ) ;
}
2022-09-09 06:26:42 +00:00
} ,
2009-08-17 07:12:16 +00:00
} ;
2022-09-09 06:26:42 +00:00
/ * *
* 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 .
* @ param { string } customMessage
* The custom message .
* /
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
Drupal . AjaxError = function ( xmlhttp , uri , customMessage ) {
2021-12-18 06:12:16 +00:00
let statusCode ;
let statusText ;
let responseText ;
2014-01-27 21:41:32 +00:00
if ( xmlhttp . status ) {
2022-09-09 06:26:42 +00:00
statusCode = ` \n ${ Drupal . t ( 'An AJAX HTTP error occurred.' ) } \n ${ Drupal . t (
'HTTP Result Code: !status' ,
{
'!status' : xmlhttp . status ,
} ,
) } ` ;
2017-05-19 22:12:53 +00:00
} else {
2022-09-09 06:26:42 +00:00
statusCode = ` \n ${ Drupal . t (
'An AJAX HTTP request terminated abnormally.' ,
) } ` ;
2014-01-27 21:41:32 +00:00
}
2021-12-18 06:12:16 +00:00
statusCode += ` \n ${ Drupal . t ( 'Debugging information follows.' ) } ` ;
2022-09-09 06:26:42 +00:00
const pathText = ` \n ${ Drupal . t ( 'Path: !uri' , { '!uri' : uri } )} ` ;
2014-01-27 21:41:32 +00:00
statusText = '' ;
2022-09-09 06:26:42 +00:00
// 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.
2014-01-27 21:41:32 +00:00
try {
2021-12-18 06:12:16 +00:00
statusText = ` \n ${ Drupal . t ( 'StatusText: !statusText' , {
2022-09-09 06:26:42 +00:00
'!statusText' : xmlhttp . statusText . trim ( ) ,
2021-12-18 06:12:16 +00:00
} ) } ` ;
2022-09-09 06:26:42 +00:00
} catch ( e ) {
// Empty.
}
2014-01-27 21:41:32 +00:00
responseText = '' ;
2022-09-09 06:26:42 +00:00
// Again, we don't have a way to know for sure whether accessing
// xmlhttp.responseText is going to throw an exception. So we'll catch it.
2014-01-27 21:41:32 +00:00
try {
2021-12-18 06:12:16 +00:00
responseText = ` \n ${ Drupal . t ( 'ResponseText: !responseText' , {
2022-09-09 06:26:42 +00:00
'!responseText' : xmlhttp . responseText . trim ( ) ,
2021-12-18 06:12:16 +00:00
} ) } ` ;
2022-09-09 06:26:42 +00:00
} catch ( e ) {
// Empty.
}
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Make the responseText more readable by stripping HTML tags and newlines.
2015-10-13 22:37:56 +00:00
responseText = responseText . replace ( /<("[^"]*"|'[^']*'|[^'">])*>/gi , '' ) ;
responseText = responseText . replace ( /[\n]+\s+/g , '\n' ) ;
2022-09-09 06:26:42 +00:00
// We don't need readyState except for status == 0.
const readyStateText =
xmlhttp . status === 0
? ` \n ${ Drupal . t ( 'ReadyState: !readyState' , {
'!readyState' : xmlhttp . readyState ,
} ) } `
: '' ;
customMessage = customMessage
? ` \n ${ Drupal . t ( 'CustomMessage: !customMessage' , {
'!customMessage' : customMessage ,
} ) } `
: '' ;
/ * *
* Formatted and translated error message .
*
* @ type { string }
* /
this . message =
statusCode +
pathText +
statusText +
customMessage +
responseText +
readyStateText ;
/ * *
* Used by some browsers to display a more accurate stack trace .
*
* @ type { string }
* /
2014-01-27 21:41:32 +00:00
this . name = 'AjaxError' ;
2023-03-24 14:06:43 +00:00
if ( ! Drupal . AjaxError . messages ) {
Drupal . AjaxError . messages = new Drupal . Message ( ) ;
}
Drupal . AjaxError . messages . add (
Drupal . t (
"Oops, something went wrong. Check your browser's developer console for more details." ,
) ,
{
type : 'error' ,
} ,
) ;
2009-08-17 07:12:16 +00:00
} ;
2014-01-27 21:41:32 +00:00
Drupal . AjaxError . prototype = new Error ( ) ;
Drupal . AjaxError . prototype . constructor = Drupal . AjaxError ;
2010-11-20 08:26:21 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Provides Ajax page updating via jQuery $ . ajax .
*
* This function is designed to improve developer experience by wrapping the
* initialization of { @ link Drupal . Ajax } objects and storing all created
* objects in the { @ link Drupal . ajax . instances } array .
*
* @ example
* Drupal . behaviors . myCustomAJAXStuff = {
* attach : function ( context , settings ) {
*
* var ajaxSettings = {
* url : 'my/url/path' ,
* // If the old version of Drupal.ajax() needs to be used those
* // properties can be added
* base : 'myBase' ,
* element : $ ( context ) . find ( '.someElement' )
* } ;
*
* var myAjaxObject = Drupal . ajax ( ajaxSettings ) ;
*
* // Declare a new Ajax command specifically for this Ajax object.
* myAjaxObject . commands . insert = function ( ajax , response , status ) {
* $ ( '#my-wrapper' ) . append ( response . data ) ;
* alert ( 'New content was appended to #my-wrapper' ) ;
* } ;
*
* // This command will remove this Ajax object from the page.
* myAjaxObject . commands . destroyObject = function ( ajax , response , status ) {
* Drupal . ajax . instances [ this . instanceIndex ] = null ;
* } ;
*
* // Programmatically trigger the Ajax request.
* myAjaxObject . execute ( ) ;
* }
* } ;
*
* @ param { object } settings
* The settings object passed to { @ link Drupal . Ajax } constructor .
* @ param { string } [ settings . base ]
* Base is passed to { @ link Drupal . Ajax } constructor as the 'base'
* parameter .
* @ param { HTMLElement } [ settings . element ]
* Element parameter of { @ link Drupal . Ajax } constructor , element on which
* event listeners will be bound .
*
* @ return { Drupal . Ajax }
* The created Ajax object .
*
* @ see Drupal . AjaxCommands
* /
2015-05-20 11:02:57 +00:00
Drupal . ajax = function ( settings ) {
if ( arguments . length !== 1 ) {
2022-09-09 06:26:42 +00:00
throw new Error (
'Drupal.ajax() function must be called with one configuration object only' ,
) ;
2015-05-20 11:02:57 +00:00
}
2022-09-09 06:26:42 +00:00
// Map those config keys to variables for the old Drupal.ajax function.
2021-12-18 06:12:16 +00:00
const base = settings . base || false ;
const element = settings . element || false ;
2015-05-20 11:02:57 +00:00
delete settings . base ;
delete settings . element ;
2022-09-09 06:26:42 +00:00
// By default do not display progress for ajax calls without an element.
2015-05-20 11:02:57 +00:00
if ( ! settings . progress && ! element ) {
settings . progress = false ;
}
2021-12-18 06:12:16 +00:00
const ajax = new Drupal . Ajax ( base , element , settings ) ;
2015-05-20 11:02:57 +00:00
ajax . instanceIndex = Drupal . ajax . instances . length ;
Drupal . ajax . instances . push ( ajax ) ;
2022-09-09 06:26:42 +00:00
2015-05-20 11:02:57 +00:00
return ajax ;
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Contains all created Ajax objects .
*
* @ type { Array . < Drupal . Ajax | null > }
* /
2015-05-20 11:02:57 +00:00
Drupal . ajax . instances = [ ] ;
2022-09-09 06:26:42 +00:00
/ * *
* List all objects where the associated element is not in the DOM
*
* This method ignores { @ link Drupal . Ajax } objects not bound to DOM elements
* when created with { @ link Drupal . ajax } .
*
* @ return { Array . < Drupal . Ajax > }
* The list of expired { @ link Drupal . Ajax } objects .
* /
2016-02-22 02:20:13 +00:00
Drupal . ajax . expired = function ( ) {
2022-09-09 06:26:42 +00:00
return Drupal . ajax . instances . filter (
( instance ) =>
instance &&
instance . element !== false &&
! document . body . contains ( instance . element ) ,
) ;
2016-02-22 02:20:13 +00:00
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Bind Ajax functionality to links that use the 'use-ajax' class .
*
* @ param { HTMLElement } element
* Element to enable Ajax functionality for .
* /
Drupal . ajax . bindAjaxLinks = ( element ) => {
// Bind Ajax behaviors to all items showing the class.
once ( 'ajax' , '.use-ajax' , element ) . forEach ( ( ajaxLink ) => {
2021-12-18 06:12:16 +00:00
const $linkElement = $ ( ajaxLink ) ;
2022-09-09 06:26:42 +00:00
2021-12-18 06:12:16 +00:00
const elementSettings = {
2022-09-09 06:26:42 +00:00
// Clicked links look better with the throbber than the progress bar.
progress : { type : 'throbber' } ,
2017-10-16 22:15:27 +00:00
dialogType : $linkElement . data ( 'dialog-type' ) ,
dialog : $linkElement . data ( 'dialog-options' ) ,
dialogRenderer : $linkElement . data ( 'dialog-renderer' ) ,
base : $linkElement . attr ( 'id' ) ,
2022-09-09 06:26:42 +00:00
element : ajaxLink ,
2017-10-16 22:15:27 +00:00
} ;
2021-12-18 06:12:16 +00:00
const href = $linkElement . attr ( 'href' ) ;
2022-09-09 06:26:42 +00:00
/ * *
* For anchor tags , these will go to the target of the anchor rather than
* the usual location .
* /
2017-10-16 22:15:27 +00:00
if ( href ) {
elementSettings . url = href ;
elementSettings . event = 'click' ;
}
2023-04-26 15:16:24 +00:00
const httpMethod = $linkElement . data ( 'ajax-http-method' ) ;
Issue #956186 by catch, dpolant, SteffenR, yhahn, lauriii, nod_, sokrplare, rwaery.11, Supreetam09, effulgentsia, geek-merlin: Allow AJAX to use GET requests
2023-04-07 09:48:30 +00:00
/ * *
2023-04-26 15:16:24 +00:00
* In case of setting custom ajax http method for link we rewrite ajax . httpMethod .
Issue #956186 by catch, dpolant, SteffenR, yhahn, lauriii, nod_, sokrplare, rwaery.11, Supreetam09, effulgentsia, geek-merlin: Allow AJAX to use GET requests
2023-04-07 09:48:30 +00:00
* /
2023-04-26 15:16:24 +00:00
if ( httpMethod ) {
elementSettings . httpMethod = httpMethod ;
Issue #956186 by catch, dpolant, SteffenR, yhahn, lauriii, nod_, sokrplare, rwaery.11, Supreetam09, effulgentsia, geek-merlin: Allow AJAX to use GET requests
2023-04-07 09:48:30 +00:00
}
2017-10-16 22:15:27 +00:00
Drupal . ajax ( elementSettings ) ;
} ) ;
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Settings for an Ajax object .
*
* @ typedef { object } Drupal . Ajax ~ elementSettings
*
* @ prop { string } url
* Target of the Ajax request .
* @ prop { ? string } [ event ]
* Event bound to settings . element which will trigger the Ajax request .
2022-11-28 11:30:17 +00:00
* @ prop { boolean } [ keypress = true ]
2022-09-09 06:26:42 +00:00
* Triggers a request on keypress events .
* @ prop { ? string } selector
* jQuery selector targeting the element to bind events to or used with
* { @ link Drupal . AjaxCommands } .
* @ prop { string } [ effect = 'none' ]
* Name of the jQuery method to use for displaying new Ajax content .
* @ prop { string | number } [ speed = 'none' ]
* Speed with which to apply the effect .
* @ prop { string } [ method ]
* Name of the jQuery method used to insert new content in the targeted
* element .
* @ prop { object } [ progress ]
* Settings for the display of a user - friendly loader .
* @ prop { string } [ progress . type = 'throbber' ]
* Type of progress element , core provides ` 'bar' ` , ` 'throbber' ` and
* ` 'fullscreen' ` .
2023-07-23 16:33:54 +00:00
* @ prop { string } [ progress . message = Drupal . t ( 'Processing...' ) ]
2022-09-09 06:26:42 +00:00
* Custom message to be used with the bar indicator .
* @ prop { object } [ submit ]
* Extra data to be sent with the Ajax request .
2022-11-28 11:30:17 +00:00
* @ prop { boolean } [ submit . js = true ]
2022-09-09 06:26:42 +00:00
* Allows the PHP side to know this comes from an Ajax request .
* @ prop { object } [ dialog ]
* Options for { @ link Drupal . dialog } .
* @ prop { string } [ dialogType ]
* One of ` 'modal' ` or ` 'dialog' ` .
* @ prop { string } [ prevent ]
* List of events on which to stop default action and stop propagation .
* /
/ * *
* Ajax constructor .
*
* 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
* included to provide Ajax capabilities .
*
* @ constructor
*
* @ param { string } [ base ]
* Base parameter of { @ link Drupal . Ajax } constructor
* @ param { HTMLElement } [ element ]
* Element parameter of { @ link Drupal . Ajax } constructor , element on which
* event listeners will be bound .
* @ param { Drupal . Ajax ~ elementSettings } elementSettings
* Settings for this Ajax object .
* /
2017-12-03 16:16:29 +00:00
Drupal . Ajax = function ( base , element , elementSettings ) {
2021-12-18 06:12:16 +00:00
const defaults = {
2023-04-26 15:16:24 +00:00
httpMethod : 'POST' ,
2015-05-20 11:02:57 +00:00
event : element ? 'mousedown' : null ,
2014-01-27 21:41:32 +00:00
keypress : true ,
2021-12-18 06:12:16 +00:00
selector : base ? ` # ${ base } ` : null ,
2014-01-27 21:41:32 +00:00
effect : 'none' ,
speed : 'none' ,
method : 'replaceWith' ,
progress : {
type : 'throbber' ,
2023-07-23 16:33:54 +00:00
message : Drupal . t ( 'Processing...' ) ,
2014-01-27 21:41:32 +00:00
} ,
submit : {
2022-09-09 06:26:42 +00:00
js : true ,
} ,
2014-01-27 21:41:32 +00:00
} ;
2022-09-09 06:26:42 +00:00
2017-12-03 16:16:29 +00:00
$ . extend ( this , defaults , elementSettings ) ;
2022-09-09 06:26:42 +00:00
/ * *
* @ type { Drupal . AjaxCommands }
* /
2014-01-27 21:41:32 +00:00
this . commands = new Drupal . AjaxCommands ( ) ;
2022-09-09 06:26:42 +00:00
/ * *
2022-11-28 11:30:17 +00:00
* @ type { boolean | number }
2022-09-09 06:26:42 +00:00
* /
2015-05-20 11:02:57 +00:00
this . instanceIndex = false ;
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// @todo Remove this after refactoring the PHP code to:
// - Call this 'selector'.
// - Include the '#' for ID-based selectors.
// - Support non-ID-based selectors.
2014-01-27 21:41:32 +00:00
if ( this . wrapper ) {
2022-09-09 06:26:42 +00:00
/ * *
* @ type { string }
* /
2021-12-18 06:12:16 +00:00
this . wrapper = ` # ${ this . wrapper } ` ;
2009-11-03 05:34:37 +00:00
}
2022-09-09 06:26:42 +00:00
/ * *
* @ type { HTMLElement }
* /
2014-01-27 21:41:32 +00:00
this . element = element ;
2022-09-09 06:26:42 +00:00
Issue #2627788 by lauriii, DuaelFr, Pancho, vsujeetkumar, _utsavsharma, KapilV, facine, Danny_Joris, alexpott, juampynr, tim.plunkett: Focus state bug on text field AJAX calls
2023-10-16 14:37:43 +00:00
/ * *
* The last focused element right before processing ajax response .
*
* @ type { string | null }
* /
this . preCommandsFocusedElementSelector = null ;
2022-09-09 06:26:42 +00:00
/ * *
* @ type { Drupal . Ajax ~ elementSettings }
* /
2017-12-03 16:16:29 +00:00
this . elementSettings = elementSettings ;
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
// If there isn't a form, jQuery.ajax() will be used instead, allowing us to
// bind Ajax to links as well.
2015-05-20 11:02:57 +00:00
if ( this . element && this . element . form ) {
2022-09-09 06:26:42 +00:00
/ * *
* @ type { jQuery }
* /
2014-01-27 21:41:32 +00:00
this . $form = $ ( this . element . form ) ;
2009-08-17 07:12:16 +00:00
}
2022-09-09 06:26:42 +00:00
// If no Ajax callback URL was given, use the link href or form action.
2014-01-27 21:41:32 +00:00
if ( ! this . url ) {
2021-12-18 06:12:16 +00:00
const $element = $ ( this . element ) ;
2023-06-26 07:01:04 +00:00
if ( this . element . tagName === 'A' ) {
2015-05-20 11:02:57 +00:00
this . url = $element . attr ( 'href' ) ;
2017-05-19 22:12:53 +00:00
} else if ( this . element && element . form ) {
2014-01-27 21:41:32 +00:00
this . url = this . $form . attr ( 'action' ) ;
}
2009-08-17 07:12:16 +00:00
}
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
// the server detect when it needs to degrade gracefully.
// There are four scenarios to check for:
// 1. /nojs/
// 2. /nojs$ - The end of a URL string.
// 3. /nojs? - Followed by a query (e.g. path/nojs?destination=foobar).
// 4. /nojs# - Followed by a fragment (e.g.: path/nojs#my-fragment).
2021-12-18 06:12:16 +00:00
const originalUrl = this . url ;
2017-05-19 22:12:53 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Processed Ajax URL .
*
* @ type { string }
* /
this . url = this . url . replace ( /\/nojs(\/|$|\?|#)/ , '/ajax$1' ) ;
// If the 'nojs' version of the URL is trusted, also trust the 'ajax'
// version.
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
if ( drupalSettings . ajaxTrustedUrl [ originalUrl ] ) {
drupalSettings . ajaxTrustedUrl [ this . url ] = true ;
}
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Set the options for the ajaxSubmit function.
// The 'this' variable will not persist inside of the options object.
2021-12-18 06:12:16 +00:00
const ajax = this ;
2022-09-09 06:26:42 +00:00
/ * *
* Options for the jQuery . ajax function .
*
* @ name Drupal . Ajax # options
*
* @ type { object }
*
* @ prop { string } url
* Ajax URL to be called .
* @ prop { object } data
* Ajax payload .
* @ prop { function } beforeSerialize
* Implement jQuery beforeSerialize function to call
* { @ link Drupal . Ajax # beforeSerialize } .
* @ prop { function } beforeSubmit
* Implement jQuery beforeSubmit function to call
* { @ link Drupal . Ajax # beforeSubmit } .
* @ prop { function } beforeSend
* Implement jQuery beforeSend function to call
* { @ link Drupal . Ajax # beforeSend } .
* @ prop { function } success
* Implement jQuery success function to call
* { @ link Drupal . Ajax # success } .
* @ prop { function } complete
* Implement jQuery success function to clean up ajax state and trigger an
* error if needed .
* @ prop { string } dataType = 'json'
* Type of the response expected .
* @ prop { string } type = 'POST'
* HTTP method to use for the Ajax request .
* /
2014-01-27 21:41:32 +00:00
ajax . options = {
url : ajax . url ,
data : ajax . submit ,
2022-12-08 17:20:31 +00:00
isInProgress ( ) {
return ajax . ajaxing ;
} ,
2021-12-18 06:12:16 +00:00
beforeSerialize ( elementSettings , options ) {
2017-12-03 16:16:29 +00:00
return ajax . beforeSerialize ( elementSettings , options ) ;
2014-01-27 21:41:32 +00:00
} ,
2021-12-18 06:12:16 +00:00
beforeSubmit ( formValues , elementSettings , options ) {
2014-01-27 21:41:32 +00:00
ajax . ajaxing = true ;
Issue #2627788 by lauriii, DuaelFr, Pancho, vsujeetkumar, _utsavsharma, KapilV, facine, Danny_Joris, alexpott, juampynr, tim.plunkett: Focus state bug on text field AJAX calls
2023-10-16 14:37:43 +00:00
ajax . preCommandsFocusedElementSelector = null ;
2017-12-03 16:16:29 +00:00
return ajax . beforeSubmit ( formValues , elementSettings , options ) ;
2014-01-27 21:41:32 +00:00
} ,
2021-12-18 06:12:16 +00:00
beforeSend ( xmlhttprequest , options ) {
2014-01-27 21:41:32 +00:00
ajax . ajaxing = true ;
return ajax . beforeSend ( xmlhttprequest , options ) ;
} ,
2021-12-18 06:12:16 +00:00
success ( response , status , xmlhttprequest ) {
Issue #2627788 by lauriii, DuaelFr, Pancho, vsujeetkumar, _utsavsharma, KapilV, facine, Danny_Joris, alexpott, juampynr, tim.plunkett: Focus state bug on text field AJAX calls
2023-10-16 14:37:43 +00:00
ajax . preCommandsFocusedElementSelector =
document . activeElement . getAttribute ( 'data-drupal-selector' ) ;
2022-09-09 06:26:42 +00:00
// Sanity check for browser support (object expected).
// When using iFrame uploads, responses must be returned as a string.
2014-01-27 21:41:32 +00:00
if ( typeof response === 'string' ) {
response = $ . parseJSON ( response ) ;
}
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
2022-09-09 06:26:42 +00:00
// Prior to invoking the response's commands, verify that they can be
// trusted by checking for a response header. See
// \Drupal\Core\EventSubscriber\AjaxResponseSubscriber for details.
// - Empty responses are harmless so can bypass verification. This
// avoids an alert message for server-generated no-op responses that
// skip Ajax rendering.
// - Ajax objects with trusted URLs (e.g., ones defined server-side via
// #ajax) can bypass header verification. This is especially useful
// for Ajax with multipart forms. Because IFRAME transport is used,
// the response headers cannot be accessed for verification.
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
if ( response !== null && ! drupalSettings . ajaxTrustedUrl [ ajax . url ] ) {
if ( xmlhttprequest . getResponseHeader ( 'X-Drupal-Ajax-Token' ) !== '1' ) {
2022-09-09 06:26:42 +00:00
const customMessage = Drupal . t (
'The response failed verification so will not be processed.' ,
) ;
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
return ajax . error ( xmlhttprequest , ajax . url , customMessage ) ;
}
}
2022-09-09 06:26:42 +00:00
return (
// Ensure that the return of the success callback is a Promise.
// When the return is a Promise, using resolve will unwrap it, and
// when the return is not a Promise we make sure it can be used as
// one. This is useful for code that overrides the success method.
Promise . resolve ( ajax . success ( response , status ) )
// Ajaxing status is back to false when all the AJAX commands have
// finished executing.
. then ( ( ) => {
ajax . ajaxing = false ;
2022-12-08 17:20:31 +00:00
// jQuery normally triggers the ajaxSuccess, ajaxComplete, and
// ajaxStop events after the "success" function passed to $.ajax()
// returns, but we prevented that via
// $.event.special[EVENT_NAME].trigger in order to wait for the
// commands to finish executing. Now that they have, re-trigger
// those events.
$ ( document ) . trigger ( 'ajaxSuccess' , [ xmlhttprequest , this ] ) ;
$ ( document ) . trigger ( 'ajaxComplete' , [ xmlhttprequest , this ] ) ;
if ( -- $ . active === 0 ) {
$ ( document ) . trigger ( 'ajaxStop' ) ;
}
2022-09-09 06:26:42 +00:00
} )
) ;
2014-01-27 21:41:32 +00:00
} ,
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
error ( xmlhttprequest , status , error ) {
2014-01-27 21:41:32 +00:00
ajax . ajaxing = false ;
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
} ,
complete ( xmlhttprequest , status ) {
2014-01-27 21:41:32 +00:00
if ( status === 'error' || status === 'parsererror' ) {
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
return ajax . error ( xmlhttprequest , ajax . url ) ;
2014-01-27 21:41:32 +00:00
}
} ,
dataType : 'json' ,
2020-09-16 11:21:15 +00:00
jsonp : false ,
2023-04-26 15:16:24 +00:00
method : ajax . httpMethod ,
2014-01-27 21:41:32 +00:00
} ;
2017-12-03 16:16:29 +00:00
if ( elementSettings . dialog ) {
ajax . options . data . dialogOptions = elementSettings . dialog ;
2014-01-27 21:41:32 +00:00
}
2022-09-09 06:26:42 +00:00
// Ensure that we have a valid URL by adding ? when no query parameter is
// yet available, otherwise append using &.
2024-01-29 10:43:58 +00:00
if ( ! ajax . options . url . includes ( '?' ) ) {
2015-05-14 01:12:54 +00:00
ajax . options . url += '?' ;
2017-05-19 22:12:53 +00:00
} else {
2015-05-14 01:12:54 +00:00
ajax . options . url += '&' ;
}
2022-09-09 06:26:42 +00:00
// If this element has a dialog type use if for the wrapper if not use 'ajax'.
2021-12-18 06:12:16 +00:00
let wrapper = ` drupal_ ${ elementSettings . dialogType || 'ajax' } ` ;
2017-12-03 16:16:29 +00:00
if ( elementSettings . dialogRenderer ) {
2021-12-18 06:12:16 +00:00
wrapper += ` . ${ elementSettings . dialogRenderer } ` ;
2017-07-14 18:32:12 +00:00
}
2021-12-18 06:12:16 +00:00
ajax . options . url += ` ${ Drupal . ajax . WRAPPER _FORMAT } = ${ wrapper } ` ;
2022-09-09 06:26:42 +00:00
// Bind the ajaxSubmit function to the element event.
2017-12-03 16:16:29 +00:00
$ ( ajax . element ) . on ( elementSettings . event , function ( event ) {
2022-09-09 06:26:42 +00:00
if (
! drupalSettings . ajaxTrustedUrl [ ajax . url ] &&
! Drupal . url . isLocal ( ajax . url )
) {
throw new Error (
Drupal . t ( 'The callback URL is not local and not trusted: !url' , {
'!url' : ajax . url ,
} ) ,
) ;
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
}
2014-01-27 21:41:32 +00:00
return ajax . eventResponse ( this , event ) ;
} ) ;
2022-09-09 06:26:42 +00:00
// If necessary, enable keyboard submission so that Ajax behaviors
// can be triggered through keyboard input as well as e.g. a mousedown
// action.
2017-12-03 16:16:29 +00:00
if ( elementSettings . keypress ) {
2014-01-27 21:41:32 +00:00
$ ( ajax . element ) . on ( 'keypress' , function ( event ) {
return ajax . keypressResponse ( this , event ) ;
} ) ;
2009-08-17 07:12:16 +00:00
}
2022-09-09 06:26:42 +00:00
// If necessary, prevent the browser default action of an additional event.
// For example, prevent the browser default action of a click, even if the
// Ajax behavior binds to mousedown.
2017-12-03 16:16:29 +00:00
if ( elementSettings . prevent ) {
$ ( ajax . element ) . on ( elementSettings . prevent , false ) ;
2009-08-17 07:12:16 +00:00
}
2014-01-27 21:41:32 +00:00
} ;
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* URL query attribute to indicate the wrapper used to render a request .
*
* The wrapper format determines how the HTML is wrapped , for example in a
* modal dialog .
*
* @ const { string }
*
* @ default
* /
2015-05-14 01:12:54 +00:00
Drupal . ajax . WRAPPER _FORMAT = '_wrapper_format' ;
2022-09-09 06:26:42 +00:00
/ * *
* Request parameter to indicate that a request is a Drupal Ajax request .
*
* @ const { string }
*
* @ default
* /
2015-06-22 13:16:58 +00:00
Drupal . Ajax . AJAX _REQUEST _PARAMETER = '_drupal_ajax' ;
2022-09-09 06:26:42 +00:00
/ * *
* Execute the ajax request .
*
* Allows developers to execute an Ajax request manually without specifying
* an event to respond to .
*
* @ return { object }
* Returns the jQuery . Deferred object underlying the Ajax request . If
* pre - serialization fails , the Deferred will be returned in the rejected
* state .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . execute = function ( ) {
2022-09-09 06:26:42 +00:00
// Do not perform another ajax command if one is already in progress.
2015-05-20 11:02:57 +00:00
if ( this . ajaxing ) {
return ;
}
try {
this . beforeSerialize ( this . element , this . options ) ;
2022-09-09 06:26:42 +00:00
// Return the jqXHR so that external code can hook into the Deferred API.
2015-12-21 17:49:42 +00:00
return $ . ajax ( this . options ) ;
2017-05-19 22:12:53 +00:00
} catch ( e ) {
2022-09-09 06:26:42 +00:00
// Unset the ajax.ajaxing flag here because it won't be unset during
// the complete response.
2015-05-20 11:02:57 +00:00
this . ajaxing = false ;
2022-09-09 06:26:42 +00:00
window . alert (
` An error occurred while attempting to process ${ this . options . url } : ${ e . message } ` ,
) ;
// For consistency, return a rejected Deferred (i.e., jqXHR's superclass)
// so that calling code can take appropriate action.
2015-12-21 17:49:42 +00:00
return $ . Deferred ( ) . reject ( ) ;
2015-05-20 11:02:57 +00:00
}
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Handle a key press .
*
* The Ajax object will , if instructed , bind to a key press response . This
* will test to see if the key press is valid to trigger this event and
* if it is , trigger it for us and prevent other keypresses from triggering .
2024-02-08 09:21:36 +00:00
* In this case we ' re handling RETURN and SPACE BAR keypresses ( event codes 13
2022-09-09 06:26:42 +00:00
* 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
* Element the event was triggered on .
* @ param { jQuery . Event } event
* Triggered event .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . keypressResponse = function ( element , event ) {
2022-09-09 06:26:42 +00:00
// Create a synonym for this to reduce code confusion.
2021-12-18 06:12:16 +00:00
const ajax = this ;
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Detect enter key and space bar and allow the standard response for them,
// except for form elements of type 'text', 'tel', 'number' and 'textarea',
2024-02-08 09:21:36 +00:00
// where the space bar activation causes inappropriate activation if
2022-09-09 06:26:42 +00:00
// #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' )
) {
2014-01-27 21:41:32 +00:00
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
2017-12-03 16:16:29 +00:00
$ ( element ) . trigger ( ajax . elementSettings . event ) ;
2014-01-27 21:41:32 +00:00
}
} ;
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Handle an event that triggers an Ajax response .
*
* When an event that triggers an Ajax response happens , this method will
* 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
* Element the event was triggered on .
* @ param { jQuery . Event } event
* Triggered event .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . eventResponse = function ( element , event ) {
2014-01-27 21:41:32 +00:00
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
2022-09-09 06:26:42 +00:00
// Create a synonym for this to reduce code confusion.
2021-12-18 06:12:16 +00:00
const ajax = this ;
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Do not perform another Ajax command if one is already in progress.
2014-01-27 21:41:32 +00:00
if ( ajax . ajaxing ) {
return ;
}
try {
if ( ajax . $form ) {
2022-09-09 06:26:42 +00:00
// If setClick is set, we must set this to ensure that the button's
// value is passed.
2014-01-27 21:41:32 +00:00
if ( ajax . setClick ) {
2022-09-09 06:26:42 +00:00
// Mark the clicked button. 'form.clk' is a special variable for
// ajaxSubmit that tells the system which element got clicked to
// trigger the submit. Without it there would be no 'op' or
// equivalent.
2014-01-27 21:41:32 +00:00
element . form . clk = element ;
}
ajax . $form . ajaxSubmit ( ajax . options ) ;
2017-05-19 22:12:53 +00:00
} else {
2014-01-27 21:41:32 +00:00
ajax . beforeSerialize ( ajax . element , ajax . options ) ;
$ . ajax ( ajax . options ) ;
2009-08-17 07:12:16 +00:00
}
2017-05-19 22:12:53 +00:00
} catch ( e ) {
2022-09-09 06:26:42 +00:00
// Unset the ajax.ajaxing flag here because it won't be unset during
// the complete response.
2014-01-27 21:41:32 +00:00
ajax . ajaxing = false ;
2022-09-09 06:26:42 +00:00
window . alert (
` An error occurred while attempting to process ${ ajax . options . url } : ${ e . message } ` ,
) ;
2014-01-27 21:41:32 +00:00
}
} ;
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Handler for the form serialization .
*
* Runs before the beforeSend ( ) handler ( see below ) , and unlike that one , runs
* before field data is collected .
*
* @ param { object } [ element ]
* Ajax object ' s ` elementSettings ` .
* @ param { object } options
* jQuery . ajax options .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . beforeSerialize = function ( element , options ) {
2022-09-09 06:26:42 +00:00
// Allow detaching behaviors to update field values before collecting them.
// This is only needed when field values are added to the POST data, so only
// when there is a form such that this.$form.ajaxSubmit() is used instead of
// $.ajax(). When there is no form and $.ajax() is used, beforeSerialize()
// isn't called, but don't rely on that: explicitly check this.$form.
2019-02-07 16:45:01 +00:00
if ( this . $form && document . body . contains ( this . $form . get ( 0 ) ) ) {
2021-12-18 06:12:16 +00:00
const settings = this . settings || drupalSettings ;
2014-01-27 21:41:32 +00:00
Drupal . detachBehaviors ( this . $form . get ( 0 ) , settings , 'serialize' ) ;
}
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
// Inform Drupal that this is an AJAX request.
2015-06-22 13:16:58 +00:00
options . data [ Drupal . Ajax . AJAX _REQUEST _PARAMETER ] = 1 ;
2022-09-09 06:26:42 +00:00
// Allow Drupal to return new JavaScript and CSS files to load without
// returning the ones already loaded.
2023-11-09 16:51:22 +00:00
// @see \Drupal\Core\StackMiddleWare\AjaxPageState
2022-09-09 06:26:42 +00:00
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
// @see \Drupal\Core\Asset\LibraryDependencyResolverInterface::getMinimalRepresentativeSubset()
// @see system_js_settings_alter()
2021-12-18 06:12:16 +00:00
const pageState = drupalSettings . ajaxPageState ;
2014-01-27 21:41:32 +00:00
options . data [ 'ajax_page_state[theme]' ] = pageState . theme ;
options . data [ 'ajax_page_state[theme_token]' ] = pageState . theme _token ;
2015-01-21 15:21:06 +00:00
options . data [ 'ajax_page_state[libraries]' ] = pageState . libraries ;
2014-01-27 21:41:32 +00:00
} ;
2013-03-17 16:04:07 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Modify form values prior to form submission .
*
* @ param { Array . < object > } formValues
* Processed form values .
* @ param { jQuery } element
* The form node as a jQuery object .
* @ param { object } options
* jQuery . ajax options .
* /
Drupal . Ajax . prototype . beforeSubmit = function ( formValues , element , options ) {
// This function is left empty to make it simple to override for modules
// that wish to add functionality here.
} ;
2009-12-12 23:36:28 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Prepare the Ajax request before it is sent .
*
* @ param { XMLHttpRequest } xmlhttprequest
* Native Ajax object .
* @ param { object } options
* jQuery . ajax options .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . beforeSend = function ( xmlhttprequest , options ) {
2022-09-09 06:26:42 +00:00
// 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.
2014-01-27 21:41:32 +00:00
if ( this . $form ) {
options . extraData = options . extraData || { } ;
2022-09-09 06:26:42 +00:00
// 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.
2014-01-27 21:41:32 +00:00
options . extraData . ajax _iframe _upload = '1' ;
2020-01-30 09:08:38 +00:00
2022-09-09 06:26:42 +00:00
// 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.
const v = $ . fieldValue ( this . element ) ;
2014-01-27 21:41:32 +00:00
if ( v !== null ) {
options . extraData [ this . element . name ] = v ;
}
2010-01-12 06:31:22 +00:00
}
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Disable the element that received the change to prevent user interface
// interaction while the Ajax request is in progress. ajax.ajaxing prevents
// the element from triggering a new request, but does not prevent the user
// from changing its value.
2015-03-14 04:02:53 +00:00
$ ( this . element ) . prop ( 'disabled' , true ) ;
2014-01-27 21:41:32 +00:00
2015-05-20 11:02:57 +00:00
if ( ! this . progress || ! this . progress . type ) {
return ;
2010-01-12 06:31:22 +00:00
}
2015-05-20 11:02:57 +00:00
2022-09-09 06:26:42 +00:00
// Insert progress indicator.
const progressIndicatorMethod = ` setProgressIndicator ${ this . progress . type
. slice ( 0 , 1 )
. toUpperCase ( ) } $ { this . progress . type . slice ( 1 ) . toLowerCase ( ) } ` ;
if (
progressIndicatorMethod in this &&
typeof this [ progressIndicatorMethod ] === 'function'
) {
2015-05-20 11:02:57 +00:00
this [ progressIndicatorMethod ] . call ( this ) ;
2014-01-27 21:41:32 +00:00
}
2015-05-20 11:02:57 +00:00
} ;
2022-09-09 06:26:42 +00:00
/ * *
* An animated progress throbber and container element for AJAX operations .
*
* @ param { string } [ message ]
* ( optional ) The message shown on the UI .
* @ return { string }
* The HTML markup for the throbber .
* /
Drupal . theme . ajaxProgressThrobber = ( message ) => {
// Build markup without adding extra white space since it affects rendering.
const messageMarkup =
typeof message === 'string'
? Drupal . theme ( 'ajaxProgressMessage' , message )
: '' ;
2021-12-18 06:12:16 +00:00
const throbber = '<div class="throbber"> </div>' ;
2022-09-09 06:26:42 +00:00
2021-12-18 06:12:16 +00:00
return ` <div class="ajax-progress ajax-progress-throbber"> ${ throbber } ${ messageMarkup } </div> ` ;
2018-06-15 16:11:46 +00:00
} ;
2022-09-09 06:26:42 +00:00
/ * *
* An animated progress throbber and container element for AJAX operations .
*
* @ return { string }
* The HTML markup for the throbber .
* /
Drupal . theme . ajaxProgressIndicatorFullscreen = ( ) =>
'<div class="ajax-progress ajax-progress-fullscreen"> </div>' ;
/ * *
* Formats text accompanying the AJAX progress throbber .
*
* @ param { string } message
* The message shown on the UI .
* @ return { string }
* The HTML markup for the throbber .
* /
Drupal . theme . ajaxProgressMessage = ( message ) =>
` <div class="message"> ${ message } </div> ` ;
/ * *
* Provide a wrapper for the AJAX progress bar element .
*
* @ param { jQuery } $element
* Progress bar element .
* @ return { string }
* The HTML markup for the progress bar .
* /
Drupal . theme . ajaxProgressBar = ( $element ) =>
$ ( '<div class="ajax-progress ajax-progress-bar"></div>' ) . append ( $element ) ;
/ * *
* Sets the progress bar progress indicator .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . setProgressIndicatorBar = function ( ) {
2022-09-09 06:26:42 +00:00
const progressBar = new Drupal . ProgressBar (
` ajax-progress- ${ this . element . id } ` ,
$ . noop ,
this . progress . method ,
$ . noop ,
) ;
2015-05-20 11:02:57 +00:00
if ( this . progress . message ) {
progressBar . setProgress ( - 1 , this . progress . message ) ;
}
if ( this . progress . url ) {
2022-09-09 06:26:42 +00:00
progressBar . startMonitoring (
this . progress . url ,
this . progress . interval || 1500 ,
) ;
2014-08-04 10:51:12 +00:00
}
2022-09-09 06:26:42 +00:00
this . progress . element = $ (
Drupal . theme ( 'ajaxProgressBar' , progressBar . element ) ,
) ;
2015-05-20 11:02:57 +00:00
this . progress . object = progressBar ;
$ ( this . element ) . after ( this . progress . element ) ;
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Sets the throbber progress indicator .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . setProgressIndicatorThrobber = function ( ) {
2022-09-09 06:26:42 +00:00
this . progress . element = $ (
Drupal . theme ( 'ajaxProgressThrobber' , this . progress . message ) ,
) ;
Issue #3087950 by Utkarsh_33, swatichouhan012, komalk, Sivaji_Ganesh_Jojodae, Therapychild, Sakthivel M, lauriii, thalles, shalinigaur, kostyashupenko, fhaeberle, amjad1233, sibustephen, pankaj.singh, KondratievaS, pranav.aeer, hooroomoo, ckrina, huzooka: Progress throbber position is incorrect
2023-05-10 11:32:46 +00:00
if ( $ ( this . element ) . closest ( '[data-drupal-ajax-container]' ) . length ) {
$ ( this . element )
. closest ( '[data-drupal-ajax-container]' )
. after ( this . progress . element ) ;
} else {
$ ( this . element ) . after ( this . progress . element ) ;
}
2015-05-20 11:02:57 +00:00
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Sets the fullscreen progress indicator .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . setProgressIndicatorFullscreen = function ( ) {
2018-06-15 16:11:46 +00:00
this . progress . element = $ ( Drupal . theme ( 'ajaxProgressIndicatorFullscreen' ) ) ;
2019-07-23 12:02:26 +00:00
$ ( 'body' ) . append ( this . progress . element ) ;
2014-01-27 21:41:32 +00:00
} ;
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Helper method to make sure commands are executed in sequence .
*
* @ param { Array . < Drupal . AjaxCommands ~ commandDefinition > } response
* Drupal Ajax response .
* @ param { number } status
* XMLHttpRequest status .
*
* @ return { Promise }
* The promise that will resolve once all commands have finished executing .
* /
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
Drupal . Ajax . prototype . commandExecutionQueue = function ( response , status ) {
const ajaxCommands = this . commands ;
2022-09-09 06:26:42 +00:00
return Object . keys ( response || { } ) . reduce (
// Add all commands to a single execution queue.
( executionQueue , key ) =>
executionQueue . then ( ( ) => {
const { command } = response [ key ] ;
if ( command && ajaxCommands [ command ] ) {
// When a command returns a promise, the remaining commands will not
// execute until that promise has been fulfilled. This is typically
// used to ensure JavaScript files added via the 'add_js' command
// have loaded before subsequent commands execute.
return ajaxCommands [ command ] ( this , response [ key ] , status ) ;
}
} ) ,
Promise . resolve ( ) ,
) ;
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
} ;
2022-09-09 06:26:42 +00:00
/ * *
* Handler for the form redirection completion .
*
* @ param { Array . < Drupal . AjaxCommands ~ commandDefinition > } response
* Drupal Ajax response .
* @ param { number } status
* XMLHttpRequest status .
*
* @ return { Promise }
* The promise that will resolve once all commands have finished executing .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . success = function ( response , status ) {
2022-09-09 06:26:42 +00:00
// Remove the progress element.
2014-01-27 21:41:32 +00:00
if ( this . progress . element ) {
$ ( this . progress . element ) . remove ( ) ;
}
if ( this . progress . object ) {
this . progress . object . stopMonitoring ( ) ;
}
2020-01-30 09:08:38 +00:00
$ ( this . element ) . prop ( 'disabled' , false ) ;
2015-11-12 16:06:34 +00:00
2022-09-09 06:26:42 +00:00
// Save element's ancestors tree so if the element is removed from the dom
// we can try to refocus one of its parents. Using addBack reverse the
// result array, meaning that index 0 is the highest parent in the hierarchy
// in this situation it is usually a <form> element.
const elementParents = $ ( this . element )
. parents ( '[data-drupal-selector]' )
. addBack ( )
. toArray ( ) ;
// Track if any command is altering the focus so we can avoid changing the
// focus set by the Ajax command.
const focusChanged = Object . keys ( response || { } ) . some ( ( key ) => {
const { command , method } = response [ key ] ;
return (
2023-12-08 08:22:45 +00:00
command === 'focusFirst' ||
command === 'openDialog' ||
( command === 'invoke' && method === 'focus' )
2022-09-09 06:26:42 +00:00
) ;
} ) ;
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
return (
this . commandExecutionQueue ( response , status )
// If the focus hasn't been changed by the AJAX commands, try to refocus
// the triggering element or one of its parents if that element does not
// exist anymore.
. then ( ( ) => {
Issue #2627788 by lauriii, DuaelFr, Pancho, vsujeetkumar, _utsavsharma, KapilV, facine, Danny_Joris, alexpott, juampynr, tim.plunkett: Focus state bug on text field AJAX calls
2023-10-16 14:37:43 +00:00
if ( ! focusChanged ) {
2022-09-09 06:26:42 +00:00
let target = false ;
Issue #2627788 by lauriii, DuaelFr, Pancho, vsujeetkumar, _utsavsharma, KapilV, facine, Danny_Joris, alexpott, juampynr, tim.plunkett: Focus state bug on text field AJAX calls
2023-10-16 14:37:43 +00:00
if ( this . element ) {
if (
$ ( this . element ) . data ( 'refocus-blur' ) &&
this . preCommandsFocusedElementSelector
) {
target = document . querySelector (
` [data-drupal-selector=" ${ this . preCommandsFocusedElementSelector } "] ` ,
) ;
}
if ( ! target && ! $ ( this . element ) . data ( 'disable-refocus' ) ) {
for (
let n = elementParents . length - 1 ;
! target && n >= 0 ;
n --
) {
target = document . querySelector (
` [data-drupal-selector=" ${ elementParents [ n ] . getAttribute (
'data-drupal-selector' ,
) } " ] ` ,
) ;
}
}
2022-09-09 06:26:42 +00:00
}
if ( target ) {
$ ( target ) . trigger ( 'focus' ) ;
}
}
// Reattach behaviors, if they were detached in beforeSerialize(). The
// attachBehaviors() called on the new content from processing the
// response commands is not sufficient, because behaviors from the
// entire form need to be reattached.
if ( this . $form && document . body . contains ( this . $form . get ( 0 ) ) ) {
const settings = this . settings || drupalSettings ;
Drupal . attachBehaviors ( this . $form . get ( 0 ) , settings ) ;
}
// Remove any response-specific settings so they don't get used on the
// next call by mistake.
this . settings = null ;
} )
. catch ( ( error ) =>
// eslint-disable-next-line no-console
console . error (
Drupal . t (
'An error occurred during the execution of the Ajax response: !error' ,
{
'!error' : error ,
} ,
) ,
) ,
)
) ;
2014-01-27 21:41:32 +00:00
} ;
2009-08-17 07:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Build an effect object to apply an effect when adding new HTML .
*
* @ param { object } response
* Drupal Ajax response .
* @ param { string } [ response . effect ]
* Override the default value of { @ link Drupal . Ajax # elementSettings } .
* @ param { string | number } [ response . speed ]
* Override the default value of { @ link Drupal . Ajax # elementSettings } .
*
* @ return { object }
* Returns an object with ` showEffect ` , ` hideEffect ` and ` showSpeed `
* properties .
* /
2015-05-20 11:02:57 +00:00
Drupal . Ajax . prototype . getEffect = function ( response ) {
2021-12-18 06:12:16 +00:00
const type = response . effect || this . effect ;
const speed = response . speed || this . speed ;
2020-01-30 09:08:38 +00:00
2022-09-09 06:26:42 +00:00
const effect = { } ;
2014-01-27 21:41:32 +00:00
if ( type === 'none' ) {
effect . showEffect = 'show' ;
effect . hideEffect = 'hide' ;
effect . showSpeed = '' ;
2017-05-19 22:12:53 +00:00
} else if ( type === 'fade' ) {
2014-01-27 21:41:32 +00:00
effect . showEffect = 'fadeIn' ;
effect . hideEffect = 'fadeOut' ;
effect . showSpeed = speed ;
2017-05-19 22:12:53 +00:00
} else {
2021-12-18 06:12:16 +00:00
effect . showEffect = ` ${ type } Toggle ` ;
effect . hideEffect = ` ${ type } Toggle ` ;
2014-01-27 21:41:32 +00:00
effect . showSpeed = speed ;
}
return effect ;
} ;
2010-11-29 03:00:50 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Handler for the form redirection error .
*
* @ param { object } xmlhttprequest
* Native XMLHttpRequest object .
* @ param { string } uri
* Ajax Request URI .
* @ param { string } [ customMessage ]
* Extra message to print with the Ajax error .
* /
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
Drupal . Ajax . prototype . error = function ( xmlhttprequest , uri , customMessage ) {
2022-09-09 06:26:42 +00:00
// Remove the progress element.
2014-01-27 21:41:32 +00:00
if ( this . progress . element ) {
$ ( this . progress . element ) . remove ( ) ;
}
if ( this . progress . object ) {
this . progress . object . stopMonitoring ( ) ;
}
2022-09-09 06:26:42 +00:00
// Undo hide.
2014-01-27 21:41:32 +00:00
$ ( this . wrapper ) . show ( ) ;
2022-09-09 06:26:42 +00:00
// Re-enable the element.
2015-03-14 04:02:53 +00:00
$ ( this . element ) . prop ( 'disabled' , false ) ;
2022-09-09 06:26:42 +00:00
// Reattach behaviors, if they were detached in beforeSerialize(), and the
// form is still part of the document.
2019-02-07 16:45:01 +00:00
if ( this . $form && document . body . contains ( this . $form . get ( 0 ) ) ) {
2021-12-18 06:12:16 +00:00
const settings = this . settings || drupalSettings ;
2014-01-27 21:41:32 +00:00
Drupal . attachBehaviors ( this . $form . get ( 0 ) , settings ) ;
}
Issue #2554219 by nod_, larowlan, tim.plunkett, regilero, droplet, japerry, samuel.mortenson, Pere Orga, effulgentsia, benjy, Gábor Hojtsy, greggles, Wim Leers, David_Rothstein, pwolanin, neclimdul, EclipseGc, znerol: Port Cross-site Scripting - Ajax system fixes from SA-CORE-2015-003 to Drupal 8
2015-08-26 22:02:58 +00:00
throw new Drupal . AjaxError ( xmlhttprequest , uri , customMessage ) ;
2014-01-27 21:41:32 +00:00
} ;
2012-07-07 20:52:10 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Provide a wrapper for new content via Ajax .
*
* Wrap the inserted markup when inserting multiple root elements with an
* ajax effect .
*
* @ param { jQuery } $newContent
* Response elements after parsing .
* @ param { Drupal . Ajax } ajax
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
*
* @ deprecated in drupal : 8.6 . 0 and is removed from drupal : 10.0 . 0.
* Use data with desired wrapper .
*
* @ see https : //www.drupal.org/node/2940704
*
* @ todo Add deprecation warning after it is possible . For more information
* see : https : //www.drupal.org/project/drupal/issues/2973400
* /
Drupal . theme . ajaxWrapperNewContent = ( $newContent , ajax , response ) =>
( response . effect || ajax . effect ) !== 'none' &&
$newContent . filter (
( i ) =>
! (
// We can not consider HTML comments or whitespace text as separate
// roots, since they do not cause visual regression with effect.
(
$newContent [ i ] . nodeName === '#comment' ||
( $newContent [ i ] . nodeName === '#text' &&
/^(\s|\n|\r)*$/ . test ( $newContent [ i ] . textContent ) )
)
) ,
) . length > 1
? Drupal . theme ( 'ajaxWrapperMultipleRootElements' , $newContent )
: $newContent ;
/ * *
* Provide a wrapper for multiple root elements via Ajax .
*
* @ param { jQuery } $elements
* Response elements after parsing .
*
* @ deprecated in drupal : 8.6 . 0 and is removed from drupal : 10.0 . 0.
* Use data with desired wrapper .
*
* @ see https : //www.drupal.org/node/2940704
*
* @ todo Add deprecation warning after it is possible . For more information
* see : https : //www.drupal.org/project/drupal/issues/2973400
* /
Drupal . theme . ajaxWrapperMultipleRootElements = ( $elements ) =>
$ ( '<div></div>' ) . append ( $elements ) ;
/ * *
* @ typedef { object } Drupal . AjaxCommands ~ commandDefinition
*
* @ prop { string } command
* @ prop { string } [ method ]
* @ prop { string } [ selector ]
* @ prop { string } [ data ]
* @ prop { object } [ settings ]
2022-11-28 11:30:17 +00:00
* @ prop { boolean } [ asterisk ]
2022-09-09 06:26:42 +00:00
* @ prop { string } [ text ]
* @ prop { string } [ title ]
* @ prop { string } [ url ]
* @ prop { object } [ argument ]
* @ prop { string } [ name ]
* @ prop { string } [ value ]
* @ prop { string } [ old ]
* @ prop { string } [ new ]
2022-11-28 11:30:17 +00:00
* @ prop { boolean } [ merge ]
2022-09-09 06:26:42 +00:00
* @ prop { Array } [ args ]
*
* @ see Drupal . AjaxCommands
* /
/ * *
* Provide a series of commands that the client will perform .
*
* @ constructor
* /
2014-01-27 21:41:32 +00:00
Drupal . AjaxCommands = function ( ) { } ;
Drupal . AjaxCommands . prototype = {
2022-09-09 06:26:42 +00:00
/ * *
* Command to insert new content into the DOM .
*
* @ param { Drupal . Ajax } ajax
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . data
* The data to use with the jQuery method .
* @ param { string } [ response . method ]
* The jQuery DOM manipulation method to be used .
* @ param { string } [ response . selector ]
* An optional jQuery selector string .
* @ param { object } [ response . settings ]
* An optional array of settings that will be used .
* /
2021-12-18 06:12:16 +00:00
insert ( ajax , response ) {
2022-09-09 06:26:42 +00:00
// Get information from the response. If it is not there, default to
// our presets.
const $wrapper = response . selector
? $ ( response . selector )
: $ ( ajax . wrapper ) ;
2021-12-18 06:12:16 +00:00
const method = response . method || ajax . method ;
const effect = ajax . getEffect ( response ) ;
2022-09-09 06:26:42 +00:00
// Apply any settings from the returned JSON if available.
2021-12-18 06:12:16 +00:00
const settings = response . settings || ajax . settings || drupalSettings ;
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Parse response.data into an element collection.
let $newContent = $ ( $ . parseHTML ( response . data , document , true ) ) ;
// For backward compatibility, in some cases a wrapper will be added. This
// behavior will be removed before Drupal 9.0.0. If different behavior is
// needed, the theme functions can be overridden.
// @see https://www.drupal.org/node/2940704
$newContent = Drupal . theme (
'ajaxWrapperNewContent' ,
$newContent ,
ajax ,
response ,
) ;
// If removing content from the wrapper, detach behaviors first.
2014-01-27 21:41:32 +00:00
switch ( method ) {
case 'html' :
case 'replaceWith' :
case 'replaceAll' :
case 'empty' :
case 'remove' :
2016-02-25 06:10:27 +00:00
Drupal . detachBehaviors ( $wrapper . get ( 0 ) , settings ) ;
Issue #736066 by droplet, drpal, vaplas, dmsmidt, effulgentsia, nod_, tic2000, tim.plunkett, lauriii, casey, morsok, idebr, alexpott, yched, tedbow, GuyPaddock, drintios, edurenye, dtamajon, adinac, VinayLondhe, ajalan065, hrmoller, ericduran, piyuesh23, andreyks, roborew, Wim Leers, lokapujya, samuel.mortenson, Dries, DuaelFr, OnkelTem, manu manu, khiminrm, Greg Boggs, SKAUGHT, itsekhmistro, fubhy, SpadXIII, s.messaris, zviryatko: ajax.js insert command sometimes wraps content in a div, potentially producing invalid HTML and other bugs
2018-07-04 18:59:57 +00:00
break ;
default :
break ;
2014-01-27 21:41:32 +00:00
}
2022-09-09 06:26:42 +00:00
// Add the new content to the page.
2017-12-03 16:16:29 +00:00
$wrapper [ method ] ( $newContent ) ;
2014-01-27 21:41:32 +00:00
2022-09-09 06:26:42 +00:00
// Immediately hide the new content if we're using any effects.
2014-01-27 21:41:32 +00:00
if ( effect . showEffect !== 'show' ) {
2017-12-03 16:16:29 +00:00
$newContent . hide ( ) ;
2014-01-27 21:41:32 +00:00
}
2022-09-09 06:26:42 +00:00
// Determine which effect to use and what content will receive the
// effect, then show the new content.
2021-12-18 06:12:16 +00:00
const $ajaxNewContent = $newContent . find ( '.ajax-new-content' ) ;
Issue #736066 by droplet, drpal, vaplas, dmsmidt, effulgentsia, nod_, tic2000, tim.plunkett, lauriii, casey, morsok, idebr, alexpott, yched, tedbow, GuyPaddock, drintios, edurenye, dtamajon, adinac, VinayLondhe, ajalan065, hrmoller, ericduran, piyuesh23, andreyks, roborew, Wim Leers, lokapujya, samuel.mortenson, Dries, DuaelFr, OnkelTem, manu manu, khiminrm, Greg Boggs, SKAUGHT, itsekhmistro, fubhy, SpadXIII, s.messaris, zviryatko: ajax.js insert command sometimes wraps content in a div, potentially producing invalid HTML and other bugs
2018-07-04 18:59:57 +00:00
if ( $ajaxNewContent . length ) {
$ajaxNewContent . hide ( ) ;
2017-12-03 16:16:29 +00:00
$newContent . show ( ) ;
Issue #736066 by droplet, drpal, vaplas, dmsmidt, effulgentsia, nod_, tic2000, tim.plunkett, lauriii, casey, morsok, idebr, alexpott, yched, tedbow, GuyPaddock, drintios, edurenye, dtamajon, adinac, VinayLondhe, ajalan065, hrmoller, ericduran, piyuesh23, andreyks, roborew, Wim Leers, lokapujya, samuel.mortenson, Dries, DuaelFr, OnkelTem, manu manu, khiminrm, Greg Boggs, SKAUGHT, itsekhmistro, fubhy, SpadXIII, s.messaris, zviryatko: ajax.js insert command sometimes wraps content in a div, potentially producing invalid HTML and other bugs
2018-07-04 18:59:57 +00:00
$ajaxNewContent [ effect . showEffect ] ( effect . showSpeed ) ;
2017-05-19 22:12:53 +00:00
} else if ( effect . showEffect !== 'show' ) {
2017-12-03 16:16:29 +00:00
$newContent [ effect . showEffect ] ( effect . showSpeed ) ;
2014-01-27 21:41:32 +00:00
}
2022-09-09 06:26:42 +00:00
// 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.
Issue #736066 by droplet, drpal, vaplas, dmsmidt, effulgentsia, nod_, tic2000, tim.plunkett, lauriii, casey, morsok, idebr, alexpott, yched, tedbow, GuyPaddock, drintios, edurenye, dtamajon, adinac, VinayLondhe, ajalan065, hrmoller, ericduran, piyuesh23, andreyks, roborew, Wim Leers, lokapujya, samuel.mortenson, Dries, DuaelFr, OnkelTem, manu manu, khiminrm, Greg Boggs, SKAUGHT, itsekhmistro, fubhy, SpadXIII, s.messaris, zviryatko: ajax.js insert command sometimes wraps content in a div, potentially producing invalid HTML and other bugs
2018-07-04 18:59:57 +00:00
if ( $newContent . parents ( 'html' ) . length ) {
2022-09-09 06:26:42 +00:00
// Attach behaviors to all element nodes.
2021-12-18 06:12:16 +00:00
$newContent . each ( ( index , element ) => {
Issue #736066 by droplet, drpal, vaplas, dmsmidt, effulgentsia, nod_, tic2000, tim.plunkett, lauriii, casey, morsok, idebr, alexpott, yched, tedbow, GuyPaddock, drintios, edurenye, dtamajon, adinac, VinayLondhe, ajalan065, hrmoller, ericduran, piyuesh23, andreyks, roborew, Wim Leers, lokapujya, samuel.mortenson, Dries, DuaelFr, OnkelTem, manu manu, khiminrm, Greg Boggs, SKAUGHT, itsekhmistro, fubhy, SpadXIII, s.messaris, zviryatko: ajax.js insert command sometimes wraps content in a div, potentially producing invalid HTML and other bugs
2018-07-04 18:59:57 +00:00
if ( element . nodeType === Node . ELEMENT _NODE ) {
Drupal . attachBehaviors ( element , settings ) ;
}
} ) ;
2014-01-27 21:41:32 +00:00
}
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to remove a chunk from the page .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . selector
* A jQuery selector string .
* @ param { object } [ response . settings ]
* An optional array of settings that will be used .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
remove ( ajax , response , status ) {
const settings = response . settings || ajax . settings || drupalSettings ;
2022-09-09 06:26:42 +00:00
$ ( response . selector )
. each ( function ( ) {
Drupal . detachBehaviors ( this , settings ) ;
} )
. remove ( ) ;
2014-01-27 21:41:32 +00:00
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to mark a chunk changed .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The JSON response object from the Ajax request .
* @ param { string } response . selector
* A jQuery selector string .
2022-11-28 11:30:17 +00:00
* @ param { boolean } [ response . asterisk ]
2022-09-09 06:26:42 +00:00
* An optional CSS selector . If specified , an asterisk will be
* appended to the HTML inside the provided selector .
* @ param { number } [ status ]
* The request status .
* /
2021-12-18 06:12:16 +00:00
changed ( ajax , response , status ) {
const $element = $ ( response . selector ) ;
2016-02-25 06:10:27 +00:00
if ( ! $element . hasClass ( 'ajax-changed' ) ) {
$element . addClass ( 'ajax-changed' ) ;
2014-01-27 21:41:32 +00:00
if ( response . asterisk ) {
2022-09-09 06:26:42 +00:00
$element
. find ( response . asterisk )
. append (
` <abbr class="ajax-changed" title=" ${ Drupal . t (
'Changed' ,
) } " > * < / a b b r > ` ,
) ;
2014-01-27 21:41:32 +00:00
}
}
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to provide an alert .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The JSON response from the Ajax request .
* @ param { string } response . text
* The text that will be displayed in an alert dialog .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
alert ( ajax , response , status ) {
2020-09-09 09:10:54 +00:00
window . alert ( response . text ) ;
2014-01-27 21:41:32 +00:00
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to provide triggers audio UAs to read the supplied text .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The JSON response from the Ajax request .
* @ param { string } [ response . text ]
* The text that will be read .
* @ param { string } [ response . priority ]
* An optional priority that will be used for the announcement .
* /
2021-12-18 06:12:16 +00:00
announce ( ajax , response ) {
2019-02-05 22:20:19 +00:00
if ( response . priority ) {
Drupal . announce ( response . text , response . priority ) ;
} else {
Drupal . announce ( response . text ) ;
}
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to set the window . location , redirecting the browser .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . url
* The URL to redirect to .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
redirect ( ajax , response , status ) {
2014-01-27 21:41:32 +00:00
window . location = response . url ;
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to provide the jQuery css ( ) function .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . selector
* A jQuery selector string .
* @ param { object } response . argument
* An array of key / value pairs to set in the CSS for the selector .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
css ( ajax , response , status ) {
2023-06-26 20:26:47 +00:00
// eslint-disable-next-line jquery/no-css
2014-01-27 21:41:32 +00:00
$ ( response . selector ) . css ( response . argument ) ;
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to set the settings used for other commands in this response .
*
* This method will also remove expired ` drupalSettings.ajax ` settings .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
2022-11-28 11:30:17 +00:00
* @ param { boolean } response . merge
2022-09-09 06:26:42 +00:00
* Determines whether the additional settings should be merged to the
* global settings .
* @ param { object } response . settings
* Contains additional settings to add to the global settings .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
settings ( ajax , response , status ) {
const ajaxSettings = drupalSettings . ajax ;
2016-02-22 02:20:13 +00:00
2022-09-09 06:26:42 +00:00
// Clean up drupalSettings.ajax.
2016-05-11 21:00:34 +00:00
if ( ajaxSettings ) {
2022-09-09 06:26:42 +00:00
Drupal . ajax . expired ( ) . forEach ( ( instance ) => {
// If the Ajax object has been created through drupalSettings.ajax
// it will have a selector. When there is no selector the object
// has been initialized with a special class name picked up by the
// Ajax behavior.
2016-05-11 21:00:34 +00:00
if ( instance . selector ) {
2021-12-18 06:12:16 +00:00
const selector = instance . selector . replace ( '#' , '' ) ;
2016-05-11 21:00:34 +00:00
if ( selector in ajaxSettings ) {
delete ajaxSettings [ selector ] ;
}
2016-02-22 02:20:13 +00:00
}
2016-05-11 21:00:34 +00:00
} ) ;
}
2016-02-22 02:20:13 +00:00
2014-01-27 21:41:32 +00:00
if ( response . merge ) {
$ . extend ( true , drupalSettings , response . settings ) ;
2017-05-19 22:12:53 +00:00
} else {
2014-01-27 21:41:32 +00:00
ajax . settings = response . settings ;
}
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to attach data using jQuery ' s data API .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . name
* The name or key ( in the key value pair ) of the data attached to this
* selector .
* @ param { string } response . selector
* A jQuery selector string .
* @ param { string | object } response . value
* The value of to be attached .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
data ( ajax , response , status ) {
2014-01-27 21:41:32 +00:00
$ ( response . selector ) . data ( response . name , response . value ) ;
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to focus the first tabbable element within a container .
*
* If no tabbable elements are found and the container is focusable , then
* focus will move to that container .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . selector
* A query selector string of the container to focus within .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
focusFirst ( ajax , response , status ) {
let focusChanged = false ;
const container = document . querySelector ( response . selector ) ;
2021-04-13 16:25:52 +00:00
if ( container ) {
2022-09-09 06:26:42 +00:00
// Find all tabbable elements within the container.
2021-12-18 06:12:16 +00:00
const tabbableElements = tabbable ( container ) ;
2021-04-13 16:25:52 +00:00
2022-09-09 06:26:42 +00:00
// Move focus to the first tabbable item found.
2021-04-13 16:25:52 +00:00
if ( tabbableElements . length ) {
tabbableElements [ 0 ] . focus ( ) ;
focusChanged = true ;
} else if ( isFocusable ( container ) ) {
2022-09-09 06:26:42 +00:00
// If no tabbable elements are found, but the container is focusable,
// move focus to the container.
2021-04-13 16:25:52 +00:00
container . focus ( ) ;
focusChanged = true ;
}
}
2022-09-09 06:26:42 +00:00
// If no items were available to receive focus, return focus to the
// triggering element.
2021-04-13 16:25:52 +00:00
if ( ajax . hasOwnProperty ( 'element' ) && ! focusChanged ) {
ajax . element . focus ( ) ;
}
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to apply a jQuery method .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { Array } response . args
* An array of arguments to the jQuery method , if any .
* @ param { string } response . method
* The jQuery method to invoke .
* @ param { string } response . selector
* A jQuery selector string .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
invoke ( ajax , response , status ) {
const $element = $ ( response . selector ) ;
$element [ response . method ] ( ... response . args ) ;
2014-01-27 21:41:32 +00:00
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to restripe a table .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . selector
* A jQuery selector string .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
restripe ( ajax , response , status ) {
2022-09-09 06:26:42 +00:00
// :even and :odd are reversed because jQuery counts from 0 and
// we count from 1, so we're out of sync.
// Match immediate children of the parent element to allow nesting.
$ ( response . selector )
. find ( '> tbody > tr:visible, > tr:visible' )
. removeClass ( 'odd even' )
. filter ( ':even' )
. addClass ( 'odd' )
. end ( )
. filter ( ':odd' )
. addClass ( 'even' ) ;
2014-01-27 21:41:32 +00:00
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to update a form ' s build ID .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . old
* The old form build ID .
* @ param { string } response . new
* The new form build ID .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
update _build _id ( ajax , response , status ) {
2022-09-09 06:26:42 +00:00
document
. querySelectorAll (
` input[name="form_build_id"][value=" ${ response . old } "] ` ,
)
. forEach ( ( item ) => {
item . value = response . new ;
} ) ;
Issue #2242749 by znerol, torotil, rszrama, larowlan, dawehner, penyaskito, tim.plunkett, sun, Damien Tournoud, David_Rothstein, effulgentsia: Fixed Port Form API security fix SA-CORE-2014-002 to Drupal 8.
2014-10-12 10:00:04 +00:00
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to add css .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
2023-03-31 09:19:12 +00:00
* @ param { object [ ] | string } response . data
* An array of styles to be added .
2022-09-09 06:26:42 +00:00
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
2021-12-18 06:12:16 +00:00
add _css ( ajax , response , status ) {
2023-03-31 09:19:12 +00:00
if ( typeof response . data === 'string' ) {
Drupal . deprecationError ( {
message :
'Passing a string to the Drupal.ajax.add_css() method is deprecated in 10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3154948.' ,
} ) ;
$ ( 'head' ) . prepend ( response . data ) ;
return ;
}
const allUniqueBundleIds = response . data . map ( function ( style ) {
const uniqueBundleId = style . href + ajax . instanceIndex ;
2024-01-12 21:25:12 +00:00
// Force file to load as a CSS stylesheet using 'css!' flag.
loadjs ( ` css! ${ style . href } ` , uniqueBundleId , {
2023-03-31 09:19:12 +00:00
before ( path , styleEl ) {
// This allows all attributes to be added, like media.
Object . keys ( style ) . forEach ( ( attributeKey ) => {
styleEl . setAttribute ( attributeKey , style [ attributeKey ] ) ;
} ) ;
} ,
} ) ;
return uniqueBundleId ;
} ) ;
// Returns the promise so that the next AJAX command waits on the
// completion of this one to execute, ensuring the CSS is loaded before
// executing.
return new Promise ( ( resolve , reject ) => {
loadjs . ready ( allUniqueBundleIds , {
success ( ) {
// All CSS files were loaded. Resolve the promise and let the
// remaining commands execute.
resolve ( ) ;
} ,
error ( depsNotFound ) {
const message = Drupal . t (
` The following files could not be loaded: @dependencies ` ,
{ '@dependencies' : depsNotFound . join ( ', ' ) } ,
) ;
reject ( message ) ;
} ,
} ) ;
} ) ;
2019-10-10 09:38:23 +00:00
} ,
2021-12-18 06:12:16 +00:00
2022-09-09 06:26:42 +00:00
/ * *
* Command to add a message to the message area .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { string } response . messageWrapperQuerySelector
* The zone where to add the message . If null , the default will be used .
* @ param { string } response . message
* The message text .
* @ param { string } response . messageOptions
* The options argument for Drupal . Message ( ) . add ( ) .
2022-11-28 11:30:17 +00:00
* @ param { boolean } response . clearPrevious
2022-09-09 06:26:42 +00:00
* If true , clear previous messages .
* /
2021-12-18 06:12:16 +00:00
message ( ajax , response ) {
2022-09-09 06:26:42 +00:00
const messages = new Drupal . Message (
document . querySelector ( response . messageWrapperQuerySelector ) ,
) ;
2019-10-10 09:38:23 +00:00
if ( response . clearPrevious ) {
messages . clear ( ) ;
}
messages . add ( response . message , response . messageOptions ) ;
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
} ,
2022-09-09 06:26:42 +00:00
/ * *
* Command to add JS .
*
* @ param { Drupal . Ajax } [ ajax ]
* { @ link Drupal . Ajax } object created by { @ link Drupal . ajax } .
* @ param { object } response
* The response from the Ajax request .
* @ param { Array } response . data
* An array of objects of script attributes .
* @ param { number } [ status ]
* The XMLHttpRequest status .
* /
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
add _js ( ajax , response , status ) {
const parentEl = document . querySelector ( response . selector || 'body' ) ;
const settings = ajax . settings || drupalSettings ;
2022-09-09 06:26:42 +00:00
const allUniqueBundleIds = response . data . map ( ( script ) => {
// loadjs requires a unique ID, and an AJAX instance's `instanceIndex`
// is guaranteed to be unique.
// @see Drupal.behaviors.AJAX.detach
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
const uniqueBundleId = script . src + ajax . instanceIndex ;
loadjs ( script . src , uniqueBundleId , {
2022-09-09 06:26:42 +00:00
// The default loadjs behavior is to load script with async, in Drupal
// we need to explicitly tell scripts to load async, this is set in
// the before callback below if necessary.
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
async : false ,
before ( path , scriptEl ) {
2022-09-09 06:26:42 +00:00
// This allows all attributes to be added, like defer, async and
// crossorigin.
Object . keys ( script ) . forEach ( ( attributeKey ) => {
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
scriptEl . setAttribute ( attributeKey , script [ attributeKey ] ) ;
} ) ;
2022-09-09 06:26:42 +00:00
// By default, loadjs appends the script to the head. When scripts
// are loaded via AJAX, their location has no impact on
// functionality. But, since non-AJAX loaded scripts can choose
// their parent element, we provide that option here for the sake of
// consistency.
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
parentEl . appendChild ( scriptEl ) ;
2022-09-09 06:26:42 +00:00
// Return false to bypass loadjs' default DOM insertion mechanism.
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
return false ;
2022-09-09 06:26:42 +00:00
} ,
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
} ) ;
return uniqueBundleId ;
} ) ;
2022-09-09 06:26:42 +00:00
// Returns the promise so that the next AJAX command waits on the
// completion of this one to execute, ensuring the JS is loaded before
// executing.
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
return new Promise ( ( resolve , reject ) => {
loadjs . ready ( allUniqueBundleIds , {
success ( ) {
Drupal . attachBehaviors ( parentEl , settings ) ;
2022-09-09 06:26:42 +00:00
// All JS files were loaded and new and old behaviors have
// been attached. Resolve the promise and let the remaining
// commands execute.
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
resolve ( ) ;
} ,
error ( depsNotFound ) {
2022-09-09 06:26:42 +00:00
const message = Drupal . t (
` The following files could not be loaded: @dependencies ` ,
{ '@dependencies' : depsNotFound . join ( ', ' ) } ,
) ;
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
reject ( message ) ;
2022-09-09 06:26:42 +00:00
} ,
Issue #1988968 by nod_, droplet, bnjmnm, viappidu, extect, jansete, olli, martin107, pmagunia, bartlangelaan, Wim Leers, zrpnr, KapilV, yogeshmpawar, Spleshka, Phil Wolstenholme, DuaelFr, agata.guc, alwaysworking, dawid_nawrot, andriic, keithdoyle9, gapple, lauriii, Martijn de Wit, jefuri, JMOmandown, larowlan, rubens.arjr, borisson_, joseph.olstad, jberube, gilgabar, Poindexterous, Aless86, jessebeach, bojanz, phma, aheimlich, heddn, phenaproxima, acbramley, codebymikey, cmlara, das-peter, matthiasm11, acolden, xjm, jrockowitz, pianomansam, clairemistry, John Pitcairn: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
2022-08-03 23:11:14 +00:00
} ) ;
} ) ;
2022-09-09 06:26:42 +00:00
} ,
2023-03-04 07:08:13 +00:00
/ * *
* Command to scroll the page to an html element .
*
* @ param { Drupal . Ajax } [ ajax ]
* A { @ link Drupal . ajax } object .
* @ param { object } response
* Ajax response .
* @ param { string } response . selector
* Selector to use .
* /
scrollTop ( ajax , response ) {
const offset = $ ( response . selector ) . offset ( ) ;
// We can't guarantee that the scrollable object should be
// the body, as the element could be embedded in something
// more complex such as a modal popup. Recurse up the DOM
// and scroll the first element that has a non-zero top.
let scrollTarget = response . selector ;
while ( $ ( scrollTarget ) . scrollTop ( ) === 0 && $ ( scrollTarget ) . parent ( ) ) {
scrollTarget = $ ( scrollTarget ) . parent ( ) ;
}
// Only scroll upward.
if ( offset . top - 10 < $ ( scrollTarget ) . scrollTop ( ) ) {
$ ( scrollTarget ) . animate ( { scrollTop : offset . top - 10 } , 500 ) ;
}
} ,
2014-01-27 21:41:32 +00:00
} ;
2022-12-08 17:20:31 +00:00
/ * *
* Delay jQuery ' s global completion events until after commands have executed .
*
* jQuery triggers the ajaxSuccess , ajaxComplete , and ajaxStop events after
* a successful response is returned and local success and complete events
* are triggered . However , Drupal Ajax responses contain commands that run
* asynchronously in a queue , so the following stops these events from getting
* triggered until after the Promise that executes the command queue is
* resolved .
* /
const stopEvent = ( xhr , settings ) => {
return (
// Only interfere with Drupal's Ajax responses.
xhr . getResponseHeader ( 'X-Drupal-Ajax-Token' ) === '1' &&
// The isInProgress() function might not be defined if the Ajax request
// was initiated without Drupal.ajax() or new Drupal.Ajax().
settings . isInProgress &&
// Until this is false, the Ajax request isn't completely done (the
// response's commands might still be running).
settings . isInProgress ( )
) ;
} ;
$ . extend ( true , $ . event . special , {
ajaxSuccess : {
trigger ( event , xhr , settings ) {
if ( stopEvent ( xhr , settings ) ) {
return false ;
}
} ,
} ,
ajaxComplete : {
trigger ( event , xhr , settings ) {
if ( stopEvent ( xhr , settings ) ) {
// jQuery decrements its internal active ajax counter even when we
// stop the ajaxComplete event, but we don't want that counter
// decremented, because for our purposes this request is still active
// while commands are executing. By incrementing it here, the net
// effect is that it remains unchanged. By remaining above 0, the
// ajaxStop event is also prevented.
$ . active ++ ;
return false ;
}
} ,
} ,
} ) ;
2022-09-09 06:26:42 +00:00
} ) ( jQuery , window , Drupal , drupalSettings , loadjs , window . tabbable ) ;