1/** 2 * @class elFinder command "rm" 3 * Delete files 4 * 5 * @author Dmitry (dio) Levashov 6 * @author Naoki Sawada 7 **/ 8elFinder.prototype.commands.rm = function() { 9 "use strict"; 10 var self = this, 11 fm = this.fm, 12 tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"></span>{title}<div class="elfinder-rm-desc">{desc}</div></div>', 13 confirm = function(dfrd, targets, files, tHash, addTexts) { 14 var cnt = targets.length, 15 cwd = fm.cwd().hash, 16 descs = [], 17 spinner = fm.i18n('calc') + '<span class="elfinder-spinner"></span>', 18 dialog, text, tmb, size, f, fname; 19 20 if (cnt > 1) { 21 size = 0; 22 jQuery.each(files, function(h, f) { 23 if (f.size && f.size != 'unknown' && f.mime !== 'directory') { 24 var s = parseInt(f.size); 25 if (s >= 0 && size >= 0) { 26 size += s; 27 } 28 } else { 29 size = 'unknown'; 30 return false; 31 } 32 }); 33 getSize = (size === 'unknown'); 34 descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(size))); 35 text = [jQuery(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))]; 36 } else { 37 f = files[0]; 38 tmb = fm.tmb(f); 39 getSize = (f.mime === 'directory'); 40 descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(f.size))); 41 descs.push(fm.i18n('modify')+': '+fm.formatDate(f)); 42 fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '​$1'); 43 text = [jQuery(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))]; 44 } 45 46 if (addTexts) { 47 text = text.concat(addTexts); 48 } 49 50 text.push(tHash? 'confirmTrash' : 'confirmRm'); 51 52 dialog = fm.confirm({ 53 title : self.title, 54 text : text, 55 accept : { 56 label : 'btnRm', 57 callback : function() { 58 if (tHash) { 59 self.toTrash(dfrd, targets, tHash); 60 } else { 61 remove(dfrd, targets); 62 } 63 } 64 }, 65 cancel : { 66 label : 'btnCancel', 67 callback : function() { 68 fm.unlockfiles({files : targets}); 69 if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) { 70 fm.select({selected : targets}); 71 } else { 72 fm.selectfiles({files : targets}); 73 } 74 dfrd.reject(); 75 } 76 } 77 }); 78 // load thumbnail 79 if (tmb) { 80 jQuery('<img/>') 81 .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); }) 82 .attr('src', tmb.url); 83 } 84 85 if (getSize) { 86 getSize = fm.getSize(jQuery.map(files, function(f) { return f.mime === 'directory'? f.hash : null; })).done(function(data) { 87 dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+data.formated); 88 }).fail(function() { 89 dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+fm.i18n('unknown')); 90 }).always(function() { 91 getSize = false; 92 }); 93 } 94 }, 95 toTrash = function(dfrd, targets, tHash) { 96 var dsts = {}, 97 itemCnt = targets.length, 98 maxCnt = self.options.toTrashMaxItems, 99 checkDirs = [], 100 reqDfd = jQuery.Deferred(), 101 req, dirs, cnt; 102 103 if (itemCnt > maxCnt) { 104 self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]); 105 return; 106 } 107 108 // Directory preparation preparation and directory enumeration 109 jQuery.each(targets, function(i, h) { 110 var file = fm.file(h), 111 path = fm.path(h).replace(/\\/g, '/'), 112 m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/); 113 114 if (file) { 115 if (m) { 116 m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1'); 117 if (! dsts[m[1]]) { 118 dsts[m[1]] = []; 119 } 120 dsts[m[1]].push(h); 121 } 122 if (file.mime === 'directory') { 123 checkDirs.push(h); 124 } 125 } 126 }); 127 128 // Check directory information 129 if (checkDirs.length) { 130 req = fm.request({ 131 data : {cmd : 'size', targets : checkDirs}, 132 notify : {type: 'readdir', cnt: 1, hideCnt: true}, 133 preventDefault : true 134 }).done(function(data) { 135 var cnt = 0; 136 data.fileCnt && (cnt += parseInt(data.fileCnt)); 137 data.dirCnt && (cnt += parseInt(data.dirCnt)); 138 reqDfd[cnt > maxCnt ? 'reject' : 'resolve'](); 139 }).fail(function() { 140 reqDfd.reject(); 141 }); 142 setTimeout(function() { 143 var xhr = (req && req.xhr)? req.xhr : null; 144 if (xhr && xhr.state() == 'pending') { 145 req.syncOnFail(false); 146 req.reject(); 147 reqDfd.reject(); 148 } 149 }, self.options.infoCheckWait * 1000); 150 } else { 151 reqDfd.resolve(); 152 } 153 154 // Directory creation and paste command execution 155 reqDfd.done(function() { 156 dirs = Object.keys(dsts); 157 cnt = dirs.length; 158 if (cnt) { 159 fm.request({ 160 data : {cmd : 'mkdir', target : tHash, dirs : dirs}, 161 notify : {type : 'chkdir', cnt : cnt}, 162 preventFail : true 163 }) 164 .fail(function(error) { 165 dfrd.reject(error); 166 fm.unlockfiles({files : targets}); 167 }) 168 .done(function(data) { 169 var margeRes = function(data, phash, reqData) {
170 var undo, prevUndo, redo, prevRedo; 171 jQuery.each(data, function(k, v) { 172 if (Array.isArray(v)) { 173 if (res[k]) { 174 res[k] = res[k].concat(v); 175 } else { 176 res[k] = v; 177 } 178 } 179 }); 180 if (data.sync) { 181 res.sync = 1; 182 } 183 if (data.added && data.added.length) { 184 undo = function() { 185 var targets = [], 186 dirs = jQuery.map(data.added, function(f) { return f.mime === 'directory'? f.hash : null; }); 187 jQuery.each(data.added, function(i, f) { 188 if (jQuery.inArray(f.phash, dirs) === -1) { 189 targets.push(f.hash); 190 } 191 }); 192 return fm.exec('restore', targets, {noToast: true}); 193 }; 194 redo = function() { 195 return fm.request({ 196 data : reqData, 197 notify : {type : 'redo', cnt : targets.length} 198 }); 199 }; 200 if (res.undo) { 201 prevUndo = res.undo; 202 res.undo = function() { 203 undo(); 204 prevUndo(); 205 }; 206 } else { 207 res.undo = undo; 208 } 209 if (res.redo) { 210 prevRedo = res.redo; 211 res.redo = function() { 212 redo(); 213 prevRedo(); 214 }; 215 } else { 216 res.redo = redo; 217 } 218 } 219 }, 220 err = ['errTrash'], 221 res = {}, 222 hasNtf = function() { 223 return fm.ui.notify.children('.elfinder-notify-trash').length; 224 }, 225 hashes, tm, prg, prgSt; 226 227 if (hashes = data.hashes) { 228 prg = 1 / cnt * 100; 229 prgSt = cnt === 1? 100 : 5; 230 tm = setTimeout(function() { 231 fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt}); 232 }, fm.notifyDelay); 233 jQuery.each(dsts, function(dir, files) { 234 var phash = fm.file(files[0]).phash, 235 reqData; 236 if (hashes[dir]) { 237 reqData = {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1}; 238 fm.request({ 239 data : reqData, 240 preventDefault : true 241 }) 242 .fail(function(error) { 243 if (error) { 244 err = err.concat(error); 245 } 246 }) 247 .done(function(data) { 248 data = fm.normalize(data); 249 fm.updateCache(data); 250 margeRes(data, phash, reqData); 251 if (data.warning) { 252 err = err.concat(data.warning); 253 delete data.warning; 254 } 255 // fire some event to update cache/ui 256 data.removed && data.removed.length && fm.remove(data); 257 data.added && data.added.length && fm.add(data); 258 data.changed && data.changed.length && fm.change(data); 259 // fire event with command name 260 fm.trigger('paste', data); 261 // fire event with command name + 'done' 262 fm.trigger('pastedone'); 263 // force update content 264 data.sync && fm.sync(); 265 }) 266 .always(function() { 267 var hashes = [], addTexts, end = 2; 268 if (hasNtf()) { 269 fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg}); 270 } else { 271 prgSt+= prg; 272 } 273 if (--cnt < 1) { 274 tm && clearTimeout(tm); 275 hasNtf() && fm.notify({type : 'trash', cnt : -1}); 276 fm.unlockfiles({files : targets}); 277 if (Object.keys(res).length) { 278 if (err.length > 1) { 279 if (res.removed || res.removed.length) { 280 hashes = jQuery.grep(targets, function(h) { 281 return jQuery.inArray(h, res.removed) === -1? true : false; 282 }); 283 } 284 if (hashes.length) { 285 if (err.length > end) { 286 end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1; 287 } 288 dfrd.reject(); 289 fm.exec('rm', hashes, { addTexts: err.slice(0, end), forceRm: true }); 290 } else { 291 fm.error(err); 292 } 293 } 294 res._noSound = true; 295 if (res.undo && res.redo) { 296 res.undo = { 297 cmd : 'trash', 298 callback : res.undo, 299 }; 300 res.redo = { 301 cmd : 'trash', 302 callback : res.redo 303 }; 304 } 305 dfrd.resolve(res); 306 } else { 307 dfrd.reject(err); 308 } 309 } 310 }); 311 } 312 }); 313 } else { 314 dfrd.reject('errFolderNotFound'); 315 fm.unlockfiles({files : targets}); 316 } 317 }); 318 } else { 319 dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']); 320 fm.unlockfiles({files : targets}); 321 } 322 }).fail(function() { 323 self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]); 324 }); 325 }, 326 remove = function(dfrd, targets, quiet) { 327 var notify = quiet? {} : {type : 'rm', cnt : targets.length}; 328 fm.request({ 329 data : {cmd : 'rm', targets : targets}, 330 notify : notify, 331 preventFail : true 332 }) 333 .fail(function(error) { 334 dfrd.reject(error); 335 }) 336 .done(function(data) { 337 if (data.error || data.warning) { 338 data.sync = true; 339 } 340 dfrd.resolve(data); 341 }) 342 .always(function() { 343 fm.unlockfiles({files : targets}); 344 }); 345 }, 346 getTHash = function(targets) { 347 var thash = null, 348 root1st; 349 350 if (targets && targets.length) { 351 if (targets.length > 1 && fm.searchStatus.state === 2) { 352 root1st = fm.file(fm.root(targets[0])).volumeid; 353 if (!jQuery.grep(targets, function(h) { return h.indexOf(root1st) !== 0? true : false ; }).length) { 354 thash = fm.option('trashHash', targets[0]); 355 } 356 } else { 357 thash = fm.option('trashHash', targets[0]); 358 } 359 } 360 return thash; 361 },
362 getSize = false; 363 364 // for to be able to overwrite 365 this.confirm = confirm; 366 this.toTrash = toTrash; 367 this.remove = remove; 368 369 this.syncTitleOnChange = true; 370 this.updateOnSelect = false; 371 this.shortcuts = [{ 372 pattern : 'delete ctrl+backspace shift+delete' 373 }]; 374 this.value = 'rm'; 375 376 this.init = function() { 377 var update = function(origin) { 378 var targets; 379 delete self.extra; 380 self.title = fm.i18n('cmd' + self.value); 381 self.className = self.value; 382 self.button && self.button.children('span.elfinder-button-icon')[self.value === 'trash'? 'addClass' : 'removeClass']('elfinder-button-icon-trash'); 383 if (origin && origin !== 'cwd' && (self.state > -1 || origin === 'navbar')) { 384 if (self.value === 'trash') { 385 self.extra = { 386 icon: 'rm', 387 node: jQuery('<span></span>') 388 .attr({title: fm.i18n('cmdrm')}) 389 .on('ready', function(e, data) { 390 targets = data.targets; 391 }) 392 .on('click touchstart', function(e){ 393 if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) { 394 return; 395 } 396 e.stopPropagation(); 397 e.preventDefault(); 398 fm.getUI().trigger('click'); // to close the context menu immediately 399 fm.exec('rm', targets, {_userAction: true, forceRm : true}); 400 }) 401 }; 402 } 403 } 404 }; 405 // re-assign for extended command 406 self = this; 407 fm = this.fm; 408 // bind function of change 409 self.change(function() { 410 update(); 411 }); 412 fm.bind('contextmenucreate', function(e) { 413 update(e.data.type); 414 }); 415 }; 416 417 this.getstate = function(select) { 418 var sel = this.hashes(select), 419 filter = function(files) { 420 var fres = true; 421 return jQuery.grep(files, function(h) { 422 var f; 423 fres = fres && (f = fm.file(h)) && ! f.locked && ! fm.isRoot(f)? true : false; 424 return fres; 425 }); 426 }; 427 428 return sel.length && filter(sel).length == sel.length ? 0 : -1; 429 }; 430 431 this.exec = function(hashes, cOpts) { 432 var opts = cOpts || {}, 433 dfrd = jQuery.Deferred() 434 .always(function() { 435 if (getSize && getSize.state && getSize.state() === 'pending') { 436 getSize.reject(); 437 } 438 }) 439 .fail(function(error) { 440 error && fm.error(error); 441 }).done(function(data) { 442 !opts.quiet && !data._noSound && data.removed && data.removed.length && fm.trigger('playsound', {soundFile : 'rm.wav'}); 443 }), 444 files = self.files(hashes), 445 cnt = files.length, 446 tHash = null, 447 addTexts = opts.addTexts? opts.addTexts : null, 448 forceRm = opts.forceRm, 449 quiet = opts.quiet, 450 targets; 451 452 if (! cnt) { 453 return dfrd.reject(); 454 } 455 456 jQuery.each(files, function(i, file) { 457 if (fm.isRoot(file)) { 458 return !dfrd.reject(['errRm', file.name, 'errPerm']); 459 } 460 if (file.locked) { 461 return !dfrd.reject(['errLocked', file.name]); 462 } 463 }); 464 465 if (dfrd.state() === 'pending') { 466 targets = self.hashes(hashes); 467 cnt = files.length; 468 469 if (forceRm || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) { 470 tHash = ''; 471 self.title = fm.i18n('cmdrm'); 472 } 473 474 if (tHash === null) { 475 tHash = getTHash(targets); 476 } 477 478 fm.lockfiles({files : targets}); 479 480 if (tHash && self.options.quickTrash) { 481 self.toTrash(dfrd, targets, tHash); 482 } else { 483 if (quiet) { 484 remove(dfrd, targets, quiet); 485 } else { 486 self.confirm(dfrd, targets, files, tHash, addTexts); 487 } 488 } 489 } 490 491 return dfrd; 492 }; 493 494 fm.bind('select contextmenucreate closecontextmenu', function(e) { 495 var targets = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(); 496 if (targets && targets.length) { 497 self.update(void(0), (targets? getTHash(targets) : fm.option('trashHash'))? 'trash' : 'rm'); 498 } 499 }); 500 501};
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.