Chris@0
|
1 /**
|
Chris@0
|
2 * @file
|
Chris@0
|
3 * Defines the Drupal JavaScript API.
|
Chris@0
|
4 */
|
Chris@0
|
5
|
Chris@0
|
6 /**
|
Chris@0
|
7 * A jQuery object, typically the return value from a `$(selector)` call.
|
Chris@0
|
8 *
|
Chris@0
|
9 * Holds an HTMLElement or a collection of HTMLElements.
|
Chris@0
|
10 *
|
Chris@0
|
11 * @typedef {object} jQuery
|
Chris@0
|
12 *
|
Chris@0
|
13 * @prop {number} length=0
|
Chris@0
|
14 * Number of elements contained in the jQuery object.
|
Chris@0
|
15 */
|
Chris@0
|
16
|
Chris@0
|
17 /**
|
Chris@0
|
18 * Variable generated by Drupal that holds all translated strings from PHP.
|
Chris@0
|
19 *
|
Chris@0
|
20 * Content of this variable is automatically created by Drupal when using the
|
Chris@0
|
21 * Interface Translation module. It holds the translation of strings used on
|
Chris@0
|
22 * the page.
|
Chris@0
|
23 *
|
Chris@0
|
24 * This variable is used to pass data from the backend to the frontend. Data
|
Chris@0
|
25 * contained in `drupalSettings` is used during behavior initialization.
|
Chris@0
|
26 *
|
Chris@0
|
27 * @global
|
Chris@0
|
28 *
|
Chris@0
|
29 * @var {object} drupalTranslations
|
Chris@0
|
30 */
|
Chris@0
|
31
|
Chris@0
|
32 /**
|
Chris@0
|
33 * Global Drupal object.
|
Chris@0
|
34 *
|
Chris@0
|
35 * All Drupal JavaScript APIs are contained in this namespace.
|
Chris@0
|
36 *
|
Chris@0
|
37 * @global
|
Chris@0
|
38 *
|
Chris@0
|
39 * @namespace
|
Chris@0
|
40 */
|
Chris@0
|
41 window.Drupal = { behaviors: {}, locale: {} };
|
Chris@0
|
42
|
Chris@0
|
43 // JavaScript should be made compatible with libraries other than jQuery by
|
Chris@0
|
44 // wrapping it in an anonymous closure.
|
Chris@0
|
45 (function (Drupal, drupalSettings, drupalTranslations) {
|
Chris@0
|
46 /**
|
Chris@0
|
47 * Helper to rethrow errors asynchronously.
|
Chris@0
|
48 *
|
Chris@0
|
49 * This way Errors bubbles up outside of the original callstack, making it
|
Chris@0
|
50 * easier to debug errors in the browser.
|
Chris@0
|
51 *
|
Chris@0
|
52 * @param {Error|string} error
|
Chris@0
|
53 * The error to be thrown.
|
Chris@0
|
54 */
|
Chris@0
|
55 Drupal.throwError = function (error) {
|
Chris@0
|
56 setTimeout(() => {
|
Chris@0
|
57 throw error;
|
Chris@0
|
58 }, 0);
|
Chris@0
|
59 };
|
Chris@0
|
60
|
Chris@0
|
61 /**
|
Chris@0
|
62 * Custom error thrown after attach/detach if one or more behaviors failed.
|
Chris@0
|
63 * Initializes the JavaScript behaviors for page loads and Ajax requests.
|
Chris@0
|
64 *
|
Chris@0
|
65 * @callback Drupal~behaviorAttach
|
Chris@0
|
66 *
|
Chris@0
|
67 * @param {HTMLDocument|HTMLElement} context
|
Chris@0
|
68 * An element to detach behaviors from.
|
Chris@0
|
69 * @param {?object} settings
|
Chris@0
|
70 * An object containing settings for the current context. It is rarely used.
|
Chris@0
|
71 *
|
Chris@0
|
72 * @see Drupal.attachBehaviors
|
Chris@0
|
73 */
|
Chris@0
|
74
|
Chris@0
|
75 /**
|
Chris@0
|
76 * Reverts and cleans up JavaScript behavior initialization.
|
Chris@0
|
77 *
|
Chris@0
|
78 * @callback Drupal~behaviorDetach
|
Chris@0
|
79 *
|
Chris@0
|
80 * @param {HTMLDocument|HTMLElement} context
|
Chris@0
|
81 * An element to attach behaviors to.
|
Chris@0
|
82 * @param {object} settings
|
Chris@0
|
83 * An object containing settings for the current context.
|
Chris@0
|
84 * @param {string} trigger
|
Chris@0
|
85 * One of `'unload'`, `'move'`, or `'serialize'`.
|
Chris@0
|
86 *
|
Chris@0
|
87 * @see Drupal.detachBehaviors
|
Chris@0
|
88 */
|
Chris@0
|
89
|
Chris@0
|
90 /**
|
Chris@0
|
91 * @typedef {object} Drupal~behavior
|
Chris@0
|
92 *
|
Chris@0
|
93 * @prop {Drupal~behaviorAttach} attach
|
Chris@0
|
94 * Function run on page load and after an Ajax call.
|
Chris@0
|
95 * @prop {Drupal~behaviorDetach} detach
|
Chris@0
|
96 * Function run when content is serialized or removed from the page.
|
Chris@0
|
97 */
|
Chris@0
|
98
|
Chris@0
|
99 /**
|
Chris@0
|
100 * Holds all initialization methods.
|
Chris@0
|
101 *
|
Chris@0
|
102 * @namespace Drupal.behaviors
|
Chris@0
|
103 *
|
Chris@0
|
104 * @type {Object.<string, Drupal~behavior>}
|
Chris@0
|
105 */
|
Chris@0
|
106
|
Chris@0
|
107 /**
|
Chris@0
|
108 * Defines a behavior to be run during attach and detach phases.
|
Chris@0
|
109 *
|
Chris@0
|
110 * Attaches all registered behaviors to a page element.
|
Chris@0
|
111 *
|
Chris@0
|
112 * Behaviors are event-triggered actions that attach to page elements,
|
Chris@0
|
113 * enhancing default non-JavaScript UIs. Behaviors are registered in the
|
Chris@0
|
114 * {@link Drupal.behaviors} object using the method 'attach' and optionally
|
Chris@0
|
115 * also 'detach'.
|
Chris@0
|
116 *
|
Chris@0
|
117 * {@link Drupal.attachBehaviors} is added below to the `jQuery.ready` event
|
Chris@0
|
118 * and therefore runs on initial page load. Developers implementing Ajax in
|
Chris@0
|
119 * their solutions should also call this function after new page content has
|
Chris@0
|
120 * been loaded, feeding in an element to be processed, in order to attach all
|
Chris@0
|
121 * behaviors to the new content.
|
Chris@0
|
122 *
|
Chris@0
|
123 * Behaviors should use `var elements =
|
Chris@0
|
124 * $(context).find(selector).once('behavior-name');` to ensure the behavior is
|
Chris@0
|
125 * attached only once to a given element. (Doing so enables the reprocessing
|
Chris@0
|
126 * of given elements, which may be needed on occasion despite the ability to
|
Chris@0
|
127 * limit behavior attachment to a particular element.)
|
Chris@0
|
128 *
|
Chris@0
|
129 * @example
|
Chris@0
|
130 * Drupal.behaviors.behaviorName = {
|
Chris@0
|
131 * attach: function (context, settings) {
|
Chris@0
|
132 * // ...
|
Chris@0
|
133 * },
|
Chris@0
|
134 * detach: function (context, settings, trigger) {
|
Chris@0
|
135 * // ...
|
Chris@0
|
136 * }
|
Chris@0
|
137 * };
|
Chris@0
|
138 *
|
Chris@0
|
139 * @param {HTMLDocument|HTMLElement} [context=document]
|
Chris@0
|
140 * An element to attach behaviors to.
|
Chris@0
|
141 * @param {object} [settings=drupalSettings]
|
Chris@0
|
142 * An object containing settings for the current context. If none is given,
|
Chris@0
|
143 * the global {@link drupalSettings} object is used.
|
Chris@0
|
144 *
|
Chris@0
|
145 * @see Drupal~behaviorAttach
|
Chris@0
|
146 * @see Drupal.detachBehaviors
|
Chris@0
|
147 *
|
Chris@0
|
148 * @throws {Drupal~DrupalBehaviorError}
|
Chris@0
|
149 */
|
Chris@0
|
150 Drupal.attachBehaviors = function (context, settings) {
|
Chris@0
|
151 context = context || document;
|
Chris@0
|
152 settings = settings || drupalSettings;
|
Chris@0
|
153 const behaviors = Drupal.behaviors;
|
Chris@0
|
154 // Execute all of them.
|
Chris@0
|
155 Object.keys(behaviors || {}).forEach((i) => {
|
Chris@0
|
156 if (typeof behaviors[i].attach === 'function') {
|
Chris@0
|
157 // Don't stop the execution of behaviors in case of an error.
|
Chris@0
|
158 try {
|
Chris@0
|
159 behaviors[i].attach(context, settings);
|
Chris@0
|
160 }
|
Chris@0
|
161 catch (e) {
|
Chris@0
|
162 Drupal.throwError(e);
|
Chris@0
|
163 }
|
Chris@0
|
164 }
|
Chris@0
|
165 });
|
Chris@0
|
166 };
|
Chris@0
|
167
|
Chris@0
|
168 /**
|
Chris@0
|
169 * Detaches registered behaviors from a page element.
|
Chris@0
|
170 *
|
Chris@0
|
171 * Developers implementing Ajax in their solutions should call this function
|
Chris@0
|
172 * before page content is about to be removed, feeding in an element to be
|
Chris@0
|
173 * processed, in order to allow special behaviors to detach from the content.
|
Chris@0
|
174 *
|
Chris@0
|
175 * Such implementations should use `.findOnce()` and `.removeOnce()` to find
|
Chris@0
|
176 * elements with their corresponding `Drupal.behaviors.behaviorName.attach`
|
Chris@0
|
177 * implementation, i.e. `.removeOnce('behaviorName')`, to ensure the behavior
|
Chris@0
|
178 * is detached only from previously processed elements.
|
Chris@0
|
179 *
|
Chris@0
|
180 * @param {HTMLDocument|HTMLElement} [context=document]
|
Chris@0
|
181 * An element to detach behaviors from.
|
Chris@0
|
182 * @param {object} [settings=drupalSettings]
|
Chris@0
|
183 * An object containing settings for the current context. If none given,
|
Chris@0
|
184 * the global {@link drupalSettings} object is used.
|
Chris@0
|
185 * @param {string} [trigger='unload']
|
Chris@0
|
186 * A string containing what's causing the behaviors to be detached. The
|
Chris@0
|
187 * possible triggers are:
|
Chris@0
|
188 * - `'unload'`: The context element is being removed from the DOM.
|
Chris@0
|
189 * - `'move'`: The element is about to be moved within the DOM (for example,
|
Chris@0
|
190 * during a tabledrag row swap). After the move is completed,
|
Chris@0
|
191 * {@link Drupal.attachBehaviors} is called, so that the behavior can undo
|
Chris@0
|
192 * whatever it did in response to the move. Many behaviors won't need to
|
Chris@0
|
193 * do anything simply in response to the element being moved, but because
|
Chris@0
|
194 * IFRAME elements reload their "src" when being moved within the DOM,
|
Chris@0
|
195 * behaviors bound to IFRAME elements (like WYSIWYG editors) may need to
|
Chris@0
|
196 * take some action.
|
Chris@0
|
197 * - `'serialize'`: When an Ajax form is submitted, this is called with the
|
Chris@0
|
198 * form as the context. This provides every behavior within the form an
|
Chris@0
|
199 * opportunity to ensure that the field elements have correct content
|
Chris@0
|
200 * in them before the form is serialized. The canonical use-case is so
|
Chris@0
|
201 * that WYSIWYG editors can update the hidden textarea to which they are
|
Chris@0
|
202 * bound.
|
Chris@0
|
203 *
|
Chris@0
|
204 * @throws {Drupal~DrupalBehaviorError}
|
Chris@0
|
205 *
|
Chris@0
|
206 * @see Drupal~behaviorDetach
|
Chris@0
|
207 * @see Drupal.attachBehaviors
|
Chris@0
|
208 */
|
Chris@0
|
209 Drupal.detachBehaviors = function (context, settings, trigger) {
|
Chris@0
|
210 context = context || document;
|
Chris@0
|
211 settings = settings || drupalSettings;
|
Chris@0
|
212 trigger = trigger || 'unload';
|
Chris@0
|
213 const behaviors = Drupal.behaviors;
|
Chris@0
|
214 // Execute all of them.
|
Chris@0
|
215 Object.keys(behaviors || {}).forEach((i) => {
|
Chris@0
|
216 if (typeof behaviors[i].detach === 'function') {
|
Chris@0
|
217 // Don't stop the execution of behaviors in case of an error.
|
Chris@0
|
218 try {
|
Chris@0
|
219 behaviors[i].detach(context, settings, trigger);
|
Chris@0
|
220 }
|
Chris@0
|
221 catch (e) {
|
Chris@0
|
222 Drupal.throwError(e);
|
Chris@0
|
223 }
|
Chris@0
|
224 }
|
Chris@0
|
225 });
|
Chris@0
|
226 };
|
Chris@0
|
227
|
Chris@0
|
228 /**
|
Chris@0
|
229 * Encodes special characters in a plain-text string for display as HTML.
|
Chris@0
|
230 *
|
Chris@0
|
231 * @param {string} str
|
Chris@0
|
232 * The string to be encoded.
|
Chris@0
|
233 *
|
Chris@0
|
234 * @return {string}
|
Chris@0
|
235 * The encoded string.
|
Chris@0
|
236 *
|
Chris@0
|
237 * @ingroup sanitization
|
Chris@0
|
238 */
|
Chris@0
|
239 Drupal.checkPlain = function (str) {
|
Chris@0
|
240 str = str.toString()
|
Chris@0
|
241 .replace(/&/g, '&')
|
Chris@0
|
242 .replace(/</g, '<')
|
Chris@0
|
243 .replace(/>/g, '>')
|
Chris@0
|
244 .replace(/"/g, '"')
|
Chris@0
|
245 .replace(/'/g, ''');
|
Chris@0
|
246 return str;
|
Chris@0
|
247 };
|
Chris@0
|
248
|
Chris@0
|
249 /**
|
Chris@0
|
250 * Replaces placeholders with sanitized values in a string.
|
Chris@0
|
251 *
|
Chris@0
|
252 * @param {string} str
|
Chris@0
|
253 * A string with placeholders.
|
Chris@0
|
254 * @param {object} args
|
Chris@0
|
255 * An object of replacements pairs to make. Incidences of any key in this
|
Chris@0
|
256 * array are replaced with the corresponding value. Based on the first
|
Chris@0
|
257 * character of the key, the value is escaped and/or themed:
|
Chris@0
|
258 * - `'!variable'`: inserted as is.
|
Chris@0
|
259 * - `'@variable'`: escape plain text to HTML ({@link Drupal.checkPlain}).
|
Chris@0
|
260 * - `'%variable'`: escape text and theme as a placeholder for user-
|
Chris@0
|
261 * submitted content ({@link Drupal.checkPlain} +
|
Chris@0
|
262 * `{@link Drupal.theme}('placeholder')`).
|
Chris@0
|
263 *
|
Chris@0
|
264 * @return {string}
|
Chris@0
|
265 * The formatted string.
|
Chris@0
|
266 *
|
Chris@0
|
267 * @see Drupal.t
|
Chris@0
|
268 */
|
Chris@0
|
269 Drupal.formatString = function (str, args) {
|
Chris@0
|
270 // Keep args intact.
|
Chris@0
|
271 const processedArgs = {};
|
Chris@0
|
272 // Transform arguments before inserting them.
|
Chris@0
|
273 Object.keys(args || {}).forEach((key) => {
|
Chris@0
|
274 switch (key.charAt(0)) {
|
Chris@0
|
275 // Escaped only.
|
Chris@0
|
276 case '@':
|
Chris@0
|
277 processedArgs[key] = Drupal.checkPlain(args[key]);
|
Chris@0
|
278 break;
|
Chris@0
|
279
|
Chris@0
|
280 // Pass-through.
|
Chris@0
|
281 case '!':
|
Chris@0
|
282 processedArgs[key] = args[key];
|
Chris@0
|
283 break;
|
Chris@0
|
284
|
Chris@0
|
285 // Escaped and placeholder.
|
Chris@0
|
286 default:
|
Chris@0
|
287 processedArgs[key] = Drupal.theme('placeholder', args[key]);
|
Chris@0
|
288 break;
|
Chris@0
|
289 }
|
Chris@0
|
290 });
|
Chris@0
|
291
|
Chris@0
|
292 return Drupal.stringReplace(str, processedArgs, null);
|
Chris@0
|
293 };
|
Chris@0
|
294
|
Chris@0
|
295 /**
|
Chris@0
|
296 * Replaces substring.
|
Chris@0
|
297 *
|
Chris@0
|
298 * The longest keys will be tried first. Once a substring has been replaced,
|
Chris@0
|
299 * its new value will not be searched again.
|
Chris@0
|
300 *
|
Chris@0
|
301 * @param {string} str
|
Chris@0
|
302 * A string with placeholders.
|
Chris@0
|
303 * @param {object} args
|
Chris@0
|
304 * Key-value pairs.
|
Chris@0
|
305 * @param {Array|null} keys
|
Chris@0
|
306 * Array of keys from `args`. Internal use only.
|
Chris@0
|
307 *
|
Chris@0
|
308 * @return {string}
|
Chris@0
|
309 * The replaced string.
|
Chris@0
|
310 */
|
Chris@0
|
311 Drupal.stringReplace = function (str, args, keys) {
|
Chris@0
|
312 if (str.length === 0) {
|
Chris@0
|
313 return str;
|
Chris@0
|
314 }
|
Chris@0
|
315
|
Chris@0
|
316 // If the array of keys is not passed then collect the keys from the args.
|
Chris@0
|
317 if (!Array.isArray(keys)) {
|
Chris@0
|
318 keys = Object.keys(args || {});
|
Chris@0
|
319
|
Chris@0
|
320 // Order the keys by the character length. The shortest one is the first.
|
Chris@0
|
321 keys.sort((a, b) => a.length - b.length);
|
Chris@0
|
322 }
|
Chris@0
|
323
|
Chris@0
|
324 if (keys.length === 0) {
|
Chris@0
|
325 return str;
|
Chris@0
|
326 }
|
Chris@0
|
327
|
Chris@0
|
328 // Take next longest one from the end.
|
Chris@0
|
329 const key = keys.pop();
|
Chris@0
|
330 const fragments = str.split(key);
|
Chris@0
|
331
|
Chris@0
|
332 if (keys.length) {
|
Chris@0
|
333 for (let i = 0; i < fragments.length; i++) {
|
Chris@0
|
334 // Process each fragment with a copy of remaining keys.
|
Chris@0
|
335 fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0));
|
Chris@0
|
336 }
|
Chris@0
|
337 }
|
Chris@0
|
338
|
Chris@0
|
339 return fragments.join(args[key]);
|
Chris@0
|
340 };
|
Chris@0
|
341
|
Chris@0
|
342 /**
|
Chris@0
|
343 * Translates strings to the page language, or a given language.
|
Chris@0
|
344 *
|
Chris@0
|
345 * See the documentation of the server-side t() function for further details.
|
Chris@0
|
346 *
|
Chris@0
|
347 * @param {string} str
|
Chris@0
|
348 * A string containing the English text to translate.
|
Chris@0
|
349 * @param {Object.<string, string>} [args]
|
Chris@0
|
350 * An object of replacements pairs to make after translation. Incidences
|
Chris@0
|
351 * of any key in this array are replaced with the corresponding value.
|
Chris@0
|
352 * See {@link Drupal.formatString}.
|
Chris@0
|
353 * @param {object} [options]
|
Chris@0
|
354 * Additional options for translation.
|
Chris@0
|
355 * @param {string} [options.context='']
|
Chris@0
|
356 * The context the source string belongs to.
|
Chris@0
|
357 *
|
Chris@0
|
358 * @return {string}
|
Chris@0
|
359 * The formatted string.
|
Chris@0
|
360 * The translated string.
|
Chris@0
|
361 */
|
Chris@0
|
362 Drupal.t = function (str, args, options) {
|
Chris@0
|
363 options = options || {};
|
Chris@0
|
364 options.context = options.context || '';
|
Chris@0
|
365
|
Chris@0
|
366 // Fetch the localized version of the string.
|
Chris@0
|
367 if (typeof drupalTranslations !== 'undefined' && drupalTranslations.strings && drupalTranslations.strings[options.context] && drupalTranslations.strings[options.context][str]) {
|
Chris@0
|
368 str = drupalTranslations.strings[options.context][str];
|
Chris@0
|
369 }
|
Chris@0
|
370
|
Chris@0
|
371 if (args) {
|
Chris@0
|
372 str = Drupal.formatString(str, args);
|
Chris@0
|
373 }
|
Chris@0
|
374 return str;
|
Chris@0
|
375 };
|
Chris@0
|
376
|
Chris@0
|
377 /**
|
Chris@0
|
378 * Returns the URL to a Drupal page.
|
Chris@0
|
379 *
|
Chris@0
|
380 * @param {string} path
|
Chris@0
|
381 * Drupal path to transform to URL.
|
Chris@0
|
382 *
|
Chris@0
|
383 * @return {string}
|
Chris@0
|
384 * The full URL.
|
Chris@0
|
385 */
|
Chris@0
|
386 Drupal.url = function (path) {
|
Chris@0
|
387 return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
|
Chris@0
|
388 };
|
Chris@0
|
389
|
Chris@0
|
390 /**
|
Chris@0
|
391 * Returns the passed in URL as an absolute URL.
|
Chris@0
|
392 *
|
Chris@0
|
393 * @param {string} url
|
Chris@0
|
394 * The URL string to be normalized to an absolute URL.
|
Chris@0
|
395 *
|
Chris@0
|
396 * @return {string}
|
Chris@0
|
397 * The normalized, absolute URL.
|
Chris@0
|
398 *
|
Chris@0
|
399 * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js
|
Chris@0
|
400 * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript
|
Chris@0
|
401 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53
|
Chris@0
|
402 */
|
Chris@0
|
403 Drupal.url.toAbsolute = function (url) {
|
Chris@0
|
404 const urlParsingNode = document.createElement('a');
|
Chris@0
|
405
|
Chris@0
|
406 // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8
|
Chris@0
|
407 // strings may throw an exception.
|
Chris@0
|
408 try {
|
Chris@0
|
409 url = decodeURIComponent(url);
|
Chris@0
|
410 }
|
Chris@0
|
411 catch (e) {
|
Chris@0
|
412 // Empty.
|
Chris@0
|
413 }
|
Chris@0
|
414
|
Chris@0
|
415 urlParsingNode.setAttribute('href', url);
|
Chris@0
|
416
|
Chris@0
|
417 // IE <= 7 normalizes the URL when assigned to the anchor node similar to
|
Chris@0
|
418 // the other browsers.
|
Chris@0
|
419 return urlParsingNode.cloneNode(false).href;
|
Chris@0
|
420 };
|
Chris@0
|
421
|
Chris@0
|
422 /**
|
Chris@0
|
423 * Returns true if the URL is within Drupal's base path.
|
Chris@0
|
424 *
|
Chris@0
|
425 * @param {string} url
|
Chris@0
|
426 * The URL string to be tested.
|
Chris@0
|
427 *
|
Chris@0
|
428 * @return {bool}
|
Chris@0
|
429 * `true` if local.
|
Chris@0
|
430 *
|
Chris@0
|
431 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58
|
Chris@0
|
432 */
|
Chris@0
|
433 Drupal.url.isLocal = function (url) {
|
Chris@0
|
434 // Always use browser-derived absolute URLs in the comparison, to avoid
|
Chris@0
|
435 // attempts to break out of the base path using directory traversal.
|
Chris@0
|
436 let absoluteUrl = Drupal.url.toAbsolute(url);
|
Chris@0
|
437 let protocol = location.protocol;
|
Chris@0
|
438
|
Chris@0
|
439 // Consider URLs that match this site's base URL but use HTTPS instead of HTTP
|
Chris@0
|
440 // as local as well.
|
Chris@0
|
441 if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) {
|
Chris@0
|
442 protocol = 'https:';
|
Chris@0
|
443 }
|
Chris@0
|
444 let baseUrl = `${protocol}//${location.host}${drupalSettings.path.baseUrl.slice(0, -1)}`;
|
Chris@0
|
445
|
Chris@0
|
446 // Decoding non-UTF-8 strings may throw an exception.
|
Chris@0
|
447 try {
|
Chris@0
|
448 absoluteUrl = decodeURIComponent(absoluteUrl);
|
Chris@0
|
449 }
|
Chris@0
|
450 catch (e) {
|
Chris@0
|
451 // Empty.
|
Chris@0
|
452 }
|
Chris@0
|
453 try {
|
Chris@0
|
454 baseUrl = decodeURIComponent(baseUrl);
|
Chris@0
|
455 }
|
Chris@0
|
456 catch (e) {
|
Chris@0
|
457 // Empty.
|
Chris@0
|
458 }
|
Chris@0
|
459
|
Chris@0
|
460 // The given URL matches the site's base URL, or has a path under the site's
|
Chris@0
|
461 // base URL.
|
Chris@0
|
462 return absoluteUrl === baseUrl || absoluteUrl.indexOf(`${baseUrl}/`) === 0;
|
Chris@0
|
463 };
|
Chris@0
|
464
|
Chris@0
|
465 /**
|
Chris@0
|
466 * Formats a string containing a count of items.
|
Chris@0
|
467 *
|
Chris@0
|
468 * This function ensures that the string is pluralized correctly. Since
|
Chris@0
|
469 * {@link Drupal.t} is called by this function, make sure not to pass
|
Chris@0
|
470 * already-localized strings to it.
|
Chris@0
|
471 *
|
Chris@0
|
472 * See the documentation of the server-side
|
Chris@0
|
473 * \Drupal\Core\StringTranslation\TranslationInterface::formatPlural()
|
Chris@0
|
474 * function for more details.
|
Chris@0
|
475 *
|
Chris@0
|
476 * @param {number} count
|
Chris@0
|
477 * The item count to display.
|
Chris@0
|
478 * @param {string} singular
|
Chris@0
|
479 * The string for the singular case. Please make sure it is clear this is
|
Chris@0
|
480 * singular, to ease translation (e.g. use "1 new comment" instead of "1
|
Chris@0
|
481 * new"). Do not use @count in the singular string.
|
Chris@0
|
482 * @param {string} plural
|
Chris@0
|
483 * The string for the plural case. Please make sure it is clear this is
|
Chris@0
|
484 * plural, to ease translation. Use @count in place of the item count, as in
|
Chris@0
|
485 * "@count new comments".
|
Chris@0
|
486 * @param {object} [args]
|
Chris@0
|
487 * An object of replacements pairs to make after translation. Incidences
|
Chris@0
|
488 * of any key in this array are replaced with the corresponding value.
|
Chris@0
|
489 * See {@link Drupal.formatString}.
|
Chris@0
|
490 * Note that you do not need to include @count in this array.
|
Chris@0
|
491 * This replacement is done automatically for the plural case.
|
Chris@0
|
492 * @param {object} [options]
|
Chris@0
|
493 * The options to pass to the {@link Drupal.t} function.
|
Chris@0
|
494 *
|
Chris@0
|
495 * @return {string}
|
Chris@0
|
496 * A translated string.
|
Chris@0
|
497 */
|
Chris@0
|
498 Drupal.formatPlural = function (count, singular, plural, args, options) {
|
Chris@0
|
499 args = args || {};
|
Chris@0
|
500 args['@count'] = count;
|
Chris@0
|
501
|
Chris@0
|
502 const pluralDelimiter = drupalSettings.pluralDelimiter;
|
Chris@0
|
503 const translations = Drupal.t(singular + pluralDelimiter + plural, args, options).split(pluralDelimiter);
|
Chris@0
|
504 let index = 0;
|
Chris@0
|
505
|
Chris@0
|
506 // Determine the index of the plural form.
|
Chris@0
|
507 if (typeof drupalTranslations !== 'undefined' && drupalTranslations.pluralFormula) {
|
Chris@0
|
508 index = count in drupalTranslations.pluralFormula ? drupalTranslations.pluralFormula[count] : drupalTranslations.pluralFormula.default;
|
Chris@0
|
509 }
|
Chris@0
|
510 else if (args['@count'] !== 1) {
|
Chris@0
|
511 index = 1;
|
Chris@0
|
512 }
|
Chris@0
|
513
|
Chris@0
|
514 return translations[index];
|
Chris@0
|
515 };
|
Chris@0
|
516
|
Chris@0
|
517 /**
|
Chris@0
|
518 * Encodes a Drupal path for use in a URL.
|
Chris@0
|
519 *
|
Chris@0
|
520 * For aesthetic reasons slashes are not escaped.
|
Chris@0
|
521 *
|
Chris@0
|
522 * @param {string} item
|
Chris@0
|
523 * Unencoded path.
|
Chris@0
|
524 *
|
Chris@0
|
525 * @return {string}
|
Chris@0
|
526 * The encoded path.
|
Chris@0
|
527 */
|
Chris@0
|
528 Drupal.encodePath = function (item) {
|
Chris@0
|
529 return window.encodeURIComponent(item).replace(/%2F/g, '/');
|
Chris@0
|
530 };
|
Chris@0
|
531
|
Chris@0
|
532 /**
|
Chris@0
|
533 * Generates the themed representation of a Drupal object.
|
Chris@0
|
534 *
|
Chris@0
|
535 * All requests for themed output must go through this function. It examines
|
Chris@0
|
536 * the request and routes it to the appropriate theme function. If the current
|
Chris@0
|
537 * theme does not provide an override function, the generic theme function is
|
Chris@0
|
538 * called.
|
Chris@0
|
539 *
|
Chris@0
|
540 * @example
|
Chris@0
|
541 * <caption>To retrieve the HTML for text that should be emphasized and
|
Chris@0
|
542 * displayed as a placeholder inside a sentence.</caption>
|
Chris@0
|
543 * Drupal.theme('placeholder', text);
|
Chris@0
|
544 *
|
Chris@0
|
545 * @namespace
|
Chris@0
|
546 *
|
Chris@0
|
547 * @param {function} func
|
Chris@0
|
548 * The name of the theme function to call.
|
Chris@0
|
549 * @param {...args}
|
Chris@0
|
550 * Additional arguments to pass along to the theme function.
|
Chris@0
|
551 *
|
Chris@0
|
552 * @return {string|object|HTMLElement|jQuery}
|
Chris@0
|
553 * Any data the theme function returns. This could be a plain HTML string,
|
Chris@0
|
554 * but also a complex object.
|
Chris@0
|
555 */
|
Chris@0
|
556 Drupal.theme = function (func, ...args) {
|
Chris@0
|
557 if (func in Drupal.theme) {
|
Chris@0
|
558 return Drupal.theme[func](...args);
|
Chris@0
|
559 }
|
Chris@0
|
560 };
|
Chris@0
|
561
|
Chris@0
|
562 /**
|
Chris@0
|
563 * Formats text for emphasized display in a placeholder inside a sentence.
|
Chris@0
|
564 *
|
Chris@0
|
565 * @param {string} str
|
Chris@0
|
566 * The text to format (plain-text).
|
Chris@0
|
567 *
|
Chris@0
|
568 * @return {string}
|
Chris@0
|
569 * The formatted text (html).
|
Chris@0
|
570 */
|
Chris@0
|
571 Drupal.theme.placeholder = function (str) {
|
Chris@0
|
572 return `<em class="placeholder">${Drupal.checkPlain(str)}</em>`;
|
Chris@0
|
573 };
|
Chris@0
|
574 }(Drupal, window.drupalSettings, window.drupalTranslations));
|