Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / file / file.js
1 /**
2  * @file
3  * Provides JavaScript additions to the managed file field type.
4  *
5  * This file provides progress bar support (if available), popup windows for
6  * file previews, and disabling of other file fields during Ajax uploads (which
7  * prevents separate file fields from accidentally uploading files).
8  */
9
10 (function ($, Drupal) {
11
12   'use strict';
13
14   /**
15    * Attach behaviors to the file fields passed in the settings.
16    *
17    * @type {Drupal~behavior}
18    *
19    * @prop {Drupal~behaviorAttach} attach
20    *   Attaches validation for file extensions.
21    * @prop {Drupal~behaviorDetach} detach
22    *   Detaches validation for file extensions.
23    */
24   Drupal.behaviors.fileValidateAutoAttach = {
25     attach: function (context, settings) {
26       var $context = $(context);
27       var elements;
28
29       function initFileValidation(selector) {
30         $context.find(selector)
31           .once('fileValidate')
32           .on('change.fileValidate', {extensions: elements[selector]}, Drupal.file.validateExtension);
33       }
34
35       if (settings.file && settings.file.elements) {
36         elements = settings.file.elements;
37         Object.keys(elements).forEach(initFileValidation);
38       }
39     },
40     detach: function (context, settings, trigger) {
41       var $context = $(context);
42       var elements;
43
44       function removeFileValidation(selector) {
45         $context.find(selector)
46           .removeOnce('fileValidate')
47           .off('change.fileValidate', Drupal.file.validateExtension);
48       }
49
50       if (trigger === 'unload' && settings.file && settings.file.elements) {
51         elements = settings.file.elements;
52         Object.keys(elements).forEach(removeFileValidation);
53       }
54     }
55   };
56
57   /**
58    * Attach behaviors to file element auto upload.
59    *
60    * @type {Drupal~behavior}
61    *
62    * @prop {Drupal~behaviorAttach} attach
63    *   Attaches triggers for the upload button.
64    * @prop {Drupal~behaviorDetach} detach
65    *   Detaches auto file upload trigger.
66    */
67   Drupal.behaviors.fileAutoUpload = {
68     attach: function (context) {
69       $(context).find('input[type="file"]').once('auto-file-upload').on('change.autoFileUpload', Drupal.file.triggerUploadButton);
70     },
71     detach: function (context, setting, trigger) {
72       if (trigger === 'unload') {
73         $(context).find('input[type="file"]').removeOnce('auto-file-upload').off('.autoFileUpload');
74       }
75     }
76   };
77
78   /**
79    * Attach behaviors to the file upload and remove buttons.
80    *
81    * @type {Drupal~behavior}
82    *
83    * @prop {Drupal~behaviorAttach} attach
84    *   Attaches form submit events.
85    * @prop {Drupal~behaviorDetach} detach
86    *   Detaches form submit events.
87    */
88   Drupal.behaviors.fileButtons = {
89     attach: function (context) {
90       var $context = $(context);
91       $context.find('.js-form-submit').on('mousedown', Drupal.file.disableFields);
92       $context.find('.js-form-managed-file .js-form-submit').on('mousedown', Drupal.file.progressBar);
93     },
94     detach: function (context) {
95       var $context = $(context);
96       $context.find('.js-form-submit').off('mousedown', Drupal.file.disableFields);
97       $context.find('.js-form-managed-file .js-form-submit').off('mousedown', Drupal.file.progressBar);
98     }
99   };
100
101   /**
102    * Attach behaviors to links within managed file elements for preview windows.
103    *
104    * @type {Drupal~behavior}
105    *
106    * @prop {Drupal~behaviorAttach} attach
107    *   Attaches triggers.
108    * @prop {Drupal~behaviorDetach} detach
109    *   Detaches triggers.
110    */
111   Drupal.behaviors.filePreviewLinks = {
112     attach: function (context) {
113       $(context).find('div.js-form-managed-file .file a').on('click', Drupal.file.openInNewWindow);
114     },
115     detach: function (context) {
116       $(context).find('div.js-form-managed-file .file a').off('click', Drupal.file.openInNewWindow);
117     }
118   };
119
120   /**
121    * File upload utility functions.
122    *
123    * @namespace
124    */
125   Drupal.file = Drupal.file || {
126
127     /**
128      * Client-side file input validation of file extensions.
129      *
130      * @name Drupal.file.validateExtension
131      *
132      * @param {jQuery.Event} event
133      *   The event triggered. For example `change.fileValidate`.
134      */
135     validateExtension: function (event) {
136       event.preventDefault();
137       // Remove any previous errors.
138       $('.file-upload-js-error').remove();
139
140       // Add client side validation for the input[type=file].
141       var extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
142       if (extensionPattern.length > 1 && this.value.length > 0) {
143         var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi');
144         if (!acceptableMatch.test(this.value)) {
145           var error = Drupal.t('The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.', {
146             // According to the specifications of HTML5, a file upload control
147             // should not reveal the real local path to the file that a user
148             // has selected. Some web browsers implement this restriction by
149             // replacing the local path with "C:\fakepath\", which can cause
150             // confusion by leaving the user thinking perhaps Drupal could not
151             // find the file because it messed up the file path. To avoid this
152             // confusion, therefore, we strip out the bogus fakepath string.
153             '%filename': this.value.replace('C:\\fakepath\\', ''),
154             '%extensions': extensionPattern.replace(/\|/g, ', ')
155           });
156           $(this).closest('div.js-form-managed-file').prepend('<div class="messages messages--error file-upload-js-error" aria-live="polite">' + error + '</div>');
157           this.value = '';
158           // Cancel all other change event handlers.
159           event.stopImmediatePropagation();
160         }
161       }
162     },
163
164     /**
165      * Trigger the upload_button mouse event to auto-upload as a managed file.
166      *
167      * @name Drupal.file.triggerUploadButton
168      *
169      * @param {jQuery.Event} event
170      *   The event triggered. For example `change.autoFileUpload`.
171      */
172     triggerUploadButton: function (event) {
173       $(event.target).closest('.js-form-managed-file').find('.js-form-submit').trigger('mousedown');
174     },
175
176     /**
177      * Prevent file uploads when using buttons not intended to upload.
178      *
179      * @name Drupal.file.disableFields
180      *
181      * @param {jQuery.Event} event
182      *   The event triggered, most likely a `mousedown` event.
183      */
184     disableFields: function (event) {
185       var $clickedButton = $(this).findOnce('ajax');
186
187       // Only disable upload fields for Ajax buttons.
188       if (!$clickedButton.length) {
189         return;
190       }
191
192       // Check if we're working with an "Upload" button.
193       var $enabledFields = [];
194       if ($clickedButton.closest('div.js-form-managed-file').length > 0) {
195         $enabledFields = $clickedButton.closest('div.js-form-managed-file').find('input.js-form-file');
196       }
197
198       // Temporarily disable upload fields other than the one we're currently
199       // working with. Filter out fields that are already disabled so that they
200       // do not get enabled when we re-enable these fields at the end of
201       // behavior processing. Re-enable in a setTimeout set to a relatively
202       // short amount of time (1 second). All the other mousedown handlers
203       // (like Drupal's Ajax behaviors) are executed before any timeout
204       // functions are called, so we don't have to worry about the fields being
205       // re-enabled too soon. @todo If the previous sentence is true, why not
206       // set the timeout to 0?
207       var $fieldsToTemporarilyDisable = $('div.js-form-managed-file input.js-form-file').not($enabledFields).not(':disabled');
208       $fieldsToTemporarilyDisable.prop('disabled', true);
209       setTimeout(function () {
210         $fieldsToTemporarilyDisable.prop('disabled', false);
211       }, 1000);
212     },
213
214     /**
215      * Add progress bar support if possible.
216      *
217      * @name Drupal.file.progressBar
218      *
219      * @param {jQuery.Event} event
220      *   The event triggered, most likely a `mousedown` event.
221      */
222     progressBar: function (event) {
223       var $clickedButton = $(this);
224       var $progressId = $clickedButton.closest('div.js-form-managed-file').find('input.file-progress');
225       if ($progressId.length) {
226         var originalName = $progressId.attr('name');
227
228         // Replace the name with the required identifier.
229         $progressId.attr('name', originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0]);
230
231         // Restore the original name after the upload begins.
232         setTimeout(function () {
233           $progressId.attr('name', originalName);
234         }, 1000);
235       }
236       // Show the progress bar if the upload takes longer than half a second.
237       setTimeout(function () {
238         $clickedButton.closest('div.js-form-managed-file').find('div.ajax-progress-bar').slideDown();
239       }, 500);
240     },
241
242     /**
243      * Open links to files within forms in a new window.
244      *
245      * @name Drupal.file.openInNewWindow
246      *
247      * @param {jQuery.Event} event
248      *   The event triggered, most likely a `click` event.
249      */
250     openInNewWindow: function (event) {
251       event.preventDefault();
252       $(this).attr('target', '_blank');
253       window.open(this.href, 'filePreview', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550');
254     }
255   };
256
257 })(jQuery, Drupal);