PageSourceSearch

https://my.absa.org/lib/jquery_tiki/files.js

js absa.org collected 2026-10-01 11:36:33 UTC 35,586 bytes, 953 lines download raw bytes

1
2(function ($) {
3    var handleFiles, ProgressBar, FileList, FileListInline;
4
5    $.fileTypeIcon = function (fileId, file) {
6        if (typeof file.type == 'undefined') {
7            return $('<img height="16" width="16" title="'+ file.type +'" alt="'+ file.type +'" src="img/icons/mime/default.png">');
8        } else if(file.type.substring(0,6) == 'image/') {
9            return $('<img src="tiki-download_file.php?fileId=' + fileId + '&display&y=24" height="24">');
10        } else if(file.type == 'application/pdf') {
11            return $('<img height="16" width="16" title="application/pdf" alt="application/pdf" src="img/icons/mime/pdf.png">');
12        } else if(file.type.indexOf("sheet") != -1) {
13            return $('<img height="16" width="16" title="'+ file.type +'" alt="'+ file.type +'" src="img/icons/mime/xls.png">');
14        } else if(file.type.indexOf("zip") != -1) {
15            return $('<img height="16" width="16" title="'+ file.type +'" alt="'+ file.type +'" src="img/icons/mime/zip.png">');
16        } else if (file.type.substring(0,6) == 'video/') {
17            return $('<img height="16" width="16" title="'+ file.type +'" alt="'+ file.type +'" src="img/icons/mime/flv.png">');
18        } else if (file.type.indexOf("word") != -1) {
19            return $('<img height="16" width="16" title="'+ file.type +'" alt="'+ file.type +'" src="img/icons/mime/doc.png">');
20        } else {
21            return $('<img height="16" width="16" title="'+ file.type +'" alt="'+ file.type +'" src="img/icons/mime/default.png">');
22        }
23    };
24
25    ProgressBar = function (options) {
26        var bar = this;
27        this.segments = [];
28        this.updateSegment = function (number, current, total) {
29            bar.segments[number] = [current, total];
30            bar.update();
31        };
32        this.update = function () {
33            var total = 0, current = 0;
34            $.each(bar.segments, function (k, item) {
35                current += item[0];
36                total += item[1];
37            });
38
39            options.progress(current, total);
40
41            if (current === total) {
42                options.done();
43            }
44        };
45    };
46
47    FileList = function (options) {
48        var $list = $(options.list);
49        this.clearErrors = function () {
50            $list.find('.text-danger').remove();
51        };
52        this.addError = function (file, message) {
53            var $li = $('<li>').appendTo($list);
54
55            $li.text(file.name);
56            $li.addClass('text-danger');
57            $li.append(' (' + (message ? message : tr('uploading failed')) + ')');
58        };
59        this.addFile = function (fileId, file, title) {
60            // called for "upload in modal" tracker Files fields when file dropped or picked from the file dialog
61            var $li = $('<li>').addClass("row mb-4").appendTo($list);
62
63            var label = file.name;
64            if (title) {
65                label = title;
66            }
67            $("<div>").addClass("col-sm-12").text(label)
68                .prepend($.fileTypeIcon(fileId, file).css("margin-right", "1em"))
69                .appendTo($li);
70
71
72            if ($list.parents(".inline").length) {
73                var $fileInput = $list.parents(".files-field").find("> input");
74                var val = $fileInput.val();
75                val = val ? val + "," + fileId : fileId;
76                $fileInput.val(val);
77            } else {
78                $('<input type="hidden" name="file[]">')
79                    .attr('value', fileId)
80                    .appendTo($li);
81
82                if ($list.data("adddescription")) {
83                    $("<label>")
84                        .addClass("col-form-label col-sm-4")
85                        .text(tr("Description"))
86                        .appendTo($li);
87                    $("<div>").addClass("col-sm-12")
88                        .append($('<textarea name="description[]">')
89                            .addClass("form-control col-sm-8")
90                        )
91                        .appendTo($li);
92                }
93            }
94
95        };
96    };
97
98    handleFiles = function (input) {
99
100        var files = input.files,
101            accept = input.accept,
102            title = input.title,
103            galleryId = input.galleryId,
104            image_max_size_x = input.image_max_size_x,
105            image_max_size_y = input.image_max_size_y,
106            progressBar = input.progress,
107            ticket = input.ticket,
108            fileList = input.list,
109            $container = input.form,    // sometimes a div when inline
110            $modal = $container.parents(".modal-dialog:first");
111
112        if (! $modal.length) {
113            $modal = $container;
114        }
115
116        var uploadUrl = $.service('file', 'upload');
117
118        var restoreExif = function(sourceImageData, newImageData){
119            if (sourceImageData.indexOf('data:image/jpeg;base64,') !== 0){
120                return newImageData;
121            }
122            var sourceImageSegments = MinifyJpeg.slice2Segments(MinifyJpeg.decode64(sourceImageData.replace("data:image/jpeg;base64,", "")));
123            var exifInfo = MinifyJpeg.getExif(sourceImageSegments);
124            var newImageBinArray = MinifyJpeg.insertExif(newImageData, exifInfo);
125            return "data:image/jpeg;base64," + MinifyJpeg.encode64(newImageBinArray);
126        };
127
128        $.each(files, function (k, file) {
129            var reader = new FileReader();
130
131            progressBar.updateSegment(k, 0, file.size);
132            window.lastFile = file;
133
134            $(window).queue('process-upload', function () {
135                reader.onloadend = function (e) {
136                    var xhr, provider, sendData, data, valid = true;
137
138                    $modal.tikiModal(tr("Uploading file..."));
139
140                    xhr = jQuery.ajaxSettings.xhr();
141                    if (xhr.upload) {
142                        xhr.upload.addEventListener('progress', function (e) {
143                            if (e.lengthComputable) {
144                                progressBar.updateSegment(k, e.loaded, e.total);
145                            }
146                        }, false);
147                    }
148                    provider = function () {
149                        return xhr;
150                    };
151
152                    sendData = {
153
154                        type: 'POST',
155                        url: uploadUrl,
156                        xhr: provider,
157                        dataType: 'json',
158                        global: false,     // prevent ajaxError in tiki-jquery.js from closing the dialogs, leading to lost edits
159                        success: function (data) {
160                            fileList.addFile(data.fileId, file, title);
161                        },
162                        error: function (jqXHR, textStatus, errorThrown) {
163                            progressBar.updateSegment(k, 0, 0);
164                            fileList.addError(file, errorThrown);
165                        },
166                        complete: function (data, textStatus) {
167                            $(window).dequeue('process-upload');
168
169                            if (data.responseJSON) {
170                                ticket = data.responseJSON.ticket;
171                                // for modal
172                                if ($modal !== $container) {
173                                    $('form.file-uploader-result input[name=ticket]').val(ticket);
174                                }
175                                // for both (form when in modal, div when inline)
176                                $container.data('ticket', ticket);
177                            } else {
178                                alert(tr("There was an error: " + textStatus));
179                            }
180
181                            $modal.tikiModal();
182                        }
183                    };
184
185                    if (window.FormData) {
186                        sendData.processData = false;
187                        sendData.contentType = false;
188                        sendData.cache = false;
189
190                        sendData.data = new FormData;
191                        sendData.data.append('galleryId', galleryId);
192                        sendData.data.append('image_max_size_x', image_max_size_x);
193                        sendData.data.append('image_max_size_y', image_max_size_y);
194                        sendData.data.append('ticket', ticket);
195                        sendData.data.append('name', file.name);
196                        sendData.data.append('title', title);
197
198                        var waiting = false;
199
200                        //checking if image is uploaded to call resize function and resizing options are set by tracker
201                        // field creator
202                        if (file.type.substring(0, 6) == 'image/' && (image_max_size_x || image_max_size_y)) {
203                            waiting = true;
204                            var imageData;
205
206                            var picFile = e.target;
207                            imageData = picFile.result;
208
209                            var img = new Image();
210                            var blobImage = null;
211                            img.src = imageData;
212                            img.onload = function () {
213                                var width = parseInt(image_max_size_x);
214                                var height = parseInt(image_max_size_y);
215                                var image_x = parseInt(image_max_size_x);
216                                var image_y = parseInt(image_max_size_y);
217                                var ratio;
218                                if (img.width > image_x || img.height > image_y) {
219
220                                    if (img.width > image_x) {
221                                        width = image_x;
222                                        ratio = image_x / img.width;
223                                        height = Math.round(img.height * ratio);
224                                    }
225
226                                    if (height > image_y) {
227                                        height = image_y;
228                                        ratio = image_y / img.height;
229                                        width = Math.round(img.width * ratio);
230                                    }
231                                } else {
232                                    width = img.width;
233                                    height = img.height;
234                                }
235                                var canvas = $("<canvas/>").get(0);
236                                canvas.width = width;
237                                canvas.height = height;
238                                var context = canvas.getContext('2d');
239                                context.drawImage(img, 0, 0, width, height);
240                                var resizedImage = restoreExif(imageData, canvas.toDataURL('image/jpeg'));
241                                blobImage = dataURItoBlob(resizedImage);
242                                sendData.data.append('data', blobImage, file.name);
243                                $.ajax(sendData);
244                            }
245
246                        } else {
247                            sendData.data.append('data', file);
248                        }
249                    } else {
250                        data = e.target.result;
251                        sendData.data = {
252                            name: file.name,
253                            size: file.size,
254                            type: file.type,
255                            title: title,
256                            data: data.substr(data.indexOf('base64') + 7),
257                            galleryId: galleryId,
258                            image_max_size_x: image_max_size_x,
259                            image_max_size_y: image_max_size_y
260                        };
261                    }
262
263                    if (accept) {
264                        valid = file.type.match(new RegExp(".?(" + accept.replace('*', '.*') + ")$", "i"));
265                    }
266
267                    if (valid && !waiting) {
268                        $.ajax(sendData);
269                    } else {
270                        if (!valid) {
271                            sendData.error(null);
272                        }
273                    }
274                };
275                reader.readAsDataURL(file);
276            });
277        });
278        $(window).dequeue('process-upload');
279    };
280
281    $(document).on('submit', 'form.file-uploader', function (e) {
282        e.preventDefault();
283    });
284
285    function doUpload($form, files) {
286        var progress, list, title = "";
287
288        progress = new ProgressBar({
289            progress: function (current, total) {
290                var percentage = Math.round(current / total * 100);
291
292                $form.find('.progress').removeClass("invisible");
293                $form.find('.progress-bar')
294                    .attr('aria-valuenow', percentage)
295                    .width(percentage + '%');
296                $form.find('.progress-bar .sr-only .count')
297                    .text(percentage);
298            },
299            done: function () {
300                $form.find('.progress').addClass("invisible");
301                $form.find('.progress-bar')
302                    .attr('aria-valuenow', 0)
303                    .width('0%');
304                $form.find('.progress-bar .sr-only .count')
305                    .text('0%');
306                // Clear required title in preparation for next file
307                $('.custom-file-title-input', $form).val('');
308            }
309        });
310
311        list = new FileList({
312            list: $form.parent().find('.file-uploader-result ul')[0]
313        });
314
315        list.clearErrors();
316
317        if ($form.find('.custom-file-title-input').length) {
318            title = $form.find('.custom-file-title-input').val();
319        }
320
321        handleFiles({
322            accept: $form.find(':file').attr('accept'),
323            galleryId: $form.data('gallery-id'),
324            title: title,
325            ticket: $form.data('ticket'),
326            image_max_size_x:$form.data('image_max_size_x'),
327            image_max_size_y:$form.data('image_max_size_y'),
328            files: files,
329            progress: progress,
330            list: list,
331            form: $form
332        });
333    }
334
335    function validateTitleProvided(form) {
336        if ($('.custom-file-title', form).is(':visible')) {
337            if (!$('.custom-file-title-input', form).val()) {
338                $('.custom-file-title .feedback-required-title', form).show();
339                $('.custom-file-label', form).text('Choose file');
340                form.trigger('reset');
341                return false;
342            } else {
343                $('.custom-file-title .feedback-required-title', form).hide();
344                return true;
345            }
346        }
347        return true;
348    }
349
350    function validateOneAtTime(form, files) {
351        if ($('.custom-file-title', form).is(':visible')) {
352            if (files.length > 1) {
353                $('.custom-file-title .feedback-one-at-time', form).show();
354                $('.custom-file-label', form).text('Choose file');
355                form.trigger('reset');
356                return false;
357            } else {
358                $('.custom-file-title .feedback-one-at-time', form).hide();
359                return true;
360            }
361        }
362        return true;
363    }
364
365    $(document).on('change', '.file-uploader input[type=file]', function () {
366
367        var $clone, $form = $(this).closest("form"), progress, list;
368        if (this.files) {
369            if (!validateTitleProvided($form)) {
370                return false;
371            }
372            if (!validateOneAtTime($form, this.files)) {
373                return false;
374            }
375
376            let $inlineDiv = $(this).parents(".file-uploader.inline");
377
378            if ($inlineDiv.length) {
379                doUpload($inlineDiv, this.files);
380            } else {
381                doUpload($form, this.files);
382            }
383
384            $(this).val('');
385            $clone = $(this).clone(true);
386            $(this).replaceWith($clone);
387        }
388    });
389
390    if (window.FileReader) {
391        $(document).ready(function () {
392            $('.drop-message').show();
393        });
394        $(document).on('dragenter', '.file-uploader', function (e) {
395            e.preventDefault();
396            e.stopPropagation();
397            $(this).addClass("active");
398            return false;
399        });
400        $(document).on('dragexit dragleave', '.file-uploader', function (e) {
401            e.preventDefault();
402            e.stopPropagation();
403            $(this).removeClass("active");
404            return false;
405        });
406        $(document).on('dragover', '.file-uploader', function (e) {
407            e.preventDefault();
408            e.stopPropagation();
409            return false;
410        });
411        $(document).on('drop', '.file-uploader', function (e) {
412            var $form = $(this);
413
414            if (!validateTitleProvided($form)) {
415                return false;
416            }
417
418            e.preventDefault();
419            e.stopPropagation();
420            $(this).removeClass("active");
421
422            var dataTransfer = e.dataTransfer;
423            if (! dataTransfer) {
424                dataTransfer = e.originalEvent.dataTransfer;
425            }
426
427            if (dataTransfer && dataTransfer.files) {
428                if (!validateOneAtTime($form, dataTransfer.files)) {
429                    return false;
430                }
431                doUpload($form, dataTransfer.files);
432            }
433            return false;
434        });
435    }
436
437    function browserRemoveFile(link)
438    {
439        var list = $(link).closest('ul');
440        $(link).closest('li').remove();
441
442        list.closest('.file-browser').trigger('selection-update');
443    }
444
445    function browserAddFile(link)
446    {
447        var content = $(link).closest('.media-body').clone(true),
448            icon = $(link).closest('.media, .panel').find('.media-object, .panel-body a').children('img').clone(true).width('16px'),
449            nav = $(link).closest('.file-browser').find('.selection ul'),
450            item = $('<li>'),
451            a = $('<a>').text($(link).text()),
452            id = $(link).data('object'),
453            limit = nav.closest('form').data('limit'),
454            current = nav.find('input[type=hidden]').filter(function () {
455                return parseInt($(this).val(), 10) === id;
456            });
457
458        if (current.length > 0) {
459            // Already in the list
460            browserRemoveFile(current[0]);
461            return;
462        }
463
464        if (limit === 1) {
465            nav.empty();
466        } else if (limit && nav.children('li').length >= limit) {
467            alert(nav.closest('form').data('limit-reached-message'));
468            return;
469        }
470
471        a
472            .prepend(' ')
473            .prepend(icon);
474        item.append(a);
475        nav.append(item);
476
477        item.append($('<input type="hidden" name="file[]">')
478            .attr('value', id));
479
480        nav.closest('.file-browser').trigger('selection-update');
481    }
482
483    $(document).on('selection-update', '.file-browser', function (e) {
484        var selection = $('.selection input[type=hidden]', this).map(function () {
485            return parseInt($(this).val(), 10);
486        });
487
488        $('.gallery-list .media-heading a, .gallery-list .panel-body a', this).each(function () {
489            var id = $(this).data('object');
490            $(this).closest('.media').toggleClass('bg-info', -1 !== $.inArray(id, selection));
491            $(this).closest('.panel').toggleClass('panel-info', -1 !== $.inArray(id, selection));
492        });
493        $('.selection', this).toggleClass("invisible", selection.length === 0);
494    });
495
496    $(document).on('click', '.file-browser .gallery-list .pagination a', function (e) {
497        e.preventDefault();
498        $(this).closest('.modal').animate({ scrollTop: 0 }, 'slow');
499        $(this).closest('.gallery-list')
500            .tikiModal(tr('Loading...'))
501            .load($(this).attr('href'), function () {
502                $(this).tikiModal('');
503                $(this).closest('.file-browser').trigger('selection-update');
504            });
505    });
506
507    $(document).on('click', '.file-browser .gallery-list .media-heading a, .file-browser .gallery-list .panel-body a', function (e) {
508        e.preventDefault();
509        e.stopPropagation();
510        browserAddFile(this);
511    });
512    $(document).on('click', '.file-browser .gallery-list .media, .file-browser .gallery-list .panel', function (e) {
513        e.preventDefault();
514        $('.media-heading a, .panel-body a', this).click();
515    });
516
517    $(document).on('click', '.file-browser .selection a', function (e) {
518        e.preventDefault();
519        browserRemoveFile(this);
520    });
521
522    $(document).on('submit', '.file-browser .form-inline', function (e) {
523        e.preventDefault();
524        $(this).closest('.file-browser').find('.gallery-list')
525            .tikiModal(tr('Loading...'))
526            .load($(this).attr('action'), $(this).serialize(), function () {
527                $(this).tikiModal('');
528                $(this).closest('.file-browser').trigger('selection-update');
529            });
530    });
531
532    $(document).on('click', '.file-browser .submit .upload-files', function (e) {
533        var $list = $(this).closest('.file-browser').find('.selection ul'),
534            handler = $.clickModal({
535                success: function (data) {
536                    $.each(data.files, function (k, file) {
537                        $('<li>')
538                            .append($('<a href="#">')
539                                .data('object', file.fileId)
540                                .data('type', 'file')
541                                .text(file.label))
542                            .append($('<input type="hidden" name="file[]">')
543                                .attr('value', file.fileId))
544                            .appendTo($list);
545
546                        $list.closest('.file-browser').trigger('selection-update');
547                    });
548                    $.closeModal();
549                }
550            });
551
552        handler.apply(this, arguments);
553    });
554
555    // File selector component
556    $(document).on('click', '.file-selector a', function () {
557        if (! $(this).data('initial-href')) {
558            $(this).data('initial-href', $(this).attr('href'));
559        }
560
561        // Before the dialog handler triggers, replace the href with one including current files
562        $(this).attr('href', $(this).data('initial-href') + '&file=' + $(this).parent().children('input').val());
563    });
564    $(document).on('click', '.file-selector a', $.clickModal({
565        size: 'modal-lg',
566        success: function (data) {
567            var files = [];
568            $.each(data.files, function (k, f) {
569                files.push(f.fileId);
570            });
571            $(this).parent().children('input').val(files.join(','));
572            $(this).text($(this).text().replace(/\d+/, files.length));
573            $.closeModal();
574        }
575    }));
576})(jQuery);
577
578function dataURItoBlob(dataURI) {
579
580
581    // convert base64/URLEncoded data component to raw binary data held in a string
582    var byteString;
583    if (dataURI.split(',')[0].indexOf('base64') >= 0) {
584        byteString = atob(dataURI.split(',')[1]);
585    } else {
586        byteString = unescape(dataURI.split(',')[1]);
587    }
588
589    // write the bytes of the string to a typed array
590    var ia = new Uint8Array(byteString.length);
591    for (var i = 0; i < byteString.length; i++) {
592        ia[i] = byteString.charCodeAt(i);
593    }
594
595    return new Blob([ia],{ type:'image/jpeg' });
596}
597
598/* To minify a jpeg image without loosing EXIF.
599 * TESTED(24/01/2013): FireFox, GoogleChrome, IE10, Opera
600 * Copyright (c) 2013 hMatoba
601 * Released under the MIT license : https://github.com/hMatoba/MinifyJpegAsync/blob/master/LICENSE.txt
602 *
603 * To minify jpeg image:
604 *     minified = MinifyJpeg.minify(image, length);
605 *     xhr.send(minified.buffer);
606 *     enc = "data:image/jpeg;base64," + MinifyJpeg.encode64(minified);
607 *     html = '<img src="' + enc + '">';
608 * MinifyJpeg.minify() - return Uint8Array
609 * image - image base64encoded, it can be obtained "FileReader().readAsDataURL(f)"
610 * length - the long side length of the rectangle
611 * MinifyJpeg.encode64() - convert array to base64encoded string
612 */
613var MinifyJpeg = (function () {
614
615    var that = {};
616
617    that.KEY_STR = "ABCDEFGHIJKLMNOP" +
618        "QRSTUVWXYZabcdef" +
619        "ghijklmnopqrstuv" +
620        "wxyz0123456789+/" +
621        "=";
622
623    that.SOF = [192, 193, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207];
624
625    that.minify = function (image, new_size) {
626        var newImage, rawImage;
627
628        if (image instanceof ArrayBuffer) {
629            if (image[0] == 255 && image[1] == 216) {
630                rawImage = [];
631                for (var i = 0; i < image.byteLength; i++) {
632                    rawImage[i] = image[i];
633                }
634            }
635            else {
636                throw "MinifyJpeg.minify got a not JPEG data";
637            }
638        }
639        else {
640            if (typeof(image) === "string") {
641                if (!image.match("data:image/jpeg;base64,")) {
642                    throw "MinifyJpeg.minify got a not JPEG data";
643                }
644                else {
645                    rawImage = that.decode64(image.replace("data:image/jpeg;base64,", ""));
646                }
647            }
648            else {
649                throw "First argument must be 'DataURL string' or ArrayBuffer.";
650            }
651        }
652
653        var segments = that.slice2Segments(rawImage);
654        var NEW_SIZE = parseInt(new_size);
655        var size = that.imageSizeFromSegments(segments);
656        var chouhen = (size[0] >= size[1]) ? size[0] : size[1];
657        if (chouhen < NEW_SIZE) {
658            return new Uint8Array(rawImage);
659        }
660
661        var exif = that.getExif(segments);
662        var resized = that.resizeImage(rawImage, segments, NEW_SIZE);
663
664        if (exif.length) {
665            newImage = that.insertExif(resized, exif);
666        }
667        else {
668            newImage = new Uint8Array(that.decode64(resized.replace("data:image/jpeg;base64,", "")));
669        }
670
671        return newImage;
672    };
673
674    that.getImageSize = function (imageArray) {
675        var segments = that.slice2Segments(imageArray);
676        return that.imageSizeFromSegments(segments);
677    };
678
679    that.slice2Segments = function (rawImageArray) {
680        var head = 0,
681            segments = [];
682
683        while (1) {
684            if (rawImageArray[head] == 255 & rawImageArray[head + 1] == 218) {
685                break;
686            }
687            if (rawImageArray[head] == 255 & rawImageArray[head + 1] == 216) {
688                head += 2;
689            }
690            else {
691                var length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3],
692                    endPoint = head + length + 2,
693                    seg = rawImageArray.slice(head, endPoint);
694                segments.push(seg);
695                head = endPoint;
696            }
697            if (head > rawImageArray.length) {
698                break;
699            }
700        }
701
702        return segments;
703    };
704
705    that.imageSizeFromSegments = function (segments) {
706        for (var x = 0; x < segments.length; x++) {
707            var seg = segments[x];
708            if (that.SOF.indexOf(seg[1]) >= 0) {
709                var height = seg[5] * 256 + seg[6],
710                    width = seg[7] * 256 + seg[8];
711                break;
712            }
713        }
714        return [width, height];
715    };
716
717    that.encode64 = function (input) {
718        var output = "",
719            chr1, chr2, chr3 = "",
720            enc1, enc2, enc3, enc4 = "",
721            i = 0;
722
723        do {
724            chr1 = input[i++];
725            chr2 = input[i++];
726            chr3 = input[i++];
727
728            enc1 = chr1 >> 2;
729            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
730            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
731            enc4 = chr3 & 63;
732
733            if (isNaN(chr2)) {
734                enc3 = enc4 = 64;
735            } else {
736                if (isNaN(chr3)) {
737                    enc4 = 64;
738                }
739            }
740
741            output += that.KEY_STR.charAt(enc1) +
742                that.KEY_STR.charAt(enc2) +
743                that.KEY_STR.charAt(enc3) +
744                that.KEY_STR.charAt(enc4);
745            chr1 = chr2 = chr3 = "";
746            enc1 = enc2 = enc3 = enc4 = "";
747        } while (i < input.length);
748        return output;
749    };
750
751    that.decode64 = function (input) {
752        var chr1, chr2, chr3 = "",
753            enc1, enc2, enc3, enc4 = "",
754            i = 0,
755            buf = [];
756
757        // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
758        var base64test = /[^A-Za-z0-9\+\/\=]/g;
759        if (base64test.exec(input)) {
760            alert("There were invalid base64 characters in the input text.\n" +
761                "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
762                "Expect errors in decoding.");
763        }
764        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
765
766        do {
767            enc1 = that.KEY_STR.indexOf(input.charAt(i++));
768            enc2 = that.KEY_STR.indexOf(input.charAt(i++));
769            enc3 = that.KEY_STR.indexOf(input.charAt(i++));
770            enc4 = that.KEY_STR.indexOf(input.charAt(i++));
771
772            chr1 = (enc1 << 2) | (enc2 >> 4);
773            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
774            chr3 = ((enc3 & 3) << 6) | enc4;
775
776            buf.push(chr1);
777
778            if (enc3 != 64) {
779                buf.push(chr2);
780            }
781            if (enc4 != 64) {
782                buf.push(chr3);
783            }
784
785            chr1 = chr2 = chr3 = "";
786            enc1 = enc2 = enc3 = enc4 = "";
787
788        } while (i < input.length);
789
790        return buf;
791    };
792
793    that.resizeImage = function (rawImage, segments, NEW_SIZE) {
794        var jpe, ctx, srcImg;
795        var size = that.imageSizeFromSegments(segments),
796            width = size[0],
797            height = size[1],
798            chouhen = (width >= height) ? width : height,
799            newSize = NEW_SIZE,
800            resizing = 1,
801            scale = parseFloat(newSize) / chouhen,
802            newWidth = parseInt(parseFloat(newSize) / chouhen * width),
803            newHeight = parseInt(parseFloat(newSize) / chouhen * height);
804
805        if (resizing == 1) // bilinear
806        {
807            var canvas = document.createElement('canvas');
808            canvas.width = width;
809            canvas.height = height;
810
811            jpe = new JpegImage();
812            ctx = canvas.getContext("2d");
813            srcImg = ctx.getImageData(0, 0, width, height);
814            jpe.parse(new Uint8Array(rawImage));
815            jpe.copyToImageData(srcImg);
816
817            var newCanvas = document.createElement('canvas');
818            newCanvas.width = newWidth;
819            newCanvas.height = newHeight;
820            var newCtx = newCanvas.getContext("2d");
821            var destImg = newCtx.createImageData(newWidth, newHeight);
822            that.bilinear(srcImg, destImg, scale);
823
824            newCtx.putImageData(destImg, 0, 0);
825            var resizedImage = newCanvas.toDataURL("image/jpeg");
826        }
827        else // nearest neighbor?
828        {
829            canvas = document.createElement('canvas');
830            canvas.width = newWidth;
831            canvas.height = newHeight;
832
833            jpe = new JpegImage();
834            ctx = canvas.getContext("2d");
835            d = ctx.getImageData(0, 0, newWidth, newHeight);
836            jpe.parse(new Uint8Array(rawImage));
837            jpe.copyToImageData(d);
838            ctx.putImageData(d, 0, 0);
839
840            resizedImage = canvas.toDataURL("image/jpeg");
841        }
842
843        return resizedImage;
844    };
845
846    that.getExif = function (segments) {
847        var seg;
848        for (var x = 0; x < segments.length; x++) {
849            seg = segments[x];
850            if (seg[0] == 255 & seg[1] == 225) //(ff e1)
851            {
852                return seg;
853            }
854        }
855        return [];
856    };
857
858    that.insertExif = function (imageStr, exifArray) {
859        var buf = that.decode64(imageStr.replace("data:image/jpeg;base64,", "")),
860            separatePoint = buf.indexOf(255, 3),
861            mae = buf.slice(0, separatePoint),
862            ato = buf.slice(separatePoint),
863            array = mae.concat(exifArray, ato);
864
865        aBuffer = new Uint8Array(array);
866
867        return aBuffer;
868    };
869
870    // compute vector index from matrix one
871    that.ivect = function (ix, iy, w) {
872        // byte array, r,g,b,a
873        return ((ix + w * iy) * 4);
874    };
875
876    that.bilinear = function (srcImg, destImg, scale) {
877        // c.f.: wikipedia english article on bilinear interpolation
878        //log.debug("in bilinear");
879        // taking the unit square
880        function inner(f00, f10, f01, f11, x, y)
881        {
882            var un_x = 1.0 - x;
883            var un_y = 1.0 - y;
884            return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y);
885        }
886
887        var srcWidth = srcImg.width;
888        var srcHeight = srcImg.height;
889        var srcData = srcImg.data;
890        var dstData = destImg.data;
891        var i, j;
892        var iyv, iy0, iy1, ixv, ix0, ix1;
893        var idxD, idxS00, idxS10, idxS01, idxS11;
894        var dx, dy;
895        for (i = 0; i < destImg.height; ++i) {
896            iyv = (i + 0.5) / scale - 0.5;
897            iy0 = Math.floor(iyv);
898            iy1 = (Math.ceil(iyv) > (srcHeight - 1) ? (srcHeight - 1) : Math.ceil(iyv));
899            for (j = 0; j < destImg.width; ++j) {
900                ixv = (j + 0.5) / scale - 0.5;
901                ix0 = Math.floor(ixv);
902                ix1 = (Math.ceil(ixv) > (srcWidth - 1) ? (srcWidth - 1) : Math.ceil(ixv));
903                idxD = that.ivect(j, i, destImg.width);
904                idxS00 = that.ivect(ix0, iy0, srcWidth);
905                idxS10 = that.ivect(ix1, iy0, srcWidth);
906                idxS01 = that.ivect(ix0, iy1, srcWidth);
907                idxS11 = that.ivect(ix1, iy1, srcWidth);
908                // log.debug(sprintf("bilinear: idx: D: %d, S00: %d, S10: %d, S01: %d, S11: %d", idxD, idxS00, idxS10,
909                // idxS01, idxS11));
910                dx = ixv - ix0;
911                dy = iyv - iy0;
912
913                //r
914                dstData[idxD] = inner(srcData[idxS00], srcData[idxS10],
915                    srcData[idxS01], srcData[idxS11], dx, dy
916                );
917
918                //g
919                dstData[idxD + 1] = inner(srcData[idxS00 + 1], srcData[idxS10 + 1],
920                    srcData[idxS01 + 1], srcData[idxS11 + 1], dx, dy
921                );
922
923                //b
924                dstData[idxD + 2] = inner(srcData[idxS00 + 2], srcData[idxS10 + 2],
925                    srcData[idxS01 + 2], srcData[idxS11 + 2], dx, dy
926                );
927
928                //a
929                dstData[idxD + 3] = inner(srcData[idxS00 + 3], srcData[idxS10 + 3],
930                    srcData[idxS01 + 3], srcData[idxS11 + 3], dx, dy
931                );
932            }
933        }
934    };
935    return that;
936})();
937
938function processFgalSyntax(fileInfo, theSyntax) {
939    var key, part, syntax = "";
940    if (typeof fileInfo === "string") {
941        fileInfo = JSON.parse(fileInfo);
942    }
943    syntax = syntax ? decodeURIComponent(syntax[1]) : (theSyntax || fileInfo.syntax || fileInfo.wiki_syntax);
944    for (key in fileInfo) {
945        if (fileInfo.hasOwnProperty(key)) {
946            part = "{" + key + "}";
947            if (syntax.indexOf(part) > -1) {
948                syntax = syntax.replace(part, fileInfo[key]);
949            }
950        }
951    }
952    return syntax;
953}

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.