PageSourceSearch

https://yanapa.pe/assets/custom/src/filepicker.js

js yanapa.pe collected 2026-09-24 13:42:53 UTC 21,456 bytes, 811 lines download raw bytes

1/**
2 * This file is part of Filepicker.
3 *
4 * Copyright (c) 2016 Cretu Eusebiu <[email protected]>
5 *
6 * For the full copyright and license information, please visit:
7 * http://codecanyon.net/licenses/standard
8 */
9
10
11class Filepicker {
12    constructor(options) {
13        this._init(options);
14    }
15
16
17    static plugins = {};
18    static namespace = 'filepicker';
19    static pluginName = 'filePicker';
20
21    /**
22     * Register a plugin.
23     *
24     * @param {String} name
25     * @param {Function} callback
26     */
27    static plugin(name, callback) {
28        Filepicker.plugins[name] = callback;
29    }
30
31    _init(options) {
32
33        this.events = {};
34        this.plugins = {};
35        this.element = options.el || null;
36        this.options = $.extend(true, $.extend(true, {}, defaults), options);
37
38        this._queue = [];
39        this._sending = 0;
40
41        this._registerPlugins();
42
43        $.extend(true, this.options, options);
44
45        if (this.options.fileInput instanceof $) {
46            console.log(this.options.fileInput);
47            this.on(this.options.fileInput, 'change', this.onChange);
48        } else {
49            console.log(this.options.fileInput);
50            this.on(this.element, 'change', this.options.fileInput, this.onChange);
51        }
52
53        this.trigger('init');
54
55        this._initPlugins();
56    }
57
58    _registerPlugins() {
59        Object.entries(this.options.plugins).forEach(([_, name]) => {
60            if (Filepicker.plugins[name]) {
61                this.plugins[name] = new Filepicker.plugins[name](this);
62            }
63        });
64    }
65
66    _initPlugins() {
67        try {
68            Object.values(this.plugins).forEach(plugin => {
69                this._initPlugin(plugin);
70            });
71        }
72        catch (e) {
73            console.error(e);
74        }
75
76    }
77
78    _initPlugin(plugin) {
79        plugin.$f = this;
80        var _this = this;
81
82        /**
83         * Remove an event handler from element and instance.
84         *
85         * See Filepicker.prototype.trigger (events.js).
86         */
87        plugin.trigger = function (type, event) {
88            var data = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
89            var context = arguments[3];
90
91            return _this.trigger(type, event, data, context);
92        };
93        /**
94         * Add event listener on element or instance.
95         *
96         * See Filepicker.prototype.on (events.js).
97         */
98        plugin.on = function () {
99            var args = arguments;
100
101            if ($.isFunction(args[2])) {
102                args[4] = args[3] || this;
103                args[3] = args[2];
104                args[2] = null;
105            } else {
106                args[4] = args[4] || this;
107            }
108
109            return plugin.$f.on(args[0], args[1], args[2], args[3], args[4]);
110        };
111
112        plugin.init();
113    }
114
115    /**
116     * Fetch files from the server.
117     *
118     * @param  {Object} options
119     * @return {Object} jQuery Promise Object
120     */
121    fetch(options) {
122        return this._ajax(this._route('fetch'), options);
123    }
124
125    /**
126     * Update file by name.
127     *
128     * @param  {String} file
129     * @param  {Object} options
130     * @return {Object} jQuery Promise Object
131     */
132    update(file, options) {
133        const route = this._route('patch', 'PATCH');
134        return this._ajax(route, { file, ...options });
135    }
136
137    /**
138     * Delete files by name.
139     *
140     * @param  {String|Array} files
141     * @return {Object} jQuery Promise Object
142     */
143    delete(files) {
144        const filesArray = Array.isArray(files) ? files : [files];
145        return this._ajax(this._route('delete', 'DELETE'), { files: filesArray });
146    }
147
148    /**
149     * Get the translation for a given key.
150     *
151     * @param  {String} id
152     * @param  {Object} params
153     * @return {String}
154     */
155    trans(id, params = {}) {
156        let message = this.options.messages[id] || id.toString();
157
158        if (typeof message === 'function') {
159            message = message.call(this, params);
160        } else {
161            for (const key in params) {
162                message = message.replace(`:${key}`, params[key]);
163            }
164        }
165
166        return message;
167    }
168
169    /**
170     * Add extra props to the file.
171     *
172     * @param {Object} file
173     */
174    addProps(file) {
175        if (!file.extension) {
176            file.extension = this._getExtension(file.name);
177        }
178
179        file.sizeFormatted = this._formatSize(file.size);
180        file.imageFile = this.options.imageFileTypes.test(file.name);
181        file.pdfFile = this.options.pdfFileTypes.test(file.name);
182        file.timeFormatted = file.time ? this._formatTime(file.time) : '';
183
184        file.timeISOString = () =>
185            Date.prototype.toISOString ? new Date(file.time * 1000).toISOString() : '';
186    }
187
188    /**
189     * Get custom data.
190     *
191     * @param  {Object} merge
192     * @return {Object}
193     */
194    data(merge = {}) {
195        let data = typeof this.options.data === 'function' ? this.options.data.call(this) : this.options.data;
196        return { ...data, ...merge };
197    }
198
199    /**
200     * Destroy events.
201     */
202    destroy() {
203        this.events = [];
204
205        if (this.element) {
206            this.element.off(`.${Filepicker.pluginName}`);
207        }
208
209        if (this.options.fileInput) {
210            if (this.options.fileInput instanceof $) {
211                this.options.fileInput.off('change');
212            } else {
213                this.element.off('change', this.options.fileInput);
214            }
215        }
216
217        if (this.options.dropZone) {
218            this.options.dropZone.off('dragover dragenter dragleave drop');
219        }
220    }
221
222    /**
223     * Extend plugin.
224     *
225     * @param {Object} plugin
226     * @param {Object} options
227     */
228    extend(plugin, options) {
229        plugin.$f = this;
230        this.options = { ...this.options, ...options };
231    }
232
233    _addMethods(data) {
234        var _this = this;
235        var dfd = $.Deferred();
236
237        data.state = 'pending';
238        data.progress = { total: 0, loaded: 0 };
239        data.promise = function () {
240            return dfd.promise();
241        };
242
243        /**
244         * Send data.
245         *
246         * @param  {Object} options
247         * @return {Object|Boolean} jQuery Promise Object
248         */
249        data.send = function (options) {
250            if (data.state === 'sending') {
251                return dfd;
252            }
253
254            data.state = 'sending';
255
256            var xhr = data.xhr = $.ajaxSettings.xhr();
257            var route = _this._route('upload', 'POST');
258
259            xhr.open(route.method, route.uri, true);
260
261            xhr.upload.onprogress = function (e) {
262                if (e.lengthComputable) {
263                    data.progress.total = e.total;
264                    data.progress.loaded = e.loaded;
265
266                    dfd.notify(e.loaded, e.total, xhr);
267                }
268            };
269
270            xhr.onload = function () {
271                xhr.responseJSON = null;
272
273                try {
274                    xhr.responseJSON = $.parseJSON(xhr.responseText);
275                } catch (e) {
276                    var max = _this._getPostMaxSize(xhr.responseText);
277
278                    if (max > -1) {
279                        dfd.reject(_this.trans('postMaxSize', { max: max }), xhr);
280                    } else {
281                        dfd.reject(_this.trans('error'), xhr);
282                    }
283                }
284
285                if (xhr.responseJSON != null) {
286                    if (xhr.status === 200 || xhr.status === 201) {
287                        dfd.resolve(xhr.responseJSON, xhr);
288                    } else {
289                        dfd.reject(_this.trans('error', { response: xhr.responseJSON }), xhr);
290                    }
291                }
292            };
293
294            var formData = new FormData();
295
296            $.each(_this.data(options), function (key, value) {
297                formData.append(key, value);
298                if ($("#token_recaptcha").length > 0)
299                    formData.append("recaptcha", $("#token_recaptcha").val())
300            });
301
302            $.each(data.files, function (_, file) {
303                if (!file.error) {
304                    formData.append(_this.options.paramName, file, file.name);
305                }
306            });
307            xhr.send(formData);
308
309            return data.promise();
310        };
311
312        /**
313         * Abort upload.
314         */
315        data.abort = function () {
316            if (data.xhr) {
317                data.xhr.abort();
318            }
319
320            _this._processQueue();
321        };
322
323        return data;
324    }
325
326    _processQueue() {
327        this._sending--;
328
329        const { parallelUploads: parallel } = this.options;
330
331        if (!parallel || parallel > this._sending) {
332            for (let i = 0; i < this._queue.length; i++) {
333                if (this._queue[i].state !== 'sending') {
334                    this._queue[i].send();
335                    this._queue.splice(i, 1);
336                    this._sending++;
337                    break;
338                }
339            }
340        }
341    }
342
343    _ajax(route, data) {
344        if (route.method != 'GET' && route.method != 'POST') {
345            data._method = route.method;
346            route.method = 'POST';
347        }
348
349        return $.ajax({
350            url: route.uri,
351            type: route.method,
352            dataType: 'json',
353            data: this.data(data)
354        });
355    }
356
357    _route(name, method) {
358        const route = this.options.routes[name];
359        return {
360            uri: route ? route.uri : this.options.url,
361            method: route ? route.method : method || 'GET',
362        };
363    }
364
365    _getExtension(filename) {
366        return filename.substring(filename.lastIndexOf('.') + 1);
367    }
368
369    _formatTime(timestamp) {
370        var d = new Date(timestamp * 1000),
371            yyyy = d.getFullYear(),
372            mm = ('0' + (d.getMonth() + 1)).slice(-2),
373            dd = ('0' + d.getDate()).slice(-2),
374            hh = d.getHours(),
375            h = hh,
376            min = ('0' + d.getMinutes()).slice(-2),
377            ampm = 'AM';
378
379        if (hh > 12) {
380            h = hh - 12;
381            ampm = 'PM';
382        } else if (hh === 12) {
383            h = 12;
384            ampm = 'PM';
385        } else if (hh == 0) {
386            h = 12;
387        }
388
389        return yyyy + '-' + mm + '-' + dd + ', ' + h + ':' + min + ' ' + ampm;
390    }
391
392    _formatSize(bytes) {
393        if (!$.isNumeric(bytes)) {
394            return '';
395        }
396
397        var quant = {
398            GB: 1073741824,
399            MB: 1048576,
400            KB: 1024,
401            B: 1
402        };
403
404        for (var unit in quant) {
405            if (bytes >= quant[unit]) {
406                var size = Math.round(bytes / quant[unit] * 10) / 10;
407                return size + ' ' + unit;
408            }
409        }
410    }
411
412    _getInputFiles(input) {
413        var files = $.makeArray(input.prop('files'));
414
415        this._replaceInput(input);
416        if (files.length > 5) {
417            alert("Solo puede cargar 5 documentos a la vez")
418            return []
419        }
420        return files;
421    }
422
423    _replaceInput(input) {
424        var inputClone = input.clone(true);
425
426        $('<form/>').append(inputClone)[0].reset();
427
428        input.after(inputClone).detach();
429
430        $.cleanData(input.off('remove'));
431
432        var inputs = [];
433
434        if (this.options.fileInput instanceof $) {
435            inputs = this.options.fileInput;
436        } else if (this.element) {
437            inputs = this.element.find(this.options.fileInput);
438        }
439
440        inputs.map(function (i, el) {
441            if (el === input[0]) {
442                return inputClone[0];
443            }
444
445            return el;
446        });
447    }
448
449    _getPostMaxSize(text) {
450        if (!text.includes('POST Content-Length')) {
451            return -1;
452        }
453
454        const matches = text.match(/\d+/g) || [];
455        return matches[1] ? Math.round(parseInt(matches[1], 10) / 1048576) : 0;
456    }
457
458    on(element, type, selector, handler, context) {
459        if (!arguments[0]) {
460            return false;
461        }
462
463        if (arguments[0] instanceof $) {
464            // Add event handler on the element.
465
466            var element = arguments[0];
467            var type = arguments[1];
468            var selector = arguments[2];
469            var handler = arguments[3];
470            var context = arguments[4];
471
472            // Shift arguments if selector is missing.
473            if ($.isFunction(selector)) {
474                context = handler;
475                handler = selector;
476                selector = null;
477            }
478
479            element.on(type, selector, $.proxy(handler, context || this));
480        } else {
481            // Add event handler the instance.
482
483            var _type = arguments[0];
484            var _handler = arguments[1];
485
486            (this.events[_type] = this.events[_type] || []).push(_handler);
487        }
488    }
489
490    off(type, handler) {
491        if (this.element) {
492            this.element.off(type, handler);
493        }
494
495        type || (this.events = {});
496
497        var list = this.events[type] || [];
498        var i = list.length = handler ? list.length : 0;
499
500        while (i--) {
501            handler == list[i] && list.splice(i, 1);
502        }
503    }
504
505    trigger(type, event) {
506        var data = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
507        var context = arguments[3];
508
509        event = $.Event(event);
510        event.type = type + '.' + Filepicker.namespace;
511
512        context = context || this;
513
514        if (this.element) {
515            event.target = this.element[0];
516        }
517
518        this._copyProps(event);
519
520        // Trigger event on the element.
521        if (this.element) {
522            this.element.trigger(event, data);
523        }
524
525        // Trigger event on the instance.
526        this._emit.apply(this, [type, context, event].concat(data));
527
528        // Trigger event on default handler.
529        var response = $.isFunction(this.options[type]) && this.options[type].apply(context, [event].concat(data)) === false;
530
531        return !(response || event.isDefaultPrevented());
532    }
533
534    _emit(type, context, ...args) {
535        const list = this.events[type] || [];
536        list.forEach(handler => {
537            handler.call(context, ...args);
538        });
539    }
540
541    _copyProps(event) {
542        const orig = event.originalEvent;
543        if (orig) {
544            for (const prop in orig) {
545                if (!(prop in event)) {
546                    event[prop] = orig[prop];
547                }
548            }
549        }
550    }
551
552
553    onChange(e) {
554        if (!(window.File && window.FileList && window.FormData)) {
555            return this.trigger('uploadfallback', e, this.trans('uploadFallback'));
556        }
557
558        this.onAdd(e, { files: this._getInputFiles($(e.target)) });
559    }
560
561    onAdd(e, data) {
562        var _this = this;
563
564        var i = 0;
565        var batchSize = 0;
566        var fileSet = [];
567        var limit = this.options.uploadMultipleLimit;
568        var limitSize = this.options.uploadMultipleSize;
569        var overhead = this.options.uploadMultipleSizeOverhead;
570        var validate = function validate(file) {
571            return _this.trigger('validate', e, file);
572        };
573
574        $.each(data.files, function (_, file) {
575            return _this.addProps(file);
576        });
577
578        data.originalFiles = data.files;
579
580        if (!this.options.uploadMultiple) {
581            $.each(data.files, function (_, file) {
582                validate(file);
583                fileSet.push([file]);
584            });
585        } else if (limit) {
586            for (i = 0; i < data.files.length; i += limit) {
587                var files = data.files.slice(i, i + limit);
588
589                $.each(files, function (_, file) {
590                    validate(file);
591                });
592
593                fileSet.push(files);
594            }
595        } else {
596            $.each(data.files, function (_, file) {
597                if (!validate(file)) {
598                    batchSize += file.size + overhead;
599
600                    if (limitSize && batchSize > limitSize) {
601                        i++;
602                        batchSize = file.size + overhead;
603                    }
604                }
605
606                if (!fileSet[i]) {
607                    fileSet[i] = [];
608                }
609
610                fileSet[i].push(file);
611            });
612        }
613
614        $.each(fileSet, function (_, files) {
615            var newData = $.extend({}, data, { files: files });
616
617            _this._addMethods(newData);
618
619            _this.trigger('add', e, newData);
620        });
621    }
622
623    onSend(e, data) {
624        var _this2 = this;
625
626        var filtered = data.files.filter(function (file) {
627            return !file.error;
628        });
629
630        if (filtered.length == 0) {
631            return false;
632        }
633
634        if (this.trigger('send', e, data) === false) {
635            return this.onFail(this.trans(data.errorThrown || 'abort'), data);
636        }
637
638        data.promise().progress(function (loaded, total) {
639            return _this2.onProgress(loaded, total, data);
640        }).done(function (result, xhr) {
641            return _this2.onDone(xhr.responseJSON, data);
642        }).fail(function (error) {
643            return _this2.onFail(error, data);
644        }).always(function (resultOrError) {
645            return _this2.onAlways(resultOrError, data);
646        });
647
648        var parallel = this.options.parallelUploads;
649
650        if (!parallel || parallel > this._sending) {
651            this._sending++;
652            data.send();
653        } else {
654            this._queue.push(data);
655        }
656    }
657
658    onProgress(loaded, total, data) {
659        data.progress.percentage = Math.floor(loaded / total * 100);
660        this.trigger('progress', null, data);
661    }
662
663    onDone(result, data) {
664        data.result = result;
665        data.state = 'sent';
666
667        for (var i = 0; i < data.files.length; i++) {
668            data.files[i] = $.extend({ original: data.files[i] }, result[i]);
669            this.addProps(data.files[i]);
670        }
671
672        this.trigger('done', null, data);
673    }
674
675    onFail(errorThrown, data) {
676        data.errorThrown = errorThrown;
677        data.state = 'failed';
678        this.trigger('fail', null, data);
679    }
680
681    onAlways(resultOrError, data) {
682        this._processQueue();
683        data.resultOrError = resultOrError;
684        this.trigger('always', null, data);
685    }
686}
687
688$.fn[Filepicker.pluginName] = function (options = {}) {
689    return this.each(function () {
690        const el = $(this);
691        let instance = el.data(Filepicker.pluginName);
692
693        if (!instance) {
694            options.el = el;
695            instance = new Filepicker(options);
696            el.data(Filepicker.pluginName, instance);
697        }
698    });
699};
700
701const defaults = {
702    // url: null,
703    routes: {
704        // upload: {uri : '/upload', method: 'POST'},
705    },
706    plugins: [],
707
708    uploadMultiple: false,
709    uploadMultipleLimit: undefined,
710    uploadMultipleSize: undefined,
711    uploadMultipleSizeOverhead: 512,
712    parallelUploads: undefined,
713
714    // acceptFileTypes: '',
715    imageFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
716    pdfFileTypes: /(\.|\/)(pdf)$/i,
717    minFileSize: 1,
718    // maxFileSize: undefined,
719
720    data: {},
721    paramName: 'files[]',
722
723    fileInput: 'input[type="file"]',
724
725    messages: {
726        uploadFallback: 'The browser does not support file uploads.',
727        minFileSize: 'The file must be at least :min KB.',
728        maxFileSize: 'The file may not be greater than :max KB.',
729        postMaxSize: 'The file exceeds the post max size limit of :max MB.',
730        invalidFileType: 'The file type is not allowed.',
731        error: 'Oops! Something went wrong.',
732        abort: 'The operation was aborted.'
733    },
734
735    /**
736     * Add handler.
737     *
738     * @param {Object} e
739     * @param {Object} data
740     */
741    add(e, data) {
742        if (!e.defaultPrevented) {
743            this.onSend(e, data);
744        }
745    },
746
747    /**
748     * Progress handler.
749     *
750     * @param {Object} e
751     * @param {Object} data
752     */
753    // progress: function (e, data) {},
754
755    /**
756     * Upload done handler.
757     *
758     * @param {Object} e
759     * @param {Object} data
760     */
761    // done: function (e, data) {},
762
763    /**
764     * Upload fail handler.
765     *
766     * @param {Object} e
767     * @param {Object} data
768     */
769    // fail: function (e, data) {},
770
771    /**
772     * Upload always handler.
773     *
774     * @param {Object} e
775     * @param {Object} data
776     */
777    // always: function (e, data) {},
778
779    /**
780     * Validate file handler.
781     *
782     * @param  {Object} e
783     * @param  {Object} file
784     * @return {Boolean}
785     */
786    validate(e, file) {
787        var o = this.options;
788
789        if (o.acceptFileTypes && !o.acceptFileTypes.test(file.name)) {
790            file.error = this.trans('invalidFileType');
791        } else if (file.size !== undefined && o.maxFileSize && file.size > o.maxFileSize) {
792            file.error = this.trans('maxFileSize', { max: o.maxFileSize / 1000 });
793        } else if (file.size !== undefined && o.minFileSize && file.size < o.minFileSize) {
794            file.error = this.trans('minFileSize', { min: o.minFileSize / 1000 });
795        }
796
797        return file.error === undefined;
798    },
799
800    /**
801     * Upload fallback handler.
802     *
803     * @param {Object} e
804     * @param {String} message
805     */
806    uploadfallback(e, message) {
807        alert(message);
808    }
809};
810
811export default Filepicker;

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.