diff --git a/core/misc/machine-name.js b/core/misc/machine-name.js index 2b304bf..28fe7f1 100644 --- a/core/misc/machine-name.js +++ b/core/misc/machine-name.js @@ -38,6 +38,7 @@ * - field_suffix: The #field_suffix of the form element. */ attach: function (context, settings) { +<<<<<<< HEAD var self = this; var $context = $(context); var timeout = null; @@ -49,109 +50,194 @@ data.$target.trigger('focus'); data.$suffix.hide(); data.$source.off('.machineName'); +======= + var sourceId; + + for (sourceId in settings.machineName) { + Drupal.MachineName.names.push(new Drupal.MachineName(sourceId, settings, context)); +>>>>>>> Issue #1686174 by lorique, mikeker, Temoor, Manjit.Singh: Refactor machine-name.js } + } + }; - function machineNameHandler(e) { - var data = e.data; - var options = data.options; - var baseValue = $(e.target).val(); + function MachineName(sourceId, settings, context) { - var rx = new RegExp(options.replace_pattern, 'g'); - var expected = baseValue.toLowerCase().replace(rx, options.replace).substr(0, options.maxlength); + /** + * @var self + * javascript object containing the current Drupal.machineName for later use. + */ + var self = this; - // Abort the last pending request because the label has changed and it - // is no longer valid. - if (xhr && xhr.readystate !== 4) { - xhr.abort(); - xhr = null; - } + /** + * @var options + * An object of options used for retrieving machine name options + * @var machine + * A string containing the current machine name + */ + var options, machine; - // Wait 300 milliseconds since the last event to update the machine name - // i.e., after the user has stopped typing. - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - timeout = setTimeout(function () { - if (baseValue.toLowerCase() !== expected) { - xhr = self.transliterate(baseValue, options).done(function (machine) { - self.showMachineName(machine.substr(0, options.maxlength), data); - }); - } - else { - self.showMachineName(expected, data); - } - }, 300); + // Get options from the settings object using the sourceId + options = settings.machineName[sourceId]; + + var $context = $(context); + + if (settings.machineName.hasOwnProperty(sourceId)) { + // Get eventData object + var eventData = this.eventData(sourceId, options, $context); + + // Figure out the maximum length for the machine name. + options.maxlength = eventData.$target.attr('maxlength'); + // Hide the form item container of the machine name form element. + eventData.$wrapper.hide(); + // Determine the initial machine name value. Unless the machine name form + // element is disabled or not empty, the initial default value is based on + // the human-readable form element value. + if (eventData.$target.is(':disabled') || eventData.$target.val() !== '') { + machine = eventData.$target.val(); } + else { + machine = self.transliterate(eventData.$source.val(), options); + } + // Append the machine name preview to the source field. + var preview_text = options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix; + var $preview = $('' + preview_text + ''); + eventData.$preview = $preview; + eventData.$suffix.empty(); + if (options.label) { + eventData.$suffix.append(' ').append('' + options.label + ':'); + } + eventData.$suffix.append(' ').append(eventData.$preview); - Object.keys(settings.machineName).forEach(function (source_id) { - var machine = ''; - var eventData; - var options = settings.machineName[source_id]; - - var $source = $context.find(source_id).addClass('machine-name-source').once('machine-name'); - var $target = $context.find(options.target).addClass('machine-name-target'); - var $suffix = $context.find(options.suffix); - var $wrapper = $target.closest('.form-item'); - // All elements have to exist. - if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) { - return; - } - // Skip processing upon a form validation error on the machine name. - if ($target.hasClass('error')) { - return; - } - // Figure out the maximum length for the machine name. - options.maxlength = $target.attr('maxlength'); - // Hide the form item container of the machine name form element. - $wrapper.addClass('visually-hidden'); - // Determine the initial machine name value. Unless the machine name - // form element is disabled or not empty, the initial default value is - // based on the human-readable form element value. - if ($target.is(':disabled') || $target.val() !== '') { - machine = $target.val(); - } - else if ($source.val() !== '') { - machine = self.transliterate($source.val(), options); + // If the machine name cannot be edited, stop further processing. + if (eventData.$target.is(':disabled')) { + return; + } + + // If it is editable, append an edit link. + var $link = $('' + Drupal.t('Edit') + '').on('click', eventData, self.clickEditHandler); + eventData.$link = $link; + + eventData.$suffix.append(' ').append(eventData.$link); + + // Preview the machine name in realtime when the human-readable name + // changes, but only if there is no machine name yet; i.e., only upon + // initial creation, not when editing. + if (eventData.$target.val() === '') { + eventData.$source.on('keyup.machineName change.machineName input.machineName', eventData, self.machineNameHandler) + // Initialize machine name preview. + .trigger('keyup'); + } + } + } + + // Used for caching objects + $.extend(MachineName, { + names : [] + }); + + $.extend(MachineName.prototype, { + + /** + * Handler for generating default eventData object. + * + * @param sourceId + * ID of source element + * + * @param $context + * Context element for finding elements + */ + eventData: function (sourceId, options, $context) { + var $source = $context.find(sourceId).addClass('machine-name-source').once('machine-name'); + var $target = $context.find(options.target).addClass('machine-name-target'); + var $suffix = $context.find(options.suffix); + var $wrapper = $target.closest('.form-item'); + + var errors = []; + + // All elements have to exist. + if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) { + if (!$source.length) { + errors.push('Source element is missing.'); } - // Append the machine name preview to the source field. - var $preview = $('' + options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix + ''); - $suffix.empty(); - if (options.label) { - $suffix.append('' + options.label + ': '); + + if (!$target.length) { + errors.push('Target element is missing.'); } - $suffix.append($preview); - // If the machine name cannot be edited, stop further processing. - if ($target.is(':disabled')) { - return; + if (!$suffix.length) { + errors.push('Suffix element is missing.'); } - eventData = { - $source: $source, - $target: $target, - $suffix: $suffix, - $wrapper: $wrapper, - $preview: $preview, - options: options - }; - // If it is editable, append an edit link. - var $link = $('').on('click', eventData, clickEditHandler); - $suffix.append($link); - - // Preview the machine name in realtime when the human-readable name - // changes, but only if there is no machine name yet; i.e., only upon - // initial creation, not when editing. - if ($target.val() === '') { - $source.on('formUpdated.machineName', eventData, machineNameHandler) - // Initialize machine name preview. - .trigger('formUpdated.machineName'); + if (!$wrapper.length) { + errors.push('Wrapper element is missing.'); } + } - // Add a listener for an invalid event on the machine name input - // to show its container and focus it. - $target.on('invalid', eventData, clickEditHandler); - }); + // Skip processing upon a form validation error on the machine name. + if ($target.hasClass('error')) { + errors.push('Machine name could not be validated.'); + } + + if (errors.length) { + throw new Error(errors.join(', ')); + } + + return { + $source: $source, + $target: $target, + $suffix: $suffix, + $wrapper: $wrapper, + options: options + }; + }, + + /** + * Edit button action for when editing a transliterated machine name. + * + * @param e + * A jQuery event object. + * - data: the event data + * - $wrapper: Wrapping form item div element. + * - $target: Target input form item element. + * - $suffix: Mandetory suffix to be added to the transliterated machine name. + * - $source: Source input form item element of the machine name. + */ + clickEditHandler: function (e) { + var data = e.data; + e.preventDefault(); + data.$wrapper.show(); + data.$target.trigger('focus'); + data.$suffix.hide(); + data.$source.off('.machineName'); + }, + + /** + * Machine name handler for generating the machine name from a human readable name. + * @param e + * A jQuery event object. + * - data: the event data + * - $wrapper: Wrapping form item div element. + * - $target: Target input form item element. + * - $suffix: Mandetory suffix to be added to the transliterated machine name. + * - $source: Source input form item element of the machine name. + * - $preview: Preview html element containing the machine name. + */ + machineNameHandler: function (e) { + var data = e.data; + var options = data.options; + var baseValue = $(e.target).val(); + + var rx = new RegExp(options.replace_pattern, 'g'); + var expected = baseValue.toLowerCase().replace(rx, options.replace).substr(0, options.maxlength); + + if (baseValue.toLowerCase() !== expected) { + Drupal.MachineName.prototype.transliterate(baseValue, options).done(function (machine) { + Drupal.MachineName.prototype.showMachineName(machine.substr(0, options.maxlength), data); + }); + } + else { + Drupal.MachineName.prototype.showMachineName(expected, data); + } }, showMachineName: function (machine, data) { @@ -190,14 +276,42 @@ * The transliterated source string. */ transliterate: function (source, settings) { - return $.get(Drupal.url('machine_name/transliterate'), { + + var now = Date.now(); + + // Number of milliseconds to wait between calls to the server. This avoids + // flooding the server with requests as a user is typing. + // @TODO: make this configurable? + this.delay = 250; + + if ('undefined' != typeof this.lastCalled && 'undefined' != typeof this.cachedResult && (now - this.lastCalled <= this.delay)) { + // Make sure we make one final call to the server to capture the final + // characters typed. + var self = this; + if ('undefined' != typeof this.finalCallback) { + clearTimeout(this.finalCallback); + } + this.finalCallback = setTimeout(function() { + self.transliterate(source, settings); + }, this.delay); + + return this.cachedResult; + } + + // Updated cached results with the latest from the server. + this.lastCalled = now; + this.cachedResult = $.get(drupalSettings.path.basePath + 'machine_name/transliterate', { text: source, langcode: drupalSettings.langcode, replace_pattern: settings.replace_pattern, replace: settings.replace, lowercase: true }); + + return this.cachedResult; } - }; + }); + + Drupal.MachineName = MachineName; })(jQuery, Drupal, drupalSettings);