Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / file / file.es6.js
diff --git a/web/core/modules/file/file.es6.js b/web/core/modules/file/file.es6.js
new file mode 100644 (file)
index 0000000..9ab5dc2
--- /dev/null
@@ -0,0 +1,253 @@
+/**
+ * @file
+ * Provides JavaScript additions to the managed file field type.
+ *
+ * This file provides progress bar support (if available), popup windows for
+ * file previews, and disabling of other file fields during Ajax uploads (which
+ * prevents separate file fields from accidentally uploading files).
+ */
+
+(function ($, Drupal) {
+  /**
+   * Attach behaviors to the file fields passed in the settings.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches validation for file extensions.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches validation for file extensions.
+   */
+  Drupal.behaviors.fileValidateAutoAttach = {
+    attach(context, settings) {
+      const $context = $(context);
+      let elements;
+
+      function initFileValidation(selector) {
+        $context.find(selector)
+          .once('fileValidate')
+          .on('change.fileValidate', { extensions: elements[selector] }, Drupal.file.validateExtension);
+      }
+
+      if (settings.file && settings.file.elements) {
+        elements = settings.file.elements;
+        Object.keys(elements).forEach(initFileValidation);
+      }
+    },
+    detach(context, settings, trigger) {
+      const $context = $(context);
+      let elements;
+
+      function removeFileValidation(selector) {
+        $context.find(selector)
+          .removeOnce('fileValidate')
+          .off('change.fileValidate', Drupal.file.validateExtension);
+      }
+
+      if (trigger === 'unload' && settings.file && settings.file.elements) {
+        elements = settings.file.elements;
+        Object.keys(elements).forEach(removeFileValidation);
+      }
+    },
+  };
+
+  /**
+   * Attach behaviors to file element auto upload.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches triggers for the upload button.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches auto file upload trigger.
+   */
+  Drupal.behaviors.fileAutoUpload = {
+    attach(context) {
+      $(context).find('input[type="file"]').once('auto-file-upload').on('change.autoFileUpload', Drupal.file.triggerUploadButton);
+    },
+    detach(context, setting, trigger) {
+      if (trigger === 'unload') {
+        $(context).find('input[type="file"]').removeOnce('auto-file-upload').off('.autoFileUpload');
+      }
+    },
+  };
+
+  /**
+   * Attach behaviors to the file upload and remove buttons.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches form submit events.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches form submit events.
+   */
+  Drupal.behaviors.fileButtons = {
+    attach(context) {
+      const $context = $(context);
+      $context.find('.js-form-submit').on('mousedown', Drupal.file.disableFields);
+      $context.find('.js-form-managed-file .js-form-submit').on('mousedown', Drupal.file.progressBar);
+    },
+    detach(context) {
+      const $context = $(context);
+      $context.find('.js-form-submit').off('mousedown', Drupal.file.disableFields);
+      $context.find('.js-form-managed-file .js-form-submit').off('mousedown', Drupal.file.progressBar);
+    },
+  };
+
+  /**
+   * Attach behaviors to links within managed file elements for preview windows.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches triggers.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches triggers.
+   */
+  Drupal.behaviors.filePreviewLinks = {
+    attach(context) {
+      $(context).find('div.js-form-managed-file .file a').on('click', Drupal.file.openInNewWindow);
+    },
+    detach(context) {
+      $(context).find('div.js-form-managed-file .file a').off('click', Drupal.file.openInNewWindow);
+    },
+  };
+
+  /**
+   * File upload utility functions.
+   *
+   * @namespace
+   */
+  Drupal.file = Drupal.file || {
+
+    /**
+     * Client-side file input validation of file extensions.
+     *
+     * @name Drupal.file.validateExtension
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered. For example `change.fileValidate`.
+     */
+    validateExtension(event) {
+      event.preventDefault();
+      // Remove any previous errors.
+      $('.file-upload-js-error').remove();
+
+      // Add client side validation for the input[type=file].
+      const extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
+      if (extensionPattern.length > 1 && this.value.length > 0) {
+        const acceptableMatch = new RegExp(`\\.(${extensionPattern})$`, 'gi');
+        if (!acceptableMatch.test(this.value)) {
+          const error = Drupal.t('The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.', {
+            // According to the specifications of HTML5, a file upload control
+            // should not reveal the real local path to the file that a user
+            // has selected. Some web browsers implement this restriction by
+            // replacing the local path with "C:\fakepath\", which can cause
+            // confusion by leaving the user thinking perhaps Drupal could not
+            // find the file because it messed up the file path. To avoid this
+            // confusion, therefore, we strip out the bogus fakepath string.
+            '%filename': this.value.replace('C:\\fakepath\\', ''),
+            '%extensions': extensionPattern.replace(/\|/g, ', '),
+          });
+          $(this).closest('div.js-form-managed-file').prepend(`<div class="messages messages--error file-upload-js-error" aria-live="polite">${error}</div>`);
+          this.value = '';
+          // Cancel all other change event handlers.
+          event.stopImmediatePropagation();
+        }
+      }
+    },
+
+    /**
+     * Trigger the upload_button mouse event to auto-upload as a managed file.
+     *
+     * @name Drupal.file.triggerUploadButton
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered. For example `change.autoFileUpload`.
+     */
+    triggerUploadButton(event) {
+      $(event.target).closest('.js-form-managed-file').find('.js-form-submit').trigger('mousedown');
+    },
+
+    /**
+     * Prevent file uploads when using buttons not intended to upload.
+     *
+     * @name Drupal.file.disableFields
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered, most likely a `mousedown` event.
+     */
+    disableFields(event) {
+      const $clickedButton = $(this).findOnce('ajax');
+
+      // Only disable upload fields for Ajax buttons.
+      if (!$clickedButton.length) {
+        return;
+      }
+
+      // Check if we're working with an "Upload" button.
+      let $enabledFields = [];
+      if ($clickedButton.closest('div.js-form-managed-file').length > 0) {
+        $enabledFields = $clickedButton.closest('div.js-form-managed-file').find('input.js-form-file');
+      }
+
+      // Temporarily disable upload fields other than the one we're currently
+      // working with. Filter out fields that are already disabled so that they
+      // do not get enabled when we re-enable these fields at the end of
+      // behavior processing. Re-enable in a setTimeout set to a relatively
+      // short amount of time (1 second). All the other mousedown handlers
+      // (like Drupal's Ajax behaviors) are executed before any timeout
+      // functions are called, so we don't have to worry about the fields being
+      // re-enabled too soon. @todo If the previous sentence is true, why not
+      // set the timeout to 0?
+      const $fieldsToTemporarilyDisable = $('div.js-form-managed-file input.js-form-file').not($enabledFields).not(':disabled');
+      $fieldsToTemporarilyDisable.prop('disabled', true);
+      setTimeout(() => {
+        $fieldsToTemporarilyDisable.prop('disabled', false);
+      }, 1000);
+    },
+
+    /**
+     * Add progress bar support if possible.
+     *
+     * @name Drupal.file.progressBar
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered, most likely a `mousedown` event.
+     */
+    progressBar(event) {
+      const $clickedButton = $(this);
+      const $progressId = $clickedButton.closest('div.js-form-managed-file').find('input.file-progress');
+      if ($progressId.length) {
+        const originalName = $progressId.attr('name');
+
+        // Replace the name with the required identifier.
+        $progressId.attr('name', originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0]);
+
+        // Restore the original name after the upload begins.
+        setTimeout(() => {
+          $progressId.attr('name', originalName);
+        }, 1000);
+      }
+      // Show the progress bar if the upload takes longer than half a second.
+      setTimeout(() => {
+        $clickedButton.closest('div.js-form-managed-file').find('div.ajax-progress-bar').slideDown();
+      }, 500);
+    },
+
+    /**
+     * Open links to files within forms in a new window.
+     *
+     * @name Drupal.file.openInNewWindow
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered, most likely a `click` event.
+     */
+    openInNewWindow(event) {
+      event.preventDefault();
+      $(this).attr('target', '_blank');
+      window.open(this.href, 'filePreview', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550');
+    },
+  };
+}(jQuery, Drupal));