oc-dialogs.js 28 KB

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