Backup of db before drupal security update
[yaffs-website] / web / core / misc / machine-name.js
1 /**
2  * @file
3  * Machine name functionality.
4  */
5
6 (function ($, Drupal, drupalSettings) {
7
8   'use strict';
9
10   /**
11    * Attach the machine-readable name form element behavior.
12    *
13    * @type {Drupal~behavior}
14    *
15    * @prop {Drupal~behaviorAttach} attach
16    *   Attaches machine-name behaviors.
17    */
18   Drupal.behaviors.machineName = {
19
20     /**
21      * Attaches the behavior.
22      *
23      * @param {Element} context
24      *   The context for attaching the behavior.
25      * @param {object} settings
26      *   Settings object.
27      * @param {object} settings.machineName
28      *   A list of elements to process, keyed by the HTML ID of the form
29      *   element containing the human-readable value. Each element is an object
30      *   defining the following properties:
31      *   - target: The HTML ID of the machine name form element.
32      *   - suffix: The HTML ID of a container to show the machine name preview
33      *     in (usually a field suffix after the human-readable name
34      *     form element).
35      *   - label: The label to show for the machine name preview.
36      *   - replace_pattern: A regular expression (without modifiers) matching
37      *     disallowed characters in the machine name; e.g., '[^a-z0-9]+'.
38      *   - replace: A character to replace disallowed characters with; e.g.,
39      *     '_' or '-'.
40      *   - standalone: Whether the preview should stay in its own element
41      *     rather than the suffix of the source element.
42      *   - field_prefix: The #field_prefix of the form element.
43      *   - field_suffix: The #field_suffix of the form element.
44      */
45     attach: function (context, settings) {
46       var self = this;
47       var $context = $(context);
48       var timeout = null;
49       var xhr = null;
50
51       function clickEditHandler(e) {
52         var data = e.data;
53         data.$wrapper.removeClass('visually-hidden');
54         data.$target.trigger('focus');
55         data.$suffix.hide();
56         data.$source.off('.machineName');
57       }
58
59       function machineNameHandler(e) {
60         var data = e.data;
61         var options = data.options;
62         var baseValue = $(e.target).val();
63
64         var rx = new RegExp(options.replace_pattern, 'g');
65         var expected = baseValue.toLowerCase().replace(rx, options.replace).substr(0, options.maxlength);
66
67         // Abort the last pending request because the label has changed and it
68         // is no longer valid.
69         if (xhr && xhr.readystate !== 4) {
70           xhr.abort();
71           xhr = null;
72         }
73
74         // Wait 300 milliseconds for Ajax request since the last event to update
75         // the machine name i.e., after the user has stopped typing.
76         if (timeout) {
77           clearTimeout(timeout);
78           timeout = null;
79         }
80         if (baseValue.toLowerCase() !== expected) {
81           timeout = setTimeout(function () {
82             xhr = self.transliterate(baseValue, options).done(function (machine) {
83               self.showMachineName(machine.substr(0, options.maxlength), data);
84             });
85           }, 300);
86         }
87         else {
88           self.showMachineName(expected, data);
89         }
90       }
91
92       Object.keys(settings.machineName).forEach(function (source_id) {
93         var machine = '';
94         var eventData;
95         var options = settings.machineName[source_id];
96
97         var $source = $context.find(source_id).addClass('machine-name-source').once('machine-name');
98         var $target = $context.find(options.target).addClass('machine-name-target');
99         var $suffix = $context.find(options.suffix);
100         var $wrapper = $target.closest('.js-form-item');
101         // All elements have to exist.
102         if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) {
103           return;
104         }
105         // Skip processing upon a form validation error on the machine name.
106         if ($target.hasClass('error')) {
107           return;
108         }
109         // Figure out the maximum length for the machine name.
110         options.maxlength = $target.attr('maxlength');
111         // Hide the form item container of the machine name form element.
112         $wrapper.addClass('visually-hidden');
113         // Determine the initial machine name value. Unless the machine name
114         // form element is disabled or not empty, the initial default value is
115         // based on the human-readable form element value.
116         if ($target.is(':disabled') || $target.val() !== '') {
117           machine = $target.val();
118         }
119         else if ($source.val() !== '') {
120           machine = self.transliterate($source.val(), options);
121         }
122         // Append the machine name preview to the source field.
123         var $preview = $('<span class="machine-name-value">' + options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix + '</span>');
124         $suffix.empty();
125         if (options.label) {
126           $suffix.append('<span class="machine-name-label">' + options.label + ': </span>');
127         }
128         $suffix.append($preview);
129
130         // If the machine name cannot be edited, stop further processing.
131         if ($target.is(':disabled')) {
132           return;
133         }
134
135         eventData = {
136           $source: $source,
137           $target: $target,
138           $suffix: $suffix,
139           $wrapper: $wrapper,
140           $preview: $preview,
141           options: options
142         };
143         // If it is editable, append an edit link.
144         var $link = $('<span class="admin-link"><button type="button" class="link">' + Drupal.t('Edit') + '</button></span>').on('click', eventData, clickEditHandler);
145         $suffix.append($link);
146
147         // Preview the machine name in realtime when the human-readable name
148         // changes, but only if there is no machine name yet; i.e., only upon
149         // initial creation, not when editing.
150         if ($target.val() === '') {
151           $source.on('formUpdated.machineName', eventData, machineNameHandler)
152             // Initialize machine name preview.
153             .trigger('formUpdated.machineName');
154         }
155
156         // Add a listener for an invalid event on the machine name input
157         // to show its container and focus it.
158         $target.on('invalid', eventData, clickEditHandler);
159       });
160     },
161
162     showMachineName: function (machine, data) {
163       var settings = data.options;
164       // Set the machine name to the transliterated value.
165       if (machine !== '') {
166         if (machine !== settings.replace) {
167           data.$target.val(machine);
168           data.$preview.html(settings.field_prefix + Drupal.checkPlain(machine) + settings.field_suffix);
169         }
170         data.$suffix.show();
171       }
172       else {
173         data.$suffix.hide();
174         data.$target.val(machine);
175         data.$preview.empty();
176       }
177     },
178
179     /**
180      * Transliterate a human-readable name to a machine name.
181      *
182      * @param {string} source
183      *   A string to transliterate.
184      * @param {object} settings
185      *   The machine name settings for the corresponding field.
186      * @param {string} settings.replace_pattern
187      *   A regular expression (without modifiers) matching disallowed characters
188      *   in the machine name; e.g., '[^a-z0-9]+'.
189      * @param {string} settings.replace_token
190      *   A token to validate the regular expression.
191      * @param {string} settings.replace
192      *   A character to replace disallowed characters with; e.g., '_' or '-'.
193      * @param {number} settings.maxlength
194      *   The maximum length of the machine name.
195      *
196      * @return {jQuery}
197      *   The transliterated source string.
198      */
199     transliterate: function (source, settings) {
200       return $.get(Drupal.url('machine_name/transliterate'), {
201         text: source,
202         langcode: drupalSettings.langcode,
203         replace_pattern: settings.replace_pattern,
204         replace_token: settings.replace_token,
205         replace: settings.replace,
206         lowercase: true
207       });
208     }
209   };
210
211 })(jQuery, Drupal, drupalSettings);