PageSourceSearch

https://waylandunion.org/js/core/FormContent.js?v=7.265.42.1

js waylandunion.org collected 2026-09-24 10:16:51 UTC 38,348 bytes, 889 lines download raw bytes

1(function ($) {
2    function _(str) {
3        return plupload.translate(str) || str;
4    }
5
6    /// <summary>
7    /// Configures a form. This widge sets up various features including
8    /// tool tips, validation, and file uploads.
9    /// </summary>
10    $.widget("ui.FormContent",
11        {
12            /// <summary>
13            /// Set of files to upload.
14            /// </summary>
15            _fileUploadsToProcess: undefined,
16
17            /// <summary>
18            /// FormContent widget options.
19            /// </summary>
20            options:
21            {
22                Disabled: false,
23                UseRecaptcha: false,
24                RecaptchaSubType: null,
25                RecaptchaSiteKey: null,
26                RecaptchaErrorV2HtmlId: null,
27                RecaptchaElementV2HtmlId: null,
28                RecaptchaInputV3HtmlId: null
29            },
30
31            /// <summary>
32            /// Called when widget is destroyed.
33            /// </summary>
34            _destroy: function () {
35                return undefined;
36            },
37
38            /// <summary>
39            /// Called on widget init.
40            /// </summary>
41            _init: function () {
42                return undefined;
43            },
44
45            /// <summary>
46            /// Called to create this widget.
47            /// </summary>
48            _create: function () {
49                this.grecaptcha_widget_id = null;
50
51                const self = this;
52                const jElement = $(self.element);
53
54                // Create exactly one (1) wrapper function instance!
55                const debounceWrapper = debounceFunc(function (event) {
56                    event.stopPropagation();
57                    event.preventDefault();
58                    event.stopImmediatePropagation();
59                    self._HandleFormSubmit(event);
60                    return false;
61                }, 250);
62
63                // Wrapper calling the single debounce function instance.
64                function doubleWrapped(event) {
65                    // Do this first! Immediately stop other submit handlers!
66                    event.preventDefault();
67
68                    // call the debouncewrapper.
69                    return debounceWrapper(event);
70                }
71                
72                self.element[0].addEventListener("submit", doubleWrapped);
73
74
75                // TODO: Do we need a submit event handler on the form?
76
77                if (self.options.Disabled) {
78                    jElement.find('input,select,button,textarea').off().prop('disabled', true);
79                    return;
80                }
81
82                jElement.vpformchecker({
83                    manageFormFieldStyling: false
84                });
85
86                jElement.vpformchecker('addOtherFunction', function () {
87                    return self._additionalFormChecking;
88                });
89
90                if (self.options.UseRecaptcha) {
91                    self._waitForGrecaptcha();
92                }
93
94                self._InitToolTips();
95                self._InitFileUploaders();
96                jElement.vpformchecker('initFormSetupFields', "(required)");
97
98                
99            },
100
101            /// <summary>
102            /// Initializes tool tip help on the form.
103            /// </summary>
104            _InitToolTips: function () {
105                const self = this;
106                const jElement = $(self.element);
107                const lHelpTextMap = [];
108
109                jElement.find(".fbcms_form_helptext").each(function (_index, aElement) //ignore jslint
110                {
111                    const jHelpTextDiv = $(aElement);
112                    lHelpTextMap["field" + jHelpTextDiv.data("field_id")] = [jHelpTextDiv.data("title"), jHelpTextDiv.html()];
113                });
114
115                jElement.find('.fbcms_custom_form_help').FoxToolTip({
116                    getInfo: (topic_id) => lHelpTextMap ? lHelpTextMap[topic_id] : ''
117                });
118            },
119
120            /// <summary>
121            /// Called after a file is uploaded to the server. 
122            /// </summary>
123            /// <param name="uploader">The uploader object doing the work.</param>
124            /// <param name="files">The set uploaded files.</param>
125            _UploadCompleteCallback: function (uploader, files) //ignore jslint
126            {
127                const self = this;
128                const jElement = $(self.element);
129
130                if (files !== null) {
131                    for (let fileIndex = 0; fileIndex < files.length; fileIndex += 1) {
132                        const fileNameId = 'UploadedFiles[' + files[fileIndex].id + '].FileName';
133                        const fileNameInput = $('<input>').attr({
134                            type: 'hidden',
135                            id: fileNameId,
136                            name: fileNameId,
137                            value: files[fileIndex].name
138                        });
139
140                        const targetNameId = 'UploadedFiles[' + files[fileIndex].id + '].TargetName';
141                        const targetNameInput = $('<input>').attr({
142                            type: 'hidden',
143                            id: targetNameId,
144                            name: targetNameId,
145                            value: files[fileIndex].target_name
146                        });
147
148                        const idId = 'UploadedFiles[' + files[fileIndex].id + '].Id';
149                        const idInput = $('<input>').attr({
150                            type: 'hidden',
151                            id: idId,
152                            name: idId,
153                            value: files[fileIndex].id
154                        });
155
156                        jElement.find("div.put_hidden_fields_here")
157                            .append(fileNameInput)
158                            .append(targetNameInput)
159                            .append(idInput);
160                    }
161                }
162
163                self._ProcessNextUploadOrSubmit();
164            },
165
166            /// <summary>
167            /// Custom initialization for an uploader specific to this widget.
168            /// </summary>
169            /// <param name="uploader">The uploader to modify.</param>
170            _CustomUploaderSetup: function (uploader) {
171                const self = this;
172                uploader.bind('UploadComplete', function (uploader, files) {
173                    self._UploadCompleteCallback(uploader, files);
174                });
175            },
176
177            /// <summary>
178            /// Configures a single upload element.
179            /// </summary>
180            /// <param name="element">The upload element to prepare.</param>
181            _SetupUploadElement: function (element) {
182                const self = this;
183                const jElement = $(self.element);
184                const jUploadElement = $(element);
185                const maxFileUploads = jUploadElement.data("max-file-uploads");
186
187                jUploadElement.FormContentUploader({
188                    max_file_count: maxFileUploads,
189                    form: jElement,
190                    filters:
191                    {
192                        mime_types: [
193                            {title: "Allowed Files", extensions: jUploadElement.data("allowed-file-extensions")},
194                            {title: "Zip files", extensions: "zip"}
195                        ],
196                        max_file_size: '250mb',
197                        prevent_duplicates: false
198                    },
199                    customSetup: function (uploader) {self._CustomUploaderSetup(uploader);}
200                });
201            },
202
203            /// <summary>
204            /// Create and intialize all uploaders.
205            /// </summary>
206            _InitFileUploaders: function () {
207                const self = this;
208                $(self.element).find(".file_upload").each(function () {
209                    self._SetupUploadElement(this);
210                });
211            },
212
213            /// <summary>
214            /// Cycles thru all the upload fields on the form. Uploads for each upload field are processed. Once all
215            /// upload fields are processed the form is submitted.
216            /// </summary>
217            _ProcessNextUploadOrSubmit: function () {
218                const self = this;
219
220                if (self._fileUploadsToProcess !== undefined && self._fileUploadsToProcess.length > 0) {
221                    for (let lUploader = self._fileUploadsToProcess.pop(); lUploader !== undefined; lUploader = self._fileUploadsToProcess.pop()) {
222                        if ($(lUploader).FormContentUploader("start")) {
223                            return;
224                        }
225                    }
226                }
227
228
229                const data = $(self.element).serialize();
230                const formid = $(self.element).data("formid");
231
232                $.ajax({
233                    url: `/Core/Forms/Submit/${formid}`,
234                    type: 'post',
235                    dataType: 'json',
236                    data: data,
237                    success: function (data) {
238                        if (data.Success !== true) {
239                            $(self.element).find("input.submit_button").removeAttr("disabled");
240                            $(self.element).find('.fbcms_custom_form_errormessage').html(data.Message);
241                        }
242                        else if (data.Reason === "redirect") {
243                            document.location = data.Message;
244                        }
245                        else {
246                            $(self.element).find('.formwrapper').hide();
247                            $(self.element).find('.messagewrapper').html(data.Message);
248                        }
249                    },
250                    error: function (jqXHR, textStatus, errorThrown) {
251                        console.error(`Failed to submit: ${textStatus} ${errorThrown}`);
252                        $(self.element).find("input.submit_button").removeAttr("disabled");
253                        $(self.element).find('.fbcms_custom_form_errormessage').html("Unable to submit the form at this time.");
254                    }
255                });
256            },
257
258            _completeSubmit: function () {
259                const $element = $(this.element);
260                $element.find(".fbcms_custom_form_errormessage").html("");
261                this._fileUploadsToProcess = $element.find(".file_upload").toArray();
262                this._ProcessNextUploadOrSubmit();
263                return;
264            },
265
266            /// <summary>
267            /// Validates, prepares, and submits the form upon clicking the submit button.
268            /// </summary>
269            _HandleFormSubmit: function (e) {
270                const self = this;
271                const $element = $(this.element);
272                const $submitButton = $element.find("input.submit_button");
273
274                e.preventDefault();
275
276                if ($submitButton.attr('disabled')) {
277                    return;
278                }
279
280                $submitButton.attr('disabled', true);
281
282                let formCheckOk = $element.vpformchecker('checkForm');
283
284                $element.find(".file_upload").each(function (_index, element) {
285                    const $myElement = $(element);
286                    const lValidation = $myElement.data("validation-mode");
287
288                    if (lValidation === "required") {
289                        let uploaderCheckOk = $myElement.FormContentUploader("haveFile");
290                        if (!uploaderCheckOk) {
291                            formCheckOk = false;
292                            $myElement.closest('.fbcms_custom_form_field').removeClass('field_correct').addClass('field_warning');
293                            $(".fbcms_custom_form_errormessage").append(
294                                "<div>Please select a file for the `"
295                                + $(element).data("validation-title")
296                                + "` field.</div>"
297                            );
298                        }
299                        else {
300                            $myElement.closest('.fbcms_custom_form_field').removeClass('field_warning').addClass('field_correct');
301                        }
302                    }
303                });
304
305
306                if (formCheckOk) {
307                    if (!self.options.UseRecaptcha) {
308                        self._completeSubmit();
309                        return;
310                    }
311                    else if (self.options.RecaptchaSubType === 'V3Score') {
312                        grecaptcha.ready(function () {
313                            grecaptcha.execute(self.options.RecaptchaSiteKey, {action: 'submit'})
314                                .then(function (token) {
315                                    $element.find(`#${self.options.RecaptchaInputV3HtmlId}`).val(token);
316                                    self._completeSubmit();
317                                });
318                        });
319                        return;
320                    }
321                    else if (self.options.RecaptchaSubType === 'V2Invisible') {
322                        grecaptcha.ready(function () {
323                            grecaptcha.execute(self.grecaptcha_widget_id);
324                            $submitButton.removeAttr('disabled');
325                            return;
326                        });
327                    }
328                    else {
329                        self._completeSubmit();
330                        return;
331                    }
332                }
333
334                $submitButton.removeAttr('disabled');
335            },
336            _additionalFormChecking: function () {
337                let success;
338                if (!this.options.UseRecaptcha) {
339                    success = true;
340                }
341                else if (this.options.RecaptchaSubType !== "V2Checkbox") {
342                    success = true;
343                }
344                else if (grecaptcha.getResponse(this.grecaptcha_widget_id)) {
345                    $(`#${this.options.RecaptchaErrorV2HtmlId}`).html('');
346                    success = true;
347                }
348                else {
349                    $(`#${this.options.RecaptchaErrorV2HtmlId}`).html('Please check the reCAPTCHA.');
350                    success = false;
351                }
352                return success;
353            },
354            _waitForGrecaptcha: function () {
355                const self = this;
356                if (typeof grecaptcha !== "undefined") {
357                    if (self.options.RecaptchaSubType === "V2Checkbox") {
358                        try {
359                            grecaptcha.ready(function () {
360                                try {
361                                    self.grecaptcha_widget_id = grecaptcha.render(self.options.RecaptchaElementV2HtmlId, {'sitekey': self.options.RecaptchaSiteKey});
362                                }
363                                catch (renderError) {
364                                    console.error(`grecaptcha.render raised an error: ${renderError}`);
365                                }
366                            });
367                        }
368                        catch (readyError) {
369                            console.error(`grecaptcha.ready raised an error: ${readyError}`);
370                        }
371                    }
372                    else if (self.options.RecaptchaSubType === "V2Invisible") {
373                        try {
374
375                            grecaptcha.ready(function () {
376                                try {
377                                    self.grecaptcha_widget_id = grecaptcha.render(
378                                        self.options.RecaptchaElementV2HtmlId,
379                                        {
380                                            'sitekey': self.options.RecaptchaSiteKey,
381                                            'size': "invisible",
382                                            'badge': "inline",
383                                            'callback': function (token) {
384                                                $(self.element).find("input.submit_button").attr('disabled', 'true');
385                                                $(self.element).find(`#${self.options.RecaptchaInputV3HtmlId}`).val(token);
386                                                self._completeSubmit();
387                                            }
388                                        });
389                                }
390                                catch (renderError) {
391                                    console.error(`grecaptcha.render raised an error: ${renderError}`);
392                                }
393                            });
394                        }
395                        catch (readyError) {
396                            console.error(`grecaptcha.ready raised an error: ${readyError}`);
397                        }
398                    }
399                }
400                else {
401                    setTimeout(function () {
402                        self._waitForGrecaptcha();
403                    }, 250);
404                }
405            }
406        });
407
408    /// <summary>
409    /// Prepares an upload area on a form for processing uploads.
410    /// </summary>
411    $.widget("ui.FormContentUploader",
412        {
413            /// <summary>
414            /// The PLUpload object backing this upload field.
415            /// </summary>
416            _uploader: undefined,
417            /// <summary>
418            /// FormContent widget options.
419            /// </summary>
420            options:
421            {
422                runtimes: 'html5,html4',
423                unique_names: true,
424                multi_selection: false,
425                max_file_count: 1,
426                form: undefined,
427                chunk_size: '1mb',
428                url: '/Core/PLUpload',
429                filters:
430                {
431                    mime_types: [
432                        {title: "Allowed Files", extensions: 'jpg,jpeg,png,gif,svg,pdf,webp,avif'},
433                        {title: "Zip files", extensions: "zip"}
434                    ],
435                    max_file_size: '250mb',
436                    prevent_duplicates: false
437                },
438                customSetup: function () {
439                    return undefined;
440                },
441                dragdrop: true,
442                container: undefined,
443                browse_button: undefined
444            },
445
446            /// <summary>
447            /// Called when widget is destroyed.
448            /// </summary>
449            _destroy: function () {
450                return undefined;
451            },
452
453            /// <summary>
454            /// Called on widget init.
455            /// </summary>
456            _init: function () {
457                return undefined;
458            },
459
460            /// <summary>
461            /// Updates the overall progress displayed on the upload field.
462            /// </summary>
463            _updateTotalProgress: function () {
464                const self = this;
465                const jElement = $(self.element);
466
467                $('.plupload_total_status', jElement).html(self._uploader.total.percent + '%');
468                $('.plupload_progress_bar', jElement).css('width', self._uploader.total.percent + '%');
469
470                const html = _('Uploaded %d/%d files').replace(/%d\/%d/, self._uploader.total.uploaded + '/' + self._uploader.files.length);
471                $('.plupload_upload_status', jElement).html(html);
472            },
473
474            /// <summary>
475            /// Updates a files status as displayed on the screen.
476            /// </summary>
477            /// <param name="file">The file that was updated.</param>
478            _updateFileStatus: function (file) {
479                const self = this;
480                const jElement = $(self.element);
481
482                let actionClass;
483                if (file.status === plupload.DONE) {
484                    actionClass = 'plupload_done';
485                }
486                else if (file.status === plupload.FAILED) {
487                    actionClass = 'plupload_failed';
488                }
489                else if (file.status === plupload.QUEUED) {
490                    actionClass = 'plupload_delete';
491                }
492                else if (file.status === plupload.UPLOADING) {
493                    actionClass = 'plupload_uploading';
494                }
495                else {
496                    actionClass = null;
497                }
498
499                const jIcons = $('#' + file.id, jElement);
500                jIcons.removeClass('plupload_done');
501                jIcons.removeClass('plupload_failed');
502                jIcons.removeClass('plupload_delete');
503                jIcons.removeClass('plupload_uploading');
504                if (actionClass) {
505                    jIcons.addClass(actionClass);
506                }
507
508                const jIconLinks = jIcons.find('a');
509                jIconLinks.css('display', 'block');
510
511                if (file.hint) {
512                    jIconLinks.attr('title', file.hint);
513                }
514            },
515
516            /// <summary>
517            /// Adds a newly queue file to the list of files displayed on the screen.
518            /// </summary>
519            /// <param name="index">Position of this file with the set of all queued files. (zero based)</param>
520            /// <param name="file">The new file.</param>
521            _addFileToFileListing: function (index, file) {
522                const self = this;
523                const jElement = $(self.element);
524
525                let inputHTML = '';
526                if (file.status === plupload.DONE) {
527                    if (file.target_name) {
528                        inputHTML += '<input type="hidden" name="'
529                            + self.options.container
530                            + '_'
531                            + index
532                            + '_tmpname" value="'
533                            + plupload.xmlEncode(file.target_name) + '" />';
534                    }
535
536                    inputHTML += '<input type="hidden" name="'
537                        + self.options.container + '_'
538                        + index
539                        + '_name" value="'
540                        + plupload.xmlEncode(file.name)
541                        + '" />';
542
543                    inputHTML += '<input type="hidden" name="'
544                        + self.options.container
545                        + '_'
546                        + index
547                        + '_status" value="'
548                        + (file.status === plupload.DONE ? 'done' : 'failed')
549                        + '" />';
550
551                    $('#' + self.options.container + '_count', jElement).val(index + 1);
552                }
553
554                $('.plupload_filelist', jElement).append(
555                    '<div class="fbcms_table_row uploadfile_row" id="' + file.id + '">'
556                    + '<span class="fbcms_table_cell plupload_file_name"><span>' + file.name + '</span></span>'
557                    + '<span class="fbcms_table_cell text-right plupload_file_size">' + plupload.formatSize(file.size)
558                    + '</span>'
559                    + '<span class="fbcms_table_cell text-right plupload_file_status">' + file.percent + '%</span>'
560                    + '<span class="fbcms_table_cell text-right plupload_file_action">'
561                    + '<button type="button"><span class="screen-reader-text">Delete</span></button>'
562                    + inputHTML
563                    + '</span>'
564                    + '</div>'
565                );
566
567                self._updateFileStatus(file);
568
569                $('#' + file.id + '.plupload_delete button', jElement).on('click', function (e) {
570                    e.preventDefault();
571
572                    let jRowDiv = $('#' + file.id, jElement);
573                    let jPrevDiv = jRowDiv.prev();
574                    let jNextDiv = jRowDiv.next();
575                    let focusElementSelector = null;
576
577                    if (jPrevDiv.length > 0) {
578                        focusElementSelector = "#" + jPrevDiv.attr('id') + " button";
579                    }
580                    else if (jNextDiv.length > 0) {
581                        focusElementSelector = "#" + jNextDiv.attr('id') + " button";
582                    }
583                    else {
584                        focusElementSelector = '#' + self.options.container + '_upload_browse';
585                    }
586
587                    jRowDiv.trigger('blur');
588                    jRowDiv.remove();
589                    self._uploader.removeFile(file);
590                    $(focusElementSelector).focus();
591                });
592
593            },
594
595            /// <summary>
596            /// Rebuilds the file list displayed on the screen.
597            /// </summary>
598            _updateFileListing: function () {
599                const self = this;
600                const jElement = $(self.element);
601                const jFileList = $('.plupload_filelist', jElement);
602
603                jFileList.html('');
604                $.each(self._uploader.files, function (index, file) {self._addFileToFileListing(index, file);});
605
606                $('.plupload_total_file_size', jElement).html(plupload.formatSize(self._uploader.total.size));
607                $('.plupload_add_text', jElement).html("Select File");
608
609                $('.plupload_start', jElement).toggleClass(
610                    'disabled',
611                    self._uploader.files.length === (self._uploader.total.uploaded + self._uploader.total.failed)
612                );
613
614                const maxFileUploads = self._uploader.settings.max_file_count;
615                const currentUploads = self._uploader.files.length;
616
617                $('.file_upload_button', jElement).prop('disabled', maxFileUploads <= currentUploads);
618
619                if (maxFileUploads == 0) {
620                    $('.plupload_maximum_count', jElement).text(``);
621                }
622                else {
623                    $('.plupload_maximum_count', jElement).text(`${currentUploads} of ${maxFileUploads} selected`);
624                }
625
626                // Scroll to end of file list
627                jFileList[0].scrollTop = jFileList[0].scrollHeight;
628
629                self._updateTotalProgress();
630
631                // Re-add drag message if there is no files
632                if (!self._uploader.files.length && self._uploader.features.dragdrop && self._uploader.settings.dragdrop) {
633                    $('#' + self.options.container + '_filelist', jElement).append('<li class="plupload_droptext">' + _("Drag files here.") + '</li>');
634                }
635            },
636
637            /// <summary>
638            /// Highlights the file the is about to be uploaded.
639            /// </summary>
640            _highlightCurrentUploadFile: function (up, file) //ignore jslint
641            {
642                const self = this;
643                const jElement = $(self.element);
644                $('#' + file.id, jElement).addClass('plupload_current_file');
645            },
646
647            /// <summary>
648            /// Starts editing of a name.
649            /// </summary>
650            /// <param name="nameElement">Element containing the name to change.</param>
651            _startRenameFile: function (nameElement) {
652                const targetSpan = $(nameElement);
653                const self = this;
654                const file = self._uploader.getFile(targetSpan.parents('li')[0].id);
655                const parts = /^(.+)(\.[^.]+)$/.exec(file.name);
656
657                let ext;
658                let name;
659                if (parts) {
660                    name = parts[1];
661                    ext = parts[2];
662                }
663                else {
664                    name = file.name;
665                    ext = "";
666                }
667
668                // Display input element
669                targetSpan.hide().after('<input type="text" />');
670                targetSpan.next().val(name)
671                    .focus()
672                    .on('blur', function () {
673                        targetSpan.show().next().remove();
674                    })
675                    .on('keydown', function (e) {
676                        const targetInput = $(this);
677
678                        if ($.inArray(e.keyCode, [13, 27]) !== -1) {
679                            e.preventDefault();
680
681                            // Rename file and glue extension back on
682                            if (e.keyCode === 13) {
683                                file.name = targetInput.val() + ext;
684                                targetSpan.html(file.name);
685                            }
686                            targetInput.trigger('blur');
687                        }
688                    });
689            },
690
691
692            /// <summary>
693            /// Initializes the plupload uploader object.
694            /// </summary>
695            _initializeUploader: function () {
696                const self = this;
697                const jElement = $(self.element);
698
699                // Enable rename support
700                if (!self._uploader.settings.unique_names && self._uploader.settings.rename) {
701                    jElement.on('click', `#${self.options.container}_filelist .plupload_file_name span`, function (e) {
702                        self._startRenameFile(e.target);
703                    });
704                }
705
706                self._uploader.settings.browse_button = self.options.container + '_upload_browse';
707
708                // Enable drag/drop.
709                if (self._uploader.features.dragdrop && self._uploader.settings.dragdrop) {
710                    self._uploader.settings.drop_element = self.options.container + '_filelist';
711                    $('#' + self.options.container + '_filelist', jElement).append('<li class="plupload_droptext">' + _("Drag files here.") + '</li>');
712                }
713
714                $('.plupload_start', jElement).on('click', function (e) {
715                    e.preventDefault();
716                    if (!$(e.target).hasClass('disabled') && jElement.vpformchecker('checkForm')) {
717                        self._uploader.start();
718                    }
719                });
720
721                $('.plupload_stop', jElement).on('click', function (e) {
722                    e.preventDefault();
723                    self._uploader.stop();
724                });
725
726                $('.plupload_start', jElement).addClass('disabled');
727            },
728
729            /// <summary>
730            /// Displays an error encountered by the uploader.
731            /// </summary>
732            /// <param name="up">Reference to the uploader.</param>
733            /// <param name="err">Object describing the error.</param>
734            _displayUploaderError: function (up, err) {
735                const self = this;
736                const jElement = $(self.element);
737                const file = err.file
738
739                if (file) {
740                    let message = err.message;
741
742                    if (err.details) {
743                        message += " (" + err.details + ")";
744                    }
745
746                    if (err.code === plupload.FILE_SIZE_ERROR) {
747                        alert(_("Error: File too large: ") + file.name);
748                    }
749
750                    if (err.code === plupload.FILE_EXTENSION_ERROR) {
751                        alert(_("Error: Invalid file extension: ") + file.name);
752                    }
753
754                    file.hint = message;
755                    $('#' + file.id, jElement).attr('class', 'plupload_failed').find('a').css('display', 'block').attr('title', message);
756                }
757            },
758
759            /// <summary>
760            /// Updates the display when the state of the uploader changes.
761            /// </summary>
762            _uploaderStateChanged: function (uploader) {
763                const self = this;
764                const jElement = $(self.element);
765
766                if (self._uploader.state === plupload.STARTED) {
767                    $('.plupload_delete a,.plupload_buttons', jElement).hide();
768                    $('.plupload_upload_status,.plupload_progress,.plupload_stop', jElement).css('display', 'block');
769                    if (self._uploader.settings.multi_selection) {
770                        $('.plupload_total_status,.plupload_total_file_size', jElement).show();
771                    }
772                }
773                else {
774                    self._updateFileListing();
775                    $('.plupload_stop, .plupload_progress', jElement).hide();
776                    $('.plupload_delete', jElement).css('display', 'block');
777                }
778            },
779
780            /// <summary>
781            /// Updates the displayed status of a file that has just been uploaded.
782            /// </summary>
783            /// <param name="up">The uploader.</param>
784            /// <param name="file">The newly uploaded file.</param>
785            _fileUploaded: function (up, file) //ignore jslint
786            {
787                const self = this;
788                self._updateFileStatus(file);
789            },
790
791            /// <summary>
792            /// Displays the program made in uploading a file.
793            /// </summary>
794            /// <param name="up">Reference to the uploader.</param>
795            /// <param name="file">The file being uploaded.</param>
796            _updateUploadProgress: function (up, file) //ignore jslint
797            {
798                const self = this;
799                const jElement = $(self.element);
800
801                // Set file specific progress
802                $('#' + file.id + ' .plupload_file_status', jElement).html(file.percent + '%');
803
804                self._updateFileStatus(file);
805                self._updateTotalProgress();
806
807                if (self._uploader.settings.multi_selection && self._uploader.total.uploaded + self._uploader.total.failed === self._uploader.files.length) {
808                    $(".plupload_buttons,.plupload_upload_status", jElement).css("display", "inline");
809                    $(".plupload_start", jElement).addClass("disabled");
810                    $('.plupload_total_status,.plupload_total_file_size', jElement).hide();
811                }
812            },
813
814            /// <summary>
815            /// Called to create this widget.
816            /// </summary>
817            _create: function () {
818                const self = this;
819                const jElement = $(self.element);
820
821                if (self.options.container === undefined) {
822                    self.options.container = jElement.attr('id');
823                    if (self.options.container === undefined) {
824                        self.options.container = plupload.guid();
825                        jElement.attr('id', self.options.container);
826                    }
827                }
828
829                self.options.browse_button = self.options.container + "_upload_browse";
830                self._uploader = new plupload.Uploader(self.options);
831
832                self._uploader.bind("UploadFile", function (up, file) {
833                    self._highlightCurrentUploadFile(up, file);
834                });
835                self._uploader.bind('Init', function (up) {
836                    self._initializeUploader(up);
837                });
838                self._uploader.bind('PostInit', function ( /*uploader*/) {
839                    const maxFileUploads = jElement.data("max-file-uploads");
840                    buttonLabel = (maxFileUploads === 1) ? "Select File" : "Add Files";
841                    $(self.element).find('input[type=file]').attr('aria-label', buttonLabel);
842                });
843                self._uploader.bind("Error", function (up, err) {
844                    self._displayUploaderError(up, err);
845                });
846                self._uploader.init();
847                self._uploader.bind('StateChanged', function (uploader) {
848                    self._uploaderStateChanged(uploader);
849                });
850                self._uploader.bind('QueueChanged', function (up, file) {
851                    self._updateFileListing(up, file);
852                });
853                self._uploader.bind('FileUploaded', function (up, file) {
854                    self._fileUploaded(up, file);
855                });
856                self._uploader.bind("UploadProgress", function (up, file) {
857                    self._updateUploadProgress(up, file);
858                });
859
860                self._uploader.bind("FilesAdded", function (uploader, files) {
861                    const maxFileUploads = uploader.settings.max_file_count;
862                    const currentUploads = uploader.files.length;
863                    if (maxFileUploads < currentUploads) {
864                        alert(`Only ${maxFileUploads} file uploads are allowed.`);
865                        uploader.splice(maxFileUploads);
866                    }
867                });
868
869                self._uploader.refresh();
869 // Reposition Flash/Silverlight
870
871                // Call customSetup function
872                if (self.options.customSetup !== undefined && (typeof self.options.customSetup === 'function')) {
873                    self.options.customSetup(self._uploader);
874                }
875            },
876
877            start: function () {
878                let started = false;
879                if (this._uploader && this._uploader.files.length > 0) {
880                    this._uploader.start();
881                    started = true;
882                }
883                return started;
884            },
885            haveFile: function () {
886                return (this._uploader && this._uploader.files.length !== 0);
887            }
888        });
889}(jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.