diff options
Diffstat (limited to 'apps/files/js')
-rw-r--r-- | apps/files/js/app.js | 92 | ||||
-rw-r--r-- | apps/files/js/file-upload.js | 80 | ||||
-rw-r--r-- | apps/files/js/fileactions.js | 2 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 122 | ||||
-rw-r--r-- | apps/files/js/jquery.iframe-transport.js | 205 | ||||
-rw-r--r-- | apps/files/js/mainfileinfodetailview.js | 38 |
6 files changed, 303 insertions, 236 deletions
diff --git a/apps/files/js/app.js b/apps/files/js/app.js index ff505d417f1..7a3d78f9663 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -11,7 +11,7 @@ * */ -/* global dragOptions, folderDropOptions */ +/* global dragOptions, folderDropOptions, OC */ (function() { if (!OCA.Files) { @@ -41,10 +41,22 @@ fileList: null, /** + * Backbone model for storing files preferences + */ + _filesConfig: null, + + /** * Initializes the files app */ initialize: function() { this.navigation = new OCA.Files.Navigation($('#app-navigation')); + this.$showHiddenFiles = $('input#showhiddenfilesToggle'); + var showHidden = $('#showHiddenFiles').val() === "1"; + this.$showHiddenFiles.prop('checked', showHidden); + + this._filesConfig = new OC.Backbone.Model({ + showhidden: showHidden + }); var urlParams = OC.Util.History.parseUrlQuery(); var fileActions = new OCA.Files.FileActions(); @@ -72,7 +84,12 @@ fileActions: fileActions, allowLegacyActions: true, scrollTo: urlParams.scrollto, - filesClient: OC.Files.getClient() + filesClient: OC.Files.getClient(), + sorting: { + mode: $('#defaultFileSorting').val(), + direction: $('#defaultFileSortingDirection').val() + }, + config: this._filesConfig, } ); this.files.initialize(); @@ -86,6 +103,8 @@ this._setupEvents(); // trigger URL change event handlers this._onPopState(urlParams); + + this._debouncedPersistShowHiddenFilesState = _.debounce(this._persistShowHiddenFilesState, 1200); }, /** @@ -140,6 +159,14 @@ }, /** + * + * @returns {Backbone.Model} + */ + getFilesConfig: function() { + return this._filesConfig; + }, + + /** * Setup events based on URL changes */ _setupEvents: function() { @@ -147,9 +174,34 @@ // detect when app changed their current directory $('#app-content').delegate('>div', 'changeDirectory', _.bind(this._onDirectoryChanged, this)); + $('#app-content').delegate('>div', 'afterChangeDirectory', _.bind(this._onAfterDirectoryChanged, this)); $('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this)); $('#app-navigation').on('itemChanged', _.bind(this._onNavigationChanged, this)); + this.$showHiddenFiles.on('change', _.bind(this._onShowHiddenFilesChange, this)); + }, + + /** + * Toggle showing hidden files according to the settings checkbox + * + * @returns {undefined} + */ + _onShowHiddenFilesChange: function() { + var show = this.$showHiddenFiles.is(':checked'); + this._filesConfig.set('showhidden', show); + this._debouncedPersistShowHiddenFilesState(); + }, + + /** + * Persist show hidden preference on ther server + * + * @returns {undefined} + */ + _persistShowHiddenFilesState: function() { + var show = this._filesConfig.get('showhidden'); + $.post(OC.generateUrl('/apps/files/api/v1/showhidden'), { + show: show + }); }, /** @@ -173,7 +225,16 @@ */ _onDirectoryChanged: function(e) { if (e.dir) { - this._changeUrl(this.navigation.getActiveItem(), e.dir); + this._changeUrl(this.navigation.getActiveItem(), e.dir, e.fileId); + } + }, + + /** + * Event handler for when an app notified that its directory changed + */ + _onAfterDirectoryChanged: function(e) { + if (e.dir && e.fileId) { + this._changeUrl(this.navigation.getActiveItem(), e.dir, e.fileId); } }, @@ -210,14 +271,35 @@ }, /** + * Encode URL params into a string, except for the "dir" attribute + * that gets encoded as path where "/" is not encoded + * + * @param {Object.<string>} params + * @return {string} encoded params + */ + _makeUrlParams: function(params) { + var dir = params.dir; + delete params.dir; + return 'dir=' + OC.encodePath(dir) + '&' + OC.buildQueryString(params); + }, + + /** * Change the URL to point to the given dir and view */ - _changeUrl: function(view, dir) { + _changeUrl: function(view, dir, fileId) { var params = {dir: dir}; if (view !== 'files') { params.view = view; + } else if (fileId) { + params.fileid = fileId; + } + var currentParams = OC.Util.History.parseUrlQuery(); + if (currentParams.dir === params.dir && currentParams.view === params.view && currentParams.fileid !== params.fileid) { + // if only fileid changed or was added, replace instead of push + OC.Util.History.replaceState(this._makeUrlParams(params)); + } else { + OC.Util.History.pushState(this._makeUrlParams(params)); } - OC.Util.History.pushState(params); } }; })(); diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index bd80afd072c..3257ded7b89 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -274,6 +274,7 @@ OC.Upload = { if ( $('#file_upload_start').exists() ) { var file_upload_param = { dropZone: $('#content'), // restrict dropZone to content div + pasteZone: null, autoUpload: false, sequentialUploads: true, //singleFileUploads is on by default, so the data.files array will always have length 1 @@ -503,7 +504,7 @@ OC.Upload = { //fetch response from iframe response = data.result[0].body.innerText; } - var result = $.parseJSON(response); + var result = JSON.parse(response); delete data.jqXHR; @@ -556,7 +557,16 @@ OC.Upload = { window.file_upload_param = fileupload; if (supportAjaxUploadWithProgress()) { - + //remaining time + var lastUpdate = new Date().getMilliseconds(); + var lastSize = 0; + var bufferSize = 20; + var buffer = []; + var bufferIndex = 0; + var bufferTotal = 0; + for(var i = 0; i < bufferSize;i++){ + buffer[i] = 0; + } // add progress handlers fileupload.on('fileuploadadd', function(e, data) { OC.Upload.log('progress handle fileuploadadd', e, data); @@ -569,7 +579,15 @@ OC.Upload = { fileupload.on('fileuploadstart', function(e, data) { OC.Upload.log('progress handle fileuploadstart', e, data); $('#uploadprogresswrapper .stop').show(); + $('#uploadprogresswrapper .label').show(); $('#uploadprogressbar').progressbar({value: 0}); + $('#uploadprogressbar .ui-progressbar-value'). + html('<em class="label inner"><span class="desktop">' + + t('files', 'Uploading...') + + '</span><span class="mobile">' + + t('files', '...') + + '</span></em>'); + $('#uploadprogressbar').tipsy({gravity:'n', fade:true, live:true}); OC.Upload._showProgressBar(); }); fileupload.on('fileuploadprogress', function(e, data) { @@ -579,11 +597,67 @@ OC.Upload = { fileupload.on('fileuploadprogressall', function(e, data) { OC.Upload.log('progress handle fileuploadprogressall', e, data); var progress = (data.loaded / data.total) * 100; + var thisUpdate = new Date().getMilliseconds(); + var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s + lastUpdate = thisUpdate; + var diffSize = data.loaded - lastSize; + lastSize = data.loaded; + diffSize = diffSize / diffUpdate; // apply timing factor, eg. 1mb/2s = 0.5mb/s + var remainingSeconds = ((data.total - data.loaded) / diffSize); + if(remainingSeconds >= 0) { + bufferTotal = bufferTotal - (buffer[bufferIndex]) + remainingSeconds; + buffer[bufferIndex] = remainingSeconds; //buffer to make it smoother + bufferIndex = (bufferIndex + 1) % bufferSize; + } + var smoothRemainingSeconds = (bufferTotal / bufferSize); //seconds + var date = new Date(smoothRemainingSeconds * 1000); + var timeStringDesktop = ""; + var timeStringMobile = ""; + if(date.getUTCHours() > 0){ + timeStringDesktop = t('files', '{hours}:{minutes}:{seconds} hour{plural_s} left' , { + hours:date.getUTCHours(), + minutes: ('0' + date.getUTCMinutes()).slice(-2), + seconds: ('0' + date.getUTCSeconds()).slice(-2), + plural_s: ( smoothRemainingSeconds === 3600 ? "": "s") // 1 hour = 1*60m*60s = 3600s + }); + timeStringMobile = t('files', '{hours}:{minutes}h' , { + hours:date.getUTCHours(), + minutes: ('0' + date.getUTCMinutes()).slice(-2), + seconds: ('0' + date.getUTCSeconds()).slice(-2) + }); + } else if(date.getUTCMinutes() > 0){ + timeStringDesktop = t('files', '{minutes}:{seconds} minute{plural_s} left' , { + minutes: date.getUTCMinutes(), + seconds: ('0' + date.getUTCSeconds()).slice(-2), + plural_s: (smoothRemainingSeconds === 60 ? "": "s") // 1 minute = 1*60s = 60s + }); + timeStringMobile = t('files', '{minutes}:{seconds}m' , { + minutes: date.getUTCMinutes(), + seconds: ('0' + date.getUTCSeconds()).slice(-2) + }); + } else if(date.getUTCSeconds() > 0){ + timeStringDesktop = t('files', '{seconds} second{plural_s} left' , { + seconds: date.getUTCSeconds(), + plural_s: (smoothRemainingSeconds === 1 ? "": "s") // 1 second = 1s = 1s + }); + timeStringMobile = t('files', '{seconds}s' , {seconds: date.getUTCSeconds()}); + } else { + timeStringDesktop = t('files', 'Any moment now...'); + timeStringMobile = t('files', 'Soon...'); + } + $('#uploadprogressbar .label .mobile').text(timeStringMobile); + $('#uploadprogressbar .label .desktop').text(timeStringDesktop); + $('#uploadprogressbar').attr('original-title', + t('files', '{loadedSize} of {totalSize} ({bitrate})' , { + loadedSize: humanFileSize(data.loaded), + totalSize: humanFileSize(data.total), + bitrate: humanFileSize(data.bitrate) + '/s' + }) + ); $('#uploadprogressbar').progressbar('value', progress); }); fileupload.on('fileuploadstop', function(e, data) { OC.Upload.log('progress handle fileuploadstop', e, data); - OC.Upload._hideProgressBar(); }); fileupload.on('fileuploadfail', function(e, data) { diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 69e32d500c4..c3d4fba9ef5 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -619,7 +619,7 @@ this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { var dir = context.$file.attr('data-path') || context.fileList.getCurrentDirectory(); - context.fileList.changeDirectory(OC.joinPaths(dir, filename)); + context.fileList.changeDirectory(OC.joinPaths(dir, filename), true, false, parseInt(context.$file.attr('data-id'), 10)); }); this.registerAction({ diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 6229d3b2316..ef29a4844bf 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -145,6 +145,11 @@ _filter: '', /** + * @type Backbone.Model + */ + _filesConfig: undefined, + + /** * Sort attribute * @type String */ @@ -198,6 +203,18 @@ return; } + if (options.config) { + this._filesConfig = options.config; + } else if (!_.isUndefined(OCA.Files) && !_.isUndefined(OCA.Files.App)) { + this._filesConfig = OCA.Files.App.getFilesConfig(); + } + + if (!_.isUndefined(this._filesConfig)) { + this._filesConfig.on('change:showhidden', function() { + self.setFiles(self.files); + }); + } + if (options.dragOptions) { this._dragOptions = options.dragOptions; } @@ -239,7 +256,11 @@ this.fileSummary = this._createSummary(); - this.setSort('name', 'asc'); + if (options.sorting) { + this.setSort(options.sorting.mode, options.sorting.direction, false, false); + } else { + this.setSort('name', 'asc', false, false); + } var breadcrumbOptions = { onClick: _.bind(this._onClickBreadCrumb, this), @@ -277,7 +298,8 @@ this.updateSearch(); - this.$fileList.on('click','td.filename>a.name', _.bind(this._onClickFile, this)); + this.$fileList.on('click','td.filename>a.name, td.filesize, td.date', _.bind(this._onClickFile, this)); + this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this)); this.$el.on('urlChanged', _.bind(this._onUrlChanged, this)); this.$el.find('.select-all').click(_.bind(this._onClickSelectAll, this)); @@ -395,6 +417,11 @@ model.toJSON(), {updateSummary: true, silent: false, animate: true} ); + + // restore selection state + var selected = !!self._selectedFiles[$tr.data('id')]; + self._selectFileEl($tr, selected); + $tr.toggleClass('highlighted', highlightState); }); model.on('busy', function(model, state) { @@ -695,14 +722,14 @@ sort = $target.attr('data-sort'); if (sort) { if (this._sort === sort) { - this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc', true); + this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc', true, true); } else { if ( sort === 'name' ) { //default sorting of name is opposite to size and mtime - this.setSort(sort, 'asc', true); + this.setSort(sort, 'asc', true, true); } else { - this.setSort(sort, 'desc', true); + this.setSort(sort, 'desc', true, true); } } } @@ -850,6 +877,10 @@ * @return array of DOM elements of the newly added files */ _nextPage: function(animate) { + // Save full files list while rendering + var allFiles = this.files; + this.files = this._filterHiddenFiles(this.files); + var index = this.$fileList.children().length, count = this.pageSize(), hidden, @@ -896,6 +927,10 @@ } }, 0); } + + // Restore full files list after rendering + this.files = allFiles; + return newTrs; }, @@ -933,18 +968,25 @@ // clear "Select all" checkbox this.$el.find('.select-all').prop('checked', false); + // Save full files list while rendering + var allFiles = this.files; + this.files = this._filterHiddenFiles(this.files); + this.isEmpty = this.files.length === 0; this._nextPage(); this.updateEmptyContent(); - this.fileSummary.calculate(filesArray); + this.fileSummary.calculate(this.files); this._selectedFiles = {}; this._selectionSummary.clear(); this.updateSelectionSummary(); $(window).scrollTop(0); + // Restore full files list after rendering + this.files = allFiles; + this.$fileList.trigger(jQuery.Event('updated')); _.defer(function() { self.$el.closest('#app-content').trigger(jQuery.Event('apprendered')); @@ -952,6 +994,21 @@ }, /** + * Filter hidden files of the given filesArray (dot-files) + * + * @param filesArray files to be filtered + * @returns {array} + */ + _filterHiddenFiles: function(files) { + if (_.isUndefined(this._filesConfig) || this._filesConfig.get('showhidden')) { + return files; + } + return _.filter(files, function(file) { + return file.name.indexOf('.') !== 0; + }); + }, + + /** * Returns the icon URL matching the given file info * * @param {OC.Files.FileInfo} fileInfo file info @@ -1318,19 +1375,20 @@ return parseInt(this.$el.find('#permissions').val(), 10); }, /** - * @brief Changes the current directory and reload the file list. - * @param targetDir target directory (non URL encoded) - * @param changeUrl false if the URL must not be changed (defaults to true) - * @param {boolean} force set to true to force changing directory + * Changes the current directory and reload the file list. + * @param {string} targetDir target directory (non URL encoded) + * @param {boolean} [changeUrl=true] if the URL must not be changed (defaults to true) + * @param {boolean} [force=false] set to true to force changing directory + * @param {string} [fileId] optional file id, if known, to be appended in the URL */ - changeDirectory: function(targetDir, changeUrl, force) { + changeDirectory: function(targetDir, changeUrl, force, fileId) { var self = this; var currentDir = this.getCurrentDirectory(); targetDir = targetDir || '/'; if (!force && currentDir === targetDir) { return; } - this._setCurrentDir(targetDir, changeUrl); + this._setCurrentDir(targetDir, changeUrl, fileId); this.reload().then(function(success){ if (!success) { self.changeDirectory(currentDir, true); @@ -1345,8 +1403,9 @@ * Sets the current directory name and updates the breadcrumb. * @param targetDir directory to display * @param changeUrl true to also update the URL, false otherwise (default) + * @param {string} [fileId] file id */ - _setCurrentDir: function(targetDir, changeUrl) { + _setCurrentDir: function(targetDir, changeUrl, fileId) { targetDir = targetDir.replace(/\\/g, '/'); var previousDir = this.getCurrentDirectory(), baseDir = OC.basename(targetDir); @@ -1364,10 +1423,14 @@ this.$el.find('#dir').val(targetDir); if (changeUrl !== false) { - this.$el.trigger(jQuery.Event('changeDirectory', { + var params = { dir: targetDir, previousDir: previousDir - })); + }; + if (fileId) { + params.fileId = fileId; + } + this.$el.trigger(jQuery.Event('changeDirectory', params)); } this.breadcrumb.setDirectory(this.getCurrentDirectory()); }, @@ -1377,8 +1440,9 @@ * @param sort sort attribute name * @param direction sort direction, one of "asc" or "desc" * @param update true to update the list, false otherwise (default) + * @param persist true to save changes in the database (default) */ - setSort: function(sort, direction, update) { + setSort: function(sort, direction, update, persist) { var comparator = FileList.Comparators[sort] || FileList.Comparators.name; this._sort = sort; this._sortDirection = (direction === 'desc')?'desc':'asc'; @@ -1409,6 +1473,13 @@ this.reload(); } } + + if (persist) { + $.post(OC.generateUrl('/apps/files/api/v1/sorting'), { + mode: sort, + direction: direction + }); + } }, /** @@ -1505,6 +1576,18 @@ result.sort(this._sortComparator); this.setFiles(result); + + if (this.dirInfo) { + var newFileId = this.dirInfo.id; + // update fileid in URL + var params = { + dir: this.getCurrentDirectory() + }; + if (newFileId) { + params.fileId = newFileId; + } + this.$el.trigger(jQuery.Event('afterChangeDirectory', params)); + } return true; }, @@ -2292,12 +2375,14 @@ this.$el.find('#filestable thead th').addClass('hidden'); this.$el.find('#emptycontent').addClass('hidden'); $('#searchresults').addClass('filter-empty'); + $('#searchresults .emptycontent').addClass('emptycontent-search'); if ( $('#searchresults').length === 0 || $('#searchresults').hasClass('hidden') ) { this.$el.find('.nofilterresults').removeClass('hidden'). find('p').text(t('files', "No entries in this folder match '{filter}'", {filter:this._filter}, null, {'escape': false})); } } else { $('#searchresults').removeClass('filter-empty'); + $('#searchresults .emptycontent').removeClass('emptycontent-search'); this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty); if (!this.$el.find('.mask').exists()) { this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty); @@ -2500,7 +2585,6 @@ } }); fileUploadStart.on('fileuploadadd', function(e, data) { - console.log('XXXXXXX'); OC.Upload.log('filelist handle fileuploadadd', e, data); //finish delete if we are uploading a deleted file @@ -2543,7 +2627,7 @@ // fetch response from iframe response = data.result[0].body.innerText; } - var result=$.parseJSON(response); + var result = JSON.parse(response); if (typeof result[0] !== 'undefined' && result[0].status === 'success') { var file = result[0]; @@ -2856,7 +2940,7 @@ $(document).ready(function() { OCA.Files.FileList.lastAction(); } }); - $(window).unload(function () { + $(window).on('unload', function () { $(window).trigger('beforeunload'); }); diff --git a/apps/files/js/jquery.iframe-transport.js b/apps/files/js/jquery.iframe-transport.js deleted file mode 100644 index 5c9df77976b..00000000000 --- a/apps/files/js/jquery.iframe-transport.js +++ /dev/null @@ -1,205 +0,0 @@ -/* - * jQuery Iframe Transport Plugin 1.7 - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/*jslint unparam: true, nomen: true */ -/*global define, window, document */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define(['jquery'], factory); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - - // Helper variable to create unique names for the transport iframes: - var counter = 0; - - // The iframe transport accepts three additional options: - // options.fileInput: a jQuery collection of file input fields - // options.paramName: the parameter name for the file form data, - // overrides the name property of the file input field(s), - // can be a string or an array of strings. - // options.formData: an array of objects with name and value properties, - // equivalent to the return data of .serializeArray(), e.g.: - // [{name: 'a', value: 1}, {name: 'b', value: 2}] - $.ajaxTransport('iframe', function (options) { - if (options.async) { - var form, - iframe, - addParamChar; - return { - send: function (_, completeCallback) { - form = $('<form style="display:none;"></form>'); - form.attr('accept-charset', options.formAcceptCharset); - addParamChar = /\?/.test(options.url) ? '&' : '?'; - // XDomainRequest only supports GET and POST: - if (options.type === 'DELETE') { - options.url = options.url + addParamChar + '_method=DELETE'; - options.type = 'POST'; - } else if (options.type === 'PUT') { - options.url = options.url + addParamChar + '_method=PUT'; - options.type = 'POST'; - } else if (options.type === 'PATCH') { - options.url = options.url + addParamChar + '_method=PATCH'; - options.type = 'POST'; - } - // javascript:false as initial iframe src - // prevents warning popups on HTTPS in IE6. - // IE versions below IE8 cannot set the name property of - // elements that have already been added to the DOM, - // so we set the name along with the iframe HTML markup: - counter += 1; - iframe = $( - '<iframe src="javascript:false;" name="iframe-transport-' + - counter + '"></iframe>' - ).bind('load', function () { - var fileInputClones, - paramNames = $.isArray(options.paramName) ? - options.paramName : [options.paramName]; - iframe - .unbind('load') - .bind('load', function () { - var response; - // Wrap in a try/catch block to catch exceptions thrown - // when trying to access cross-domain iframe contents: - try { - response = iframe.contents(); - // Google Chrome and Firefox do not throw an - // exception when calling iframe.contents() on - // cross-domain requests, so we unify the response: - if (!response.length || !response[0].firstChild) { - throw new Error(); - } - } catch (e) { - response = undefined; - } - // The complete callback returns the - // iframe content document as response object: - completeCallback( - 200, - 'success', - {'iframe': response} - ); - // Fix for IE endless progress bar activity bug - // (happens on form submits to iframe targets): - $('<iframe src="javascript:false;"></iframe>') - .appendTo(form); - window.setTimeout(function () { - // Removing the form in a setTimeout call - // allows Chrome's developer tools to display - // the response result - form.remove(); - }, 0); - }); - form - .prop('target', iframe.prop('name')) - .prop('action', options.url) - .prop('method', options.type); - if (options.formData) { - $.each(options.formData, function (index, field) { - $('<input type="hidden"/>') - .prop('name', field.name) - .val(field.value) - .appendTo(form); - }); - } - if (options.fileInput && options.fileInput.length && - options.type === 'POST') { - fileInputClones = options.fileInput.clone(); - // Insert a clone for each file input field: - options.fileInput.after(function (index) { - return fileInputClones[index]; - }); - if (options.paramName) { - options.fileInput.each(function (index) { - $(this).prop( - 'name', - paramNames[index] || options.paramName - ); - }); - } - // Appending the file input fields to the hidden form - // removes them from their original location: - form - .append(options.fileInput) - .prop('enctype', 'multipart/form-data') - // enctype must be set as encoding for IE: - .prop('encoding', 'multipart/form-data'); - } - form.submit(); - // Insert the file input fields at their original location - // by replacing the clones with the originals: - if (fileInputClones && fileInputClones.length) { - options.fileInput.each(function (index, input) { - var clone = $(fileInputClones[index]); - $(input).prop('name', clone.prop('name')); - clone.replaceWith(input); - }); - } - }); - form.append(iframe).appendTo(document.body); - }, - abort: function () { - if (iframe) { - // javascript:false as iframe src aborts the request - // and prevents warning popups on HTTPS in IE6. - // concat is used to avoid the "Script URL" JSLint error: - iframe - .unbind('load') - .prop('src', 'javascript'.concat(':false;')); - } - if (form) { - form.remove(); - } - } - }; - } - }); - - // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, xml - // and script. - // Please note that the Content-Type for JSON responses has to be text/plain - // or text/html, if the browser doesn't include application/json in the - // Accept header, else IE will show a download dialog. - // The Content-Type for XML responses on the other hand has to be always - // application/xml or text/xml, so IE properly parses the XML response. - // See also - // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation - $.ajaxSetup({ - converters: { - 'iframe text': function (iframe) { - return iframe && $(iframe[0].body).text(); - }, - 'iframe json': function (iframe) { - return iframe && $.parseJSON($(iframe[0].body).text()); - }, - 'iframe html': function (iframe) { - return iframe && $(iframe[0].body).html(); - }, - 'iframe xml': function (iframe) { - var xmlDoc = iframe && iframe[0]; - return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : - $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || - $(xmlDoc.body).html()); - }, - 'iframe script': function (iframe) { - return iframe && $.globalEval($(iframe[0].body).text()); - } - } - }); - -}));
\ No newline at end of file diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 1bcb4873c53..c586135b9c7 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -12,7 +12,13 @@ var TEMPLATE = '<div class="thumbnailContainer"><a href="#" class="thumbnail action-default"><div class="stretcher"/></a></div>' + '<div class="file-details-container">' + - '<div class="fileName"><h3 title="{{name}}" class="ellipsis">{{name}}</h3></div>' + + '<div class="fileName">' + + '<h3 title="{{name}}" class="ellipsis">{{name}}</h3>' + + '<a class="permalink" href="{{permalink}}" title="{{permalinkTitle}}">' + + '<span class="icon icon-public"></span>' + + '<span class="hidden-visually">{{permalinkTitle}}</span>' + + '</a>' + + '</div>' + ' <div class="file-details ellipsis">' + ' <a href="#" ' + ' class="action action-favorite favorite">' + @@ -20,6 +26,9 @@ ' </a>' + ' {{#if hasSize}}<span class="size" title="{{altSize}}">{{size}}</span>, {{/if}}<span class="date" title="{{altDate}}">{{date}}</span>' + ' </div>' + + '</div>' + + '<div class="hidden permalink-field">' + + '<input type="text" value="{{permalink}}" placeholder="{{permalinkTitle}}" readonly="readonly"/>' + '</div>'; /** @@ -50,7 +59,9 @@ events: { 'click a.action-favorite': '_onClickFavorite', - 'click a.action-default': '_onClickDefaultAction' + 'click a.action-default': '_onClickDefaultAction', + 'click a.permalink': '_onClickPermalink', + 'focus .permalink-field>input': '_onFocusPermalink' }, template: function(data) { @@ -72,6 +83,20 @@ } }, + _onClickPermalink: function() { + var $row = this.$('.permalink-field'); + $row.toggleClass('hidden'); + if (!$row.hasClass('hidden')) { + $row.find('>input').focus(); + } + // cancel click, user must right-click + copy or middle click + return false; + }, + + _onFocusPermalink: function() { + this.$('.permalink-field>input').select(); + }, + _onClickFavorite: function(event) { event.preventDefault(); this._fileActions.triggerAction('Favorite', this.model, this._fileList); @@ -87,6 +112,11 @@ this.render(); }, + _makePermalink: function(fileId) { + var baseUrl = OC.getProtocol() + '://' + OC.getHost(); + return baseUrl + OC.generateUrl('/f/{fileId}', {fileId: fileId}); + }, + setFileInfo: function(fileInfo) { if (this.model) { this.model.off('change', this._onModelChanged, this); @@ -118,7 +148,9 @@ altDate: OC.Util.formatDate(this.model.get('mtime')), date: OC.Util.relativeModifiedDate(this.model.get('mtime')), starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'), - starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star') + starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star'), + permalink: this._makePermalink(this.model.get('id')), + permalinkTitle: t('files', 'Local link') })); // TODO: we really need OC.Previews |