Source: core/js/oc-dialogs.js

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