PageSourceSearch

https://portal.owncloud.com/core/js/oc-dialogs.js?v=dd2774748f47720d9deea7a9f29fea34

js owncloud.com collected 2026-10-01 10:18:14 UTC 27,006 bytes, 872 lines download raw bytes

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: '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' // 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.