1/** 2 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors 3 * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc. 4 * SPDX-License-Identifier: AGPL-3.0-or-later 5 */ 6 7(function() { 8 /** 9 * The FileSummary class encapsulates the file summary values and 10 * the logic to render it in the given container 11 * 12 * @constructs FileSummary 13 * @memberof OCA.Files 14 * 15 * @param $tr table row element 16 * @param {OC.Backbone.Model} [options.filesConfig] files app configuration 17 */ 18 var FileSummary = function($tr, options) { 19 options = options || {}; 20 var self = this; 21 this.$el = $tr; 22 var filesConfig = options.config; 23 if (filesConfig) { 24 this._showHidden = !!filesConfig.show_hidden; 25 window._nc_event_bus.subscribe('files:config:updated', ({ key, value }) => { 26 if (key === 'show_hidden') { 27 self._showHidden = !!value; 28 self.update(); 29 } 30 }); 31 } 32 this.clear(); 33 this.render(); 34 }; 35 36 FileSummary.prototype = { 37 _showHidden: null, 38 39 summary: { 40 totalFiles: 0, 41 totalDirs: 0, 42 totalHidden: 0, 43 totalSize: 0, 44 filter:'', 45 sumIsPending:false 46 }, 47 48 /** 49 * Returns whether the given file info must be hidden 50 * 51 * @param {OC.Files.FileInfo} fileInfo file info 52 * 53 * @return {boolean} true if the file is a hidden file, false otherwise 54 */ 55 _isHiddenFile: function(file) { 56 return file.name && file.name.charAt(0) === '.'; 57 }, 58 59 /** 60 * Adds file 61 * @param {OC.Files.FileInfo} file file to add 62 * @param {boolean} update whether to update the display 63 */ 64 add: function(file, update) { 65 if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) { 66 return; 67 } 68 if (file.type === 'dir' || file.mime === 'httpd/unix-directory') { 69 this.summary.totalDirs++; 70 } 71 else { 72 this.summary.totalFiles++; 73 } 74 if (this._isHiddenFile(file)) { 75 this.summary.totalHidden++; 76 } 77 78 var size = parseInt(file.size, 10) || 0; 79 if (size >=0) { 80 this.summary.totalSize += size; 81 } else { 82 this.summary.sumIsPending = true; 83 } 84 if (!!update) { 85 this.update(); 86 } 87 }, 88 /** 89 * Removes file 90 * @param {OC.Files.FileInfo} file file to remove 91 * @param {boolean} update whether to update the display 92 */ 93 remove: function(file, update) { 94 if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) { 95 return; 96 } 97 if (file.type === 'dir' || file.mime === 'httpd/unix-directory') { 98 this.summary.totalDirs--; 99 } 100 else { 101 this.summary.totalFiles--; 102 } 103 if (this._isHiddenFile(file)) { 104 this.summary.totalHidden--; 105 } 106 var size = parseInt(file.size, 10) || 0; 107 if (size >=0) { 108 this.summary.totalSize -= size; 109 } 110 if (!!update) { 111 this.update(); 112 } 113 }, 114 setFilter: function(filter, files){ 115 this.summary.filter = filter.toLowerCase(); 116 this.calculate(files); 117 }, 118 /** 119 * Returns the total of files and directories 120 */ 121 getTotal: function() { 122 return this.summary.totalDirs + this.summary.totalFiles; 123 }, 124 /** 125 * Recalculates the summary based on the given files array 126 * @param files array of files 127 */ 128 calculate: function(files) { 129 var file; 130 var summary = { 131 totalDirs: 0, 132 totalFiles: 0, 133 totalHidden: 0, 134 totalSize: 0, 135 filter: this.summary.filter, 136 sumIsPending: false 137 }; 138 139 for (var i = 0; i < files.length; i++) { 140 file = files[i]; 141 if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) { 142 continue; 143 } 144 if (file.type === 'dir' || file.mime === 'httpd/unix-directory') { 145 summary.totalDirs++; 146 } 147 else { 148 summary.totalFiles++; 149 } 150 if (this._isHiddenFile(file)) { 151 summary.totalHidden++; 152 } 153 var size = parseInt(file.size, 10) || 0; 154 if (size >=0) { 155 summary.totalSize += size; 156 } else { 157 summary.sumIsPending = true; 158 } 159 } 160 this.setSummary(summary); 161 }, 162 /** 163 * Clears the summary 164 */ 165 clear: function() { 166 this.calculate([]); 167 }, 168 /** 169 * Sets the current summary values 170 * @param summary map 171 */ 172 setSummary: function(summary) { 173 this.summary = summary; 174 if (typeof this.summary.filter === 'undefined') { 175 this.summary.filter = ''; 176 } 177 this.update(); 178 }, 179 180 _infoTemplate: function(data) { 181 /* NOTE: To update the template make changes in filesummary.handlebars 182 * and run: 183 * 184 * handlebars -n OCA.Files.FileSummary.Templates filesummary.handlebars -f filesummary_template.js 185 */ 186 return OCA.Files.Templates['filesummary'](_.extend({ 187 connectorLabel: t('files', '{dirs} and {files}', {dirs: '', files: ''}) 188 }, data)); 189 }, 190 191 /** 192 * Renders the file summary element 193 */ 194 update: function() { 195 if (!this.$el) { 196 return; 197 } 198 if (!this.summary.totalFiles && !this.summary.totalDirs) { 199 this.$el.addClass('hidden'); 200 return; 201 } 202 // There's a summary and data -> Update the summary 203 this.$el.removeClass('hidden'); 204 var $dirInfo = this.$el.find('.dirinfo'); 205 var $fileInfo = this.$el.find('.fileinfo'); 206 var $connector = this.$el.find('.connector'); 207 var $filterInfo = this.$el.find('.filter'); 208 var $hiddenInfo = this.$el.find('.hiddeninfo'); 209 210 // Substitute old content with new translations 211 $dirInfo.html(n('files', '%n folder', '%n folders', this.summary.totalDirs)); 212 $fileInfo.html(n('files', '%n file', '%n files', this.summary.totalFiles)); 213 $hiddenInfo.html(' (' + n('files', 'including %n hidden', 'including %n hidden', this.summary.totalHidden) + ')'); 214 var fileSize = this.summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(this.summary.totalSize, false, false); 215 this.$el.find('.filesize').html(fileSize); 216 217 // Show only what's necessary (may be hidden) 218 if (this.summary.totalDirs === 0) { 219 $dirInfo.addClass('hidden'); 220 $connector.addClass('hidden'); 221 } else { 222 $dirInfo.removeClass('hidden'); 223 } 224 if (this.summary.totalFiles === 0) { 225 $fileInfo.addClass('hidden'); 226 $connector.addClass('hidden'); 227 } else { 228 $fileInfo.removeClass('hidden'); 229 } 230 if (this.summary.totalDirs > 0 && this.summary.totalFiles > 0) { 231 $connector.removeClass('hidden'); 232 } 233 $hiddenInfo.toggleClass('hidden', this.summary.totalHidden === 0 || this._showHidden) 234 if (this.summary.filter === '') { 235 $filterInfo.html(''); 236 $filterInfo.addClass('hidden'); 237 } else { 238 $filterInfo.html(' ' + n('files', 'matches "{filter}"', 'match "{filter}"', this.summary.totalDirs + this.summary.totalFiles, {filter: this.summary.filter})); 239 $filterInfo.removeClass('hidden'); 240 } 241 }, 242 render: function() { 243 if (!this.$el) { 244 return; 245 } 246 var summary = this.summary; 247 248 // don't show the filesize column, if filesize is NaN (e.g. in trashbin) 249 var fileSize = ''; 250 if (!isNaN(summary.totalSize)) { 251 fileSize = summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(summary.totalSize, false, false); 252 fileSize = '<td class="filesize">' + fileSize + '</td>'; 253 } 254 255 var $summary = $( 256 '<td class="filesummary">'+ this._infoTemplate() + '</td>' + 257 fileSize + 258 '<td class="date"></td>' 259 ); 260 this.$el.addClass('hidden'); 261 this.$el.append($summary); 262 this.update(); 263 } 264 }; 265 OCA.Files.FileSummary = FileSummary; 266})(); 267
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.