1/** 2 * ownCloud 3 * 4 * @author Bartek Przybylski, Christopher Schäpers, Thomas Tanghus 5 * @copyright Copyright (c) 2012 Bartek Przybylski [email protected] 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 9 * License as published by the Free Software Foundation; either 10 * version 3 of the License, or any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public 18 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 19 * 20 */ 21 22/* global alert */ 23 24/** 25 * this class to ease the usage of jquery dialogs 26 * @lends OC.dialogs 27 */ 28var OCdialogs = { 29 // dialog button types 30 YES_NO_BUTTONS: 70, 31 OK_BUTTONS: 71, 32 // used to name each dialog 33 dialogsCounter: 0, 34 /** 35 * displays alert dialog 36 * @param text content of dialog 37 * @param title dialog title 38 * @param callback which will be triggered when user presses OK 39 * @param modal make the dialog modal 40 */ 41 alert:function(text, title, callback, modal) { 42 this.message( 43 text, 44 title, 45 'alert', 46 OCdialogs.OK_BUTTON, 47 callback, 48 modal 49 ); 50 }, 51 /** 52 * displays info dialog 53 * @param text content of dialog 54 * @param title dialog title 55 * @param callback which will be triggered when user presses OK 56 * @param modal make the dialog modal 57 */ 58 info:function(text, title, callback, modal) { 59 this.message(text, title, 'info', OCdialogs.OK_BUTTON, callback, modal); 60 }, 61 /** 62 * displays confirmation dialog 63 * @param text content of dialog 64 * @param title dialog title 65 * @param callback which will be triggered when user presses YES or NO 66 * (true or false would be passed to callback respectively) 67 * @param modal make the dialog modal 68 */ 69 confirm:function(text, title, callback, modal) { 70 return this.message( 71 text, 72 title, 73 'notice', 74 OCdialogs.YES_NO_BUTTONS, 75 callback, 76 modal 77 ); 78 }, 79 /** 80 * displays prompt dialog 81 * @param text content of dialog 82 * @param title dialog title 83 * @param callback which will be triggered when user presses YES or NO 84 * (true or false would be passed to callback respectively) 85 * @param modal make the dialog modal 86 * @param name name of the input field 87 * @param password whether the input should be a password input 88 * @param buttonDeclineText overwrite button decline text 89 * @param buttonConfirmText overwrite button confirm text 90 */ 91 prompt: function (text, title, callback, modal, name, password, buttonDeclineText, buttonConfirmText) { 92 return $.when(this._getMessageTemplate()).then(function ($tmpl) { 93 var dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content'; 94 var dialogId = '#' + dialogName; 95 var $dlg = $tmpl.octemplate({ 96 dialog_name: dialogName, 97 title : title, 98 message : text, 99 type : 'notice' 100 }); 101 var input = $('<input/>'); 102 input.attr('type', password ? 'password' : 'text').attr('id', dialogName + '-input'); 103 var label = $('<label/>').attr('for', dialogName + '-input').text(name + ': '); 104 $dlg.append(label); 105 $dlg.append(input); 106 if (modal === undefined) { 107 modal = false; 108 } 109 $('body').append($dlg); 110 var buttonlist = [{ 111 text : buttonDeclineText ? buttonDeclineText : t('core', 'No'), 112 click: function () { 113 if (callback !== undefined) { 114 callback(false, input.val()); 115 } 116 $(dialogId).ocdialog('close'); 117 } 118 }, { 119 text : buttonConfirmText ? buttonConfirmText : t('core', 'Yes'), 120 click : function () { 121 if (callback !== undefined) { 122 callback(true, input.val()); 123 } 124 $(dialogId).ocdialog('close'); 125 }, 126 defaultButton: true 127 } 128 ]; 129 130 $(dialogId).ocdialog({ 131 closeOnEscape: true, 132 modal : modal, 133 buttons : buttonlist 134 }); 135 OCdialogs.dialogsCounter++; 136 }); 137 }, 138 /** 139 * show a file picker to pick a file from 140 * @param title dialog title 141 * @param callback which will be triggered when user presses Choose 142 * @param multiselect whether it should be possible to select multiple files 143 * @param mimetypeFilter mimetype to filter by - directories will always be included 144 * @param modal make the dialog modal 145 */ 146 filepicker:function(title, callback, multiselect, mimetypeFilter, modal) { 147 var self = this; 148 // avoid opening the picker twice 149 if (this.filepicker.loading) { 150 return; 151 } 152 this.filepicker.loading = true; 153 $.when(this._getFilePickerTemplate()).then(function($tmpl) { 154 self.filepicker.loading = false;
155 var dialogName = 'oc-dialog-filepicker-content'; 156 if(self.$filePicker) { 157 self.$filePicker.ocdialog('close'); 158 } 159 self.$filePicker = $tmpl.octemplate({ 160 dialog_name: dialogName, 161 title: title 162 }).data('path', '').data('multiselect', multiselect).data('mimetype', mimetypeFilter); 163 164 if (modal === undefined) { 165 modal = false; 166 } 167 if (multiselect === undefined) { 168 multiselect = false; 169 } 170 if (mimetypeFilter === undefined) { 171 mimetypeFilter = ''; 172 } 173 174 $('body').append(self.$filePicker); 175 176 self.$filePicker.ready(function() { 177 self.$filelist = self.$filePicker.find('.filelist'); 178 self.$dirTree = self.$filePicker.find('.dirtree'); 179 self.$dirTree.on('click', 'span:not(:last-child)', self, self._handleTreeListSelect); 180 self.$filelist.on('click', 'li', function(event) { 181 self._handlePickerClick(event, $(this)); 182 }); 183 self._fillFilePicker(''); 184 }); 185 186 // build buttons 187 var functionToCall = function() { 188 if (callback !== undefined) { 189 var datapath; 190 if (multiselect === true) { 191 datapath = []; 192 self.$filelist.find('.filepicker_element_selected .filename').each(function(index, element) { 193 datapath.push(self.$filePicker.data('path') + '/' + $(element).text()); 194 }); 195 } else { 196 datapath = self.$filePicker.data('path'); 197 datapath += '/' + self.$filelist.find('.filepicker_element_selected .filename').text(); 198 } 199 callback(datapath); 200 self.$filePicker.ocdialog('close'); 201 } 202 }; 203 var buttonlist = [{ 204 text: t('core', 'Choose'), 205 click: functionToCall, 206 defaultButton: true 207 }]; 208 209 self.$filePicker.ocdialog({ 210 closeOnEscape: true, 211 // max-width of 600 212 width: Math.min((4/5)*$(document).width(), 600), 213 height: 420, 214 modal: modal, 215 buttons: buttonlist, 216 close: function() { 217 try { 218 $(this).ocdialog('destroy').remove(); 219 } catch(e) {} 220 self.$filePicker = null; 221 } 222 }); 223 224 // We can access primary class only from oc-dialog. 225 // Hence this is one of the approach to get the choose button. 226 var getOcDialog = self.$filePicker.closest('.oc-dialog'); 227 var buttonEnableDisable = getOcDialog.find('.primary'); 228 if (self.$filePicker.data('mimetype') === "httpd/unix-directory") { 229 buttonEnableDisable.prop("disabled", false); 230 } else { 231 buttonEnableDisable.prop("disabled", true); 232 } 233 234 if (!OC.Util.hasSVGSupport()) { 235 OC.Util.replaceSVG(self.$filePicker.parent()); 236 } 237 }) 238 .fail(function(status, error) { 239 // If the method is called while navigating away 240 // from the page, it is probably not needed ;) 241 self.filepicker.loading = false; 242 if(status !== 0) { 243 alert(t('core', 'Error loading file picker template: {error}', {error: error})); 244 } 245 }); 246 }, 247 /** 248 * Displays raw dialog 249 * You better use a wrapper instead ... 250 */ 251 message:function(content, title, dialogType, buttons, callback, modal, cssClass) { 252 return $.when(this._getMessageTemplate()).then(function($tmpl) { 253 var dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content'; 254 var dialogId = '#' + dialogName; 255 var $dlg = $tmpl.octemplate({ 256 dialog_name: dialogName, 257 title: title, 258 message: content, 259 type: dialogType 260 }); 261 262 if (modal === undefined) { 263 modal = false; 264 } 265 266 $('body').append($dlg); 267 var buttonlist = []; 268 if (_.isArray(buttons)) { 269 buttonlist = buttons; 270 } else { 271 switch (buttons) { 272 case OCdialogs.YES_NO_BUTTONS: 273 buttonlist = [{ 274 text: t('core', 'No'), 275 click: function(){ 276 if (callback !== undefined) { 277 callback(false); 278 } 279 $(dialogId).ocdialog('close'); 280 } 281 }, 282 { 283 text: t('core', 'Yes'), 284 click: function(){ 285 if (callback !== undefined) { 286 callback(true); 287 } 288 $(dialogId).ocdialog('close'); 289 }, 290 defaultButton: true 291 }]; 292 break; 293 case OCdialogs.OK_BUTTON: 294 var functionToCall = function() { 295 $(dialogId).ocdialog('close'); 296 if(callback !== undefined) { 297 callback(); 298 } 299 }; 300 buttonlist[0] = { 301 text: t('core', 'Ok'), 302 click: functionToCall, 303 defaultButton: true 304 }; 305 break; 306 } 307 } 308 309 $(dialogId).ocdialog({ 310 closeOnEscape: true, 311 modal: modal, 312 buttons: buttonlist 313 }); 314 315 if (cssClass != undefined) { 316 $(dialogId).addClass(cssClass); 317 } 318 319 OCdialogs.dialogsCounter++; 320 }) 321 .fail(function(status, error) { 322 // If the method is called while navigating away from 323 // the page, we still want to deliver the message. 324 if(status === 0) { 325 alert(title + ': ' + content); 326 } else { 327 alert(t('core', 'Error loading message template: {error}', {error: error})); 328 } 329 }); 330 }, 331 _fileexistsshown: false, 332 /** 333 * Displays file exists dialog 334 * @param {object} data upload object 335 * @param {object} original file with name, size and mtime 336 * @param {object} replacement file with name, size and mtime 337 * @param {object} controller with onCancel, onSkip, onReplace and onRename methods 338 * @return {Promise} jquery promise that resolves after the dialog template was loaded 339 */ 340 fileexists:function(data, original, replacement, controller) { 341 var self = this; 342 var dialogDeferred = new $.Deferred(); 343 344 var getCroppedPreview = function(file) { 345 var deferred = new $.Deferred(); 346 // Only process image files. 347 var type = file.type && file.type.split('/').shift(); 348 if (window.FileReader && type === 'image') { 349 var reader = new FileReader(); 350 reader.onload = function (e) { 351 var blob = new Blob([e.target.result]); 352 window.URL = window.URL || window.webkitURL; 353 var originalUrl = window.URL.createObjectURL(blob); 354 var image = new Image(); 355 image.src = originalUrl; 356 image.onload = function () { 357 var url = crop(image); 358 deferred.resolve(url); 359 }; 360 }; 361 reader.readAsArrayBuffer(file); 362 } else { 363 deferred.reject(); 364 } 365 return deferred; 366 }; 367 368 var crop = function(img) { 369 var canvas = document.createElement('canvas'),
370 targetSize = 96, 371 width = img.width, 372 height = img.height, 373 x, y, size; 374 375 // Calculate the width and height, constraining the proportions 376 if (width > height) { 377 y = 0; 378 x = (width - height) / 2; 379 } else { 380 y = (height - width) / 2; 381 x = 0; 382 } 383 size = Math.min(width, height); 384 385 // Set canvas size to the cropped area 386 canvas.width = size; 387 canvas.height = size; 388 var ctx = canvas.getContext("2d"); 389 ctx.drawImage(img, x, y, size, size, 0, 0, size, size); 390 391 // Resize the canvas to match the destination (right size uses 96px) 392 resampleHermite(canvas, size, size, targetSize, targetSize); 393 394 return canvas.toDataURL("image/png", 0.7); 395 }; 396 397 /** 398 * Fast image resize/resample using Hermite filter with JavaScript. 399 * 400 * @author: ViliusL 401 * 402 * @param {*} canvas 403 * @param {number} W 404 * @param {number} H 405 * @param {number} W2 406 * @param {number} H2 407 */ 408 var resampleHermite = function (canvas, W, H, W2, H2) { 409 W2 = Math.round(W2); 410 H2 = Math.round(H2); 411 var img = canvas.getContext("2d").getImageData(0, 0, W, H); 412 var img2 = canvas.getContext("2d").getImageData(0, 0, W2, H2); 413 var data = img.data; 414 var data2 = img2.data; 415 var ratio_w = W / W2; 416 var ratio_h = H / H2; 417 var ratio_w_half = Math.ceil(ratio_w / 2); 418 var ratio_h_half = Math.ceil(ratio_h / 2); 419 420 for (var j = 0; j < H2; j++) { 421 for (var i = 0; i < W2; i++) { 422 var x2 = (i + j * W2) * 4; 423 var weight = 0; 424 var weights = 0; 425 var weights_alpha = 0; 426 var gx_r = 0; 427 var gx_g = 0; 428 var gx_b = 0; 429 var gx_a = 0; 430 var center_y = (j + 0.5) * ratio_h; 431 for (var yy = Math.floor(j * ratio_h); yy < (j + 1) * ratio_h; yy++) { 432 var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; 433 var center_x = (i + 0.5) * ratio_w; 434 var w0 = dy * dy; //pre-calc part of w 435 for (var xx = Math.floor(i * ratio_w); xx < (i + 1) * ratio_w; xx++) { 436 var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; 437 var w = Math.sqrt(w0 + dx * dx); 438 if (w >= -1 && w <= 1) { 439 //hermite filter 440 weight = 2 * w * w * w - 3 * w * w + 1; 441 if (weight > 0) { 442 dx = 4 * (xx + yy * W); 443 //alpha 444 gx_a += weight * data[dx + 3]; 445 weights_alpha += weight; 446 //colors 447 if (data[dx + 3] < 255) 448 weight = weight * data[dx + 3] / 250; 449 gx_r += weight * data[dx]; 450 gx_g += weight * data[dx + 1]; 451 gx_b += weight * data[dx + 2]; 452 weights += weight; 453 } 454 } 455 } 456 } 457 data2[x2] = gx_r / weights; 458 data2[x2 + 1] = gx_g / weights; 459 data2[x2 + 2] = gx_b / weights; 460 data2[x2 + 3] = gx_a / weights_alpha; 461 } 462 } 463 canvas.getContext("2d").clearRect(0, 0, Math.max(W, W2), Math.max(H, H2)); 464 canvas.width = W2; 465 canvas.height = H2; 466 canvas.getContext("2d").putImageData(img2, 0, 0); 467 }; 468 469 var addConflict = function($conflicts, original, replacement) { 470 471 var $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict'); 472 var $originalDiv = $conflict.find('.original'); 473 var $replacementDiv = $conflict.find('.replacement'); 474 475 $conflict.data('data',data); 476 477 $conflict.find('.filename').text(original.name); 478 $originalDiv.find('.size').text(humanFileSize(original.size)); 479 $originalDiv.find('.mtime').text(formatDate(original.mtime)); 480 // ie sucks 481 if (replacement.size && replacement.lastModifiedDate) { 482 $replacementDiv.find('.size').text(humanFileSize(replacement.size)); 483 $replacementDiv.find('.mtime').text(formatDate(replacement.lastModifiedDate)); 484 } 485 var path = original.directory + '/' +original.name; 486 487 FileList.lazyLoadPreview({ 488 path: path, 489 mime: original.mimetype, 490 etag: original.etag, 491 callback: function(url) { 492 $originalDiv.find('.icon').css('background-image', 'url("' + url + '")'); 493 } 494 }); 495 496 getCroppedPreview(replacement).then( 497 function(path){ 498 $replacementDiv.find('.icon').css('background-image','url(' + path + ')'); 499 }, function(){ 500 path = OC.MimeType.getIconUrl(replacement.type); 501 $replacementDiv.find('.icon').css('background-image','url(' + path + ')'); 502 } 503 ); 504 // connect checkboxes with labels 505 var checkboxId = $conflicts.find('.conflict').length;
506 $originalDiv.find('input:checkbox').attr('id', 'checkbox_original_'+checkboxId); 507 $replacementDiv.find('input:checkbox').attr('id', 'checkbox_replacement_'+checkboxId); 508 509 $conflicts.append($conflict); 510 511 //set more recent mtime bold 512 // ie sucks 513 if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime) { 514 $replacementDiv.find('.mtime').css('font-weight', 'bold'); 515 } else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime) { 516 $originalDiv.find('.mtime').css('font-weight', 'bold'); 517 } else { 518 //TODO add to same mtime collection? 519 } 520 521 // set bigger size bold 522 if (replacement.size && replacement.size > original.size) { 523 $replacementDiv.find('.size').css('font-weight', 'bold'); 524 } else if (replacement.size && replacement.size < original.size) { 525 $originalDiv.find('.size').css('font-weight', 'bold'); 526 } else { 527 //TODO add to same size collection? 528 } 529 530 //TODO show skip action for files with same size and mtime in bottom row 531 532 // always keep readonly files 533 534 if (original.status === 'readonly') { 535 $originalDiv 536 .addClass('readonly') 537 .find('input[type="checkbox"]') 538 .prop('checked', true) 539 .prop('disabled', true); 540 $originalDiv.find('.message') 541 .text(t('core','read-only')) 542 } 543 }; 544 //var selection = controller.getSelection(data.originalFiles); 545 //if (selection.defaultAction) { 546 // controller[selection.defaultAction](data); 547 //} else { 548 var dialogName = 'oc-dialog-fileexists-content'; 549 var dialogId = '#' + dialogName; 550 if (this._fileexistsshown) { 551 // add conflict 552 553 var $conflicts = $(dialogId+ ' .conflicts'); 554 addConflict($conflicts, original, replacement); 555 556 var count = $(dialogId+ ' .conflict').length; 557 var title = n('core', 558 '{count} file conflict', 559 '{count} file conflicts', 560 count, 561 {count:count} 562 ); 563 $(dialogId).parent().children('.oc-dialog-title').text(title); 564 565 //recalculate dimensions 566 $(window).trigger('resize'); 567 dialogDeferred.resolve(); 568 } else { 569 //create dialog 570 this._fileexistsshown = true; 571 $.when(this._getFileExistsTemplate()).then(function($tmpl) { 572 var title = t('core','One file conflict'); 573 var $dlg = $tmpl.octemplate({ 574 dialog_name: dialogName, 575 title: title, 576 type: 'fileexists', 577 578 allnewfiles: t('core','New Files'), 579 allexistingfiles: t('core','Already existing files'), 580 581 why: t('core','Which files do you want to keep?'), 582 what: t('core','If you select both versions, the copied file will have a number added to its name.') 583 }); 584 $('body').append($dlg); 585 586 if (original && replacement) { 587 var $conflicts = $dlg.find('.conflicts'); 588 addConflict($conflicts, original, replacement); 589 } 590 591 var buttonlist = [{ 592 text: t('core', 'Cancel'), 593 classes: 'cancel', 594 click: function(){ 595 if ( typeof controller.onCancel !== 'undefined') { 596 controller.onCancel(data); 597 } 598 $(dialogId).ocdialog('close'); 599 } 600 }, 601 { 602 text: t('core', 'Continue'), 603 classes: 'continue', 604 click: function(){ 605 if ( typeof controller.onContinue !== 'undefined') { 606 controller.onContinue($(dialogId + ' .conflict')); 607 } 608 $(dialogId).ocdialog('close'); 609 } 610 }]; 611 612 $(dialogId).ocdialog({ 613 width: 500, 614 closeOnEscape: true, 615 modal: true, 616 buttons: buttonlist, 617 closeButton: null, 618 close: function() { 619 self._fileexistsshown = false; 620 $(this).ocdialog('destroy').remove(); 621 } 622 }); 623 624 $(dialogId).css('height','auto'); 625 626 var $primaryButton = $dlg.closest('.oc-dialog').find('button.continue'); 627 $primaryButton.prop('disabled', true); 628 629 function updatePrimaryButton() { 630 var checkedCount = $dlg.find('.conflicts .checkbox:checked').length; 631 $primaryButton.prop('disabled', checkedCount === 0); 632 } 633 634 //add checkbox toggling actions 635 $(dialogId).find('.allnewfiles').on('click', function() { 636 var $checkboxes = $(dialogId).find('.conflict .replacement input[type="checkbox"]'); 637 $checkboxes.prop('checked', $(this).prop('checked')); 638 }); 639 $(dialogId).find('.allexistingfiles').on('click', function() { 640 var $checkboxes = $(dialogId).find('.conflict .original:not(.readonly) input[type="checkbox"]'); 641 $checkboxes.prop('checked', $(this).prop('checked')); 642 }); 643 $(dialogId).find('.conflicts').on('click', '.replacement,.original:not(.readonly)', function() { 644 var $checkbox = $(this).find('input[type="checkbox"]'); 645 $checkbox.prop('checked', !$checkbox.prop('checked')); 646 }); 647 $(dialogId).find('.conflicts').on('click', '.replacement input[type="checkbox"],.original:not(.readonly) input[type="checkbox"]', function() { 648 var $checkbox = $(this); 649 $checkbox.prop('checked', !$checkbox.prop('checked')); 650 }); 651 652 //update counters 653 $(dialogId).on('click', '.replacement,.allnewfiles', function() { 654 var count = $(dialogId).find('.conflict .replacement input[type="checkbox"]:checked').length; 655 if (count === $(dialogId+ ' .conflict').length) { 656 $(dialogId).find('.allnewfiles').prop('checked', true); 657 $(dialogId).find('.allnewfiles + .count').text(t('core','(all selected)')); 658 } else if (count > 0) { 659 $(dialogId).find('.allnewfiles').prop('checked', false); 660 $(dialogId).find('.allnewfiles + .count').text(t('core','({count}
660 selected)',{count:count})); 661 } else { 662 $(dialogId).find('.allnewfiles').prop('checked', false); 663 $(dialogId).find('.allnewfiles + .count').text(''); 664 } 665 updatePrimaryButton(); 666 }); 667 $(dialogId).on('click', '.original,.allexistingfiles', function(){ 668 var count = $(dialogId).find('.conflict .original input[type="checkbox"]:checked').length; 669 if (count === $(dialogId+ ' .conflict').length) { 670 $(dialogId).find('.allexistingfiles').prop('checked', true); 671 $(dialogId).find('.allexistingfiles + .count').text(t('core','(all selected)')); 672 } else if (count > 0) { 673 $(dialogId).find('.allexistingfiles').prop('checked', false); 674 $(dialogId).find('.allexistingfiles + .count') 675 .text(t('core','({count} selected)',{count:count})); 676 } else { 677 $(dialogId).find('.allexistingfiles').prop('checked', false); 678 $(dialogId).find('.allexistingfiles + .count').text(''); 679 } 680 updatePrimaryButton(); 681 }); 682 683 dialogDeferred.resolve(); 684 }) 685 .fail(function() { 686 dialogDeferred.reject(); 687 alert(t('core', 'Error loading file exists template')); 688 }); 689 } 690 //} 691 return dialogDeferred.promise(); 692 }, 693 _getFilePickerTemplate: function() { 694 var defer = $.Deferred(); 695 if(!this.$filePickerTemplate) { 696 var self = this; 697 $.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) { 698 self.$filePickerTemplate = $(tmpl); 699 self.$listTmpl = self.$filePickerTemplate.find('.filelist li:first-child').detach(); 700 defer.resolve(self.$filePickerTemplate); 701 }) 702 .fail(function(jqXHR, textStatus, errorThrown) { 703 defer.reject(jqXHR.status, errorThrown); 704 }); 705 } else { 706 defer.resolve(this.$filePickerTemplate); 707 } 708 return defer.promise(); 709 }, 710 _getMessageTemplate: function() { 711 var defer = $.Deferred(); 712 if(!this.$messageTemplate) { 713 var self = this; 714 $.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) { 715 self.$messageTemplate = $(tmpl); 716 defer.resolve(self.$messageTemplate); 717 }) 718 .fail(function(jqXHR, textStatus, errorThrown) { 719 defer.reject(jqXHR.status, errorThrown); 720 }); 721 } else { 722 defer.resolve(this.$messageTemplate); 723 } 724 return defer.promise(); 725 }, 726 _getFileExistsTemplate: function () { 727 var defer = $.Deferred(); 728 if (!this.$fileexistsTemplate) { 729 var self = this; 730 $.get(OC.filePath('files', 'templates', 'fileexists.html'), function (tmpl) { 731 self.$fileexistsTemplate = $(tmpl); 732 defer.resolve(self.$fileexistsTemplate); 733 }) 734 .fail(function () { 735 defer.reject(); 736 }); 737 } else { 738 defer.resolve(this.$fileexistsTemplate); 739 } 740 return defer.promise(); 741 }, 742 _getFileList: function(dir, mimeType) { 743 if (typeof(mimeType) === "string") { 744 mimeType = [mimeType]; 745 } 746 747 return $.getJSON( 748 OC.filePath('files', 'ajax', 'list.php'), 749 { 750 dir: dir, 751 mimetypes: JSON.stringify(mimeType) 752 } 753 ); 754 }, 755 756 /** 757 * fills the filepicker with files 758 */ 759 _fillFilePicker:function(dir) { 760 var dirs = []; 761 var others = []; 762 var self = this; 763 this.$filelist.empty().addClass('icon-loading'); 764 this.$filePicker.data('path', dir); 765 $.when(this._getFileList(dir, this.$filePicker.data('mimetype'))).then(function(response) { 766 767 $.each(response.data.files, function(index, file) { 768 if (file.type === 'dir') { 769 dirs.push(file); 770 } else { 771 others.push(file); 772 } 773 }); 774 775 self._fillSlug(); 776 var sorted = dirs.concat(others); 777 778 $.each(sorted, function(idx, entry) { 779 entry.icon = OC.MimeType.getIconUrl(entry.mimetype); 780 var $li = self.$listTmpl.octemplate({ 781 type: entry.type, 782 dir: dir, 783 filename: entry.name, 784 date: OC.Util.relativeModifiedDate(entry.mtime) 785 }); 786 if (entry.type === 'file') { 787 var urlSpec = { 788 file: dir + '/' + entry.name, 789 x: 32, 790 y: 32, 791 forceIcon: 0, 792 preview: 1, 793 }; 794 795 var previewURL = OC.linkToRemoteBase('dav') + '/files/' + OC.getCurrentUser().uid + dir + '/' + entry.name + '?' + $.param(urlSpec); 796 $li.find('img').attr('src', previewURL); 797 } 798 else { 799 $li.find('img').attr('src', OC.Util.replaceSVGIcon(entry.icon)); 800 } 801 self.$filelist.append($li); 802 }); 803 804 self.$filelist.removeClass('icon-loading'); 805 if (!OC.Util.hasSVGSupport()) { 806 OC.Util.replaceSVG(self.$filePicker.find('.dirtree')); 807 } 808 }); 809 }, 810 /** 811 * fills the tree list with directories 812 */ 813 _fillSlug: function() { 814 this.$dirTree.empty(); 815 var self = this; 816 var path = this.$filePicker.data('path'); 817 var $template = $('<span data-dir="{dir}">{name}</span>'); 818 if(path) { 819 var paths = path.split('/'); 820 $.each(paths, function(index, dir) { 821 dir = paths.pop(); 822 if(dir === '') { 823 return false;
824 } 825 self.$dirTree.prepend($template.octemplate({ 826 dir: paths.join('/') + '/' + dir, 827 name: dir 828 })); 829 }); 830 } 831 $template.octemplate({ 832 dir: '', 833 name: ' ' // Ugly but works ;) 834 }, {escapeFunction: null}).addClass('home svg').prependTo(this.$dirTree); 835 }, 836 /** 837 * handle selection made in the tree list 838 */ 839 _handleTreeListSelect:function(event) { 840 var self = event.data; 841 var dir = $(event.target).data('dir'); 842 self._fillFilePicker(dir); 843 var getOcDialog = this.closest('.oc-dialog'); 844 var buttonEnableDisable = $('.primary', getOcDialog); 845 if (this.$filePicker.data('mimetype') === "http/unix-directory") { 846 buttonEnableDisable.prop("disabled", false); 847 } else { 848 buttonEnableDisable.prop("disabled", true); 849 } 850 }, 851 /** 852 * handle clicks made in the filepicker 853 */ 854 _handlePickerClick:function(event, $element) { 855 var getOcDialog = this.$filePicker.closest('.oc-dialog'); 856 var buttonEnableDisable = getOcDialog.find('.primary'); 857 if ($element.data('type') === 'file') { 858 if (this.$filePicker.data('multiselect') !== true || !event.ctrlKey) { 859 this.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected'); 860 } 861 $element.toggleClass('filepicker_element_selected'); 862 buttonEnableDisable.prop("disabled", false); 863 } else if ( $element.data('type') === 'dir' ) { 864 this._fillFilePicker(this.$filePicker.data('path') + '/' + $element.data('entryname')); 865 if (this.$filePicker.data('mimetype') === "httpd/unix-directory") { 866 buttonEnableDisable.prop("disabled", false); 867 } else { 868 buttonEnableDisable.prop("disabled", true); 869 } 870 } 871 } 872};
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.