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