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