X-Git-Url: http://mmka.chem.univ.gda.pl/gitweb/?a=blobdiff_plain;f=filex%2Fstatic%2Ffilex%2Ffilex.js;h=9f34b5ede6deeaab84120142fbd4005efcbea1d5;hb=bc6c1b10265d2c52e9f592cafacf0ae54fa033b0;hp=e1dc50f3968374e119022e7a350027e31d6a78de;hpb=6b7a14ba918e8b4607c5652920b8ae7dab79e42d;p=qcg-portal.git diff --git a/filex/static/filex/filex.js b/filex/static/filex/filex.js index e1dc50f..9f34b5e 100644 --- a/filex/static/filex/filex.js +++ b/filex/static/filex/filex.js @@ -15,6 +15,10 @@ $(function(){ }); Filex.File = OfflineModel.extend({ + defaults: { + checked: false + }, + isDir: function() { return false; }, @@ -25,6 +29,10 @@ $(function(){ isHidden: function() { return this.get('name')[0] == '.'; + }, + + toggle: function() { + this.set('checked', !this.get('checked')); } }); @@ -59,7 +67,7 @@ $(function(){ case 'file': return new Filex.File(attrs, options); default: - console.log('Unknown model type:', attrs['type']); + console.error('Unknown model type:', attrs['type']); } }, @@ -86,26 +94,28 @@ $(function(){ Filex.Path = Backbone.Collection.extend({ model: Filex.PathBit, - initialize: function() { - this.listenTo(this, 'reset', this.setActive); - this.listenTo(this, 'add', this.setActive); - }, - - setActive: function() { - _.each(this.initial(), function(bit) { - bit.set('active', false); - }); - - this.last().set('active', true); + full: function() { + return this.pluck('name').join('/').replace(/^\/+/, '/'); }, - full: function() { - return this.pluck('path').join('/') || '/'; + append: function(name) { + return this.pluck('name').concat(name).join('/').replace(/^\/+/, '/'); } }); // ------------------------------------------------------------------------ + // Routers + // ------------------------------------------------------------------------ + + Filex.Router = Backbone.Router.extend({ + routes: { + '*url': 'load' + } + }); + + + // ------------------------------------------------------------------------ // Views // ------------------------------------------------------------------------ @@ -113,12 +123,13 @@ $(function(){ tagName: 'tr', events: { - 'click .link': 'selected' + 'click': 'click' }, initialize: function(options) { this.view = options.view; + this.listenTo(this.model, 'change:checked', this.toggleChecked); this.listenTo(this.model, 'remove', this.remove); this.listenTo(this.model, 'hidden', this.toggleHidden); }, @@ -131,11 +142,17 @@ $(function(){ render: function() { var data = this.model.toJSON(); - data['url_params'] = $.param({ - host: this.view.host, - path: this.view.path.full(), - name: this.model.get('name') - }); + + if (this.model.isDir()) { + data['url'] = this.view.host + '/' + this.view.path.append(this.model.get('name')) + } + else { + data['params'] = $.param({ + host: this.view.host, + path: this.view.path.append(this.model.get('name')) + }); + } + data['cid'] = this.model.cid; this.$el.html(this.template()(data)); this.toggleHidden(); @@ -144,22 +161,27 @@ $(function(){ }, toggleHidden: function() { - this.$el.toggleClass('hidden', this.model.isHidden() && !this.view.showHidden()); + var isHidden = this.model.isHidden() && !this.view.showHidden(); + this.$el.toggleClass('hidden', isHidden); + + if (isHidden && this.model.get('checked')) + this.model.toggle(); + }, + + toggleChecked: function(obj, value) { + this.$el.toggleClass('active', value); + this.$el.find('input[type="checkbox"]').prop('checked', value); }, - selected: function(e) { - e.preventDefault(); - this.model.trigger(this.model.isDir() ? 'selected:dir' : 'selected:file', this.model); + click: function(e) { + if (e.target.className != 'link') + this.model.toggle(); } }); Filex.Breadcrumb = Backbone.View.extend({ tagName: 'li', - events: { - 'click a': 'selected' - }, - initialize: function() { this.listenTo(this.model, 'change:active', this.render); this.listenTo(this.model, 'remove', this.remove); @@ -167,22 +189,17 @@ $(function(){ render: function() { if (this.model.get('active')) { - this.$el.text(this.model.get('text')); + this.$el.addClass('active'); + this.$el.text(this.model.get('name')); } else { this.$el.html($('', { - href: '#', - text: this.model.get('text') + href: '#' + this.model.get('url'), + text: this.model.get('name') })); } - this.$el.toggleClass('active', this.model.get('active')); return this; - }, - - selected: function(e) { - e.preventDefault(); - this.model.trigger('selected', this.model); } }); @@ -191,86 +208,157 @@ $(function(){ events: { 'change #show-hidden': 'toggleHidden', - 'click #host-controls .view': 'hostEdit' + 'click #select-all': 'selectAll', + 'click #btn-refresh': 'reloadFiles', + 'click #btn-favorites': 'toggleFavorites', + 'click #btn-host': 'editHost' }, initialize: function(options) { this.$noItems = $('#no-items'); this.$error = $('#error'); this.$showHidden = $('#show-hidden'); - this.$host = $('#host-controls'); + this.$selectAll = $('#select-all'); + this.$favorites = $('#btn-favorites'); + this.$host = $('#btn-host'); - this.host = options.host; this.path = new Filex.Path(); this.files = new Filex.FileList(); + this.router = new Filex.Router(); - this.listenTo(this.path, 'reset', this.resetPath); - this.listenTo(this.path, 'add', this.reloadFiles); this.listenTo(this.path, 'add', this.addPath); - this.listenTo(this.path, 'selected', this.selectedPath); + this.listenTo(this.path, 'reset', this.resetPath); this.listenTo(this.files, 'reset', this.resetFiles); - this.listenTo(this.files, 'selected:dir', this.selectedDir); - this.listenTo(this.files, 'selected:file', this.selectedFile); + this.listenTo(this.files, 'change:checked', this.updateSelectAll); + this.listenTo(this.router, 'route:load', this.load); // used in selectize callbacks var view = this, - optionTemplate = _.template('
<%= host %>
<%= path %>
'); + optionTemplate = _.template('
<%- host %>
<%- path %>
'); this.$('#host-selector').selectize({ - valueField: 'host', + optgroupField: 'group', labelField: 'host', searchField: ['host', 'path'], sortField: [ {field: 'host', direction: 'asc'}, {field: 'path', direction: 'asc'} ], - items: [this.host], - options: options.hostOptions, + options: options.locations, + optgroups: [ + {value: 'sys', label: 'Podstawowe'}, + {value: 'usr', label: 'Użytkownika'} + ], + lockOptgroupOrder: true, + create: function(input, callback) { + var $form = $('#favorite-form'), + parts = input.split('/'), + callback_called = false; + + $form[0].reset(); + $form.find('.alert-danger').remove(); + $form.find('#id_host').val(parts.shift()); + + if (parts.length) + $form.find('#id_path').val(parts.join('/')); + + $form.off(); + $form.on('submit', function(e) { + var $this = $(this), + $btn = $this.find('[type="submit"]'); + + e.preventDefault(); + $btn.button('loading'); + $this.find('.alert-danger').remove(); + + $.post($this.attr('action'), $this.serialize(), function(data) { + callback(data); + callback_called = true; + + $this.modal('hide'); + $btn.button('reset'); + }, 'json').fail(function(xhr) { + console.error(arguments); + var error = (xhr.responseJSON || {}).error || undefined; + + if (error.__all__) { + error = 'Podana lokalizacja jest już zapisana' + } + else if (xhr.status == 400) { + error = 'Niepoprawna nazwa hosta lub ścieżka' + } + + if (typeof error === 'string') { + $('
', { + 'class': 'alert alert-danger', + html: ' ' + error + }).prependTo($this.find('.modal-body')); + $btn.button('reset'); + } + else { + $btn.button('error'); + } + }); + }); + + $form.one('hide.bs.modal', function() { + if (!callback_called) + callback(); + $form.off(); + }); + + $form.modal(); + }, render: { option: function(item) { return optionTemplate(item); + }, + option_create : function(data, escape) { + return '
Dodaj ' + escape(data.input) + '
'; } }, - onDropdownClose: function() { + onItemAdd: function(value) { + view.router.navigate('#' + value, {trigger: true}); this.blur(); }, onBlur: function() { - view.$host.toggleClass('editing'); - - var value = this.getValue(); - if (!value) { - this.addItem(view.host, true); - return; - } - - if (value != view.host) { - var location = this.options[value]; - - view.host = location.host; - view.navigate(location.path); - } + $('#host').removeClass('edit'); + this.clear(); } }); this.hostSelectize = this.$('#host-selector')[0].selectize; this.render(); - this.navigate(this.hostSelectize.options[this.host].path); + Backbone.history.start(); }, render: function() { - this.$host.find('.view').text(this.host); - this.$noItems.toggle((this.showHidden() ? !Boolean(this.files.length) : !Boolean(this.files.visible().length))); + this.updateSelectAll(); + this.updateFavorites(); + this.$noItems.toggle(!Boolean(this.visibleFiles().length)); this.$error.hide(); }, - navigate: function(path) { - var pathBits = [new Filex.PathBit({'text': '/', 'path': ''})]; + load: function(location) { + if (!location) + return; + + var hostRootPath = location.split(/\/(\/|~)(.*)/), self = this; + + this.host = hostRootPath[0]; + this.$host.text(this.host); + + this.path.reset([new Filex.PathBit({'name': hostRootPath[1], url: this.host + '/' + hostRootPath[1]})]); + _.each(hostRootPath[2].split('/'), function(item) { + if (item) + self.path.add(new Filex.PathBit({name: item, url: self.host + '/' + self.path.append(item)})); + }); + this.path.last().set('active', true); - pathBits = pathBits.concat(_.map(path.replace(/(^\/+|\/+$)/g, '').split('/'), function(name) { - return new Filex.PathBit({'text': name, 'path': name}); - })); + this.reloadFiles(); + this.updateFavorites(); - this.path.reset(pathBits); + localStorage.last_location = location; }, reloadFiles: function() { @@ -288,7 +376,7 @@ $(function(){ error: function(collection, response) { view.files.reset(); - var msg = (response.responseJSON || {}).msg || 'Błąd serwera'; + var msg = (response.responseJSON || {}).error || 'Błąd serwera'; view.$noItems.hide(); view.$error.find('.msg').text(msg); @@ -304,8 +392,6 @@ $(function(){ }, resetPath: function(models, options) { - this.reloadFiles(); - _.each(options.previousModels, function(model) { model.trigger('remove'); }); @@ -329,35 +415,113 @@ $(function(){ this.render(); }, - selectedDir: function(dir) { - this.path.add({'text': dir.get('name'), 'path': dir.get('name')}); + showHidden: function() { + return this.$showHidden[0].checked; }, - selectedFile: function(file) { - this.trigger('selected:file', this.host + this.path.full() + '/' + file.get('name')); + busy: function() { + this.$el.addClass('busy'); }, - selectedPath: function(bit) { - var newPath = this.path.slice(0, this.path.indexOf(bit) + 1); - this.path.set(newPath); - this.reloadFiles(); + idle: function() { + this.$el.removeClass('busy'); }, - showHidden: function() { - return this.$showHidden[0].checked; + visibleFiles: function() { + return this.showHidden() ? this.files.models : this.files.visible(); }, - hostEdit: function() { - this.$host.toggleClass('editing'); - this.hostSelectize.focus(); + selectedFiles: function() { + return _.filter(this.visibleFiles(), function(item) { + return item.get('checked'); + }); }, - busy: function() { - this.$el.addClass('busy'); + selectAll: function() { + var checked = this.$selectAll[0].checked; + + _.each(this.visibleFiles(), function(item) { + item.set('checked', checked); + }) }, - idle: function() { - this.$el.removeClass('busy'); + updateSelectAll: function() { + if (this.visibleFiles().length) { + this.$selectAll.prop('disabled', false); + this.$selectAll.prop('checked', this.selectedFiles().length == this.visibleFiles().length); + } + else { + this.$selectAll.prop('disabled', true); + this.$selectAll.prop('checked', false); + } + }, + + clearSelection: function() { + _.each(this.visibleFiles(), function(item) { + item.set('checked', false); + }); + }, + + toggleFavorites: function() { + var $btn = this.$favorites, + locations = this.hostSelectize, + is_active = $btn.hasClass('active'), + url = is_active ? '/filex/fav/delete/' : '/filex/fav/add/', + data = { + host: this.host, + path: this.path.full() + }; + + $btn.button('loading'); + + $.post(url, data, 'json').done(function (response) { + $btn.button('reset'); + + if (is_active) + locations.removeOption(data.host + '/' + data.path); + else + locations.addOption(response); + }).fail(function() { + $btn.button('reset'); + $btn.button('toggle'); + + console.error(arguments); + }); + }, + + initialLoad: function() { + var opts = this.hostSelectize.options, + location = localStorage.last_location || opts[Object.keys(opts)[0]].value; + + this.router.navigate('#' + location, {trigger: true, replace: true}); + }, + + updateFavorites: function() { + var loc = this.host + '/' + this.path.full(), + favorites = this.hostSelectize.options; + + if (favorites.hasOwnProperty(loc)) { + if (favorites[loc].group == 'sys') { + this.$favorites.addClass('disabled').prop('disabled', true); + if (this.$favorites.hasClass('active')) + this.$favorites.button('toggle'); + } + else { + this.$favorites.removeClass('disabled').prop('disabled', false); + if (!this.$favorites.hasClass('active')) + this.$favorites.button('toggle'); + } + } + else { + this.$favorites.removeClass('disabled').prop('disabled', false); + if (this.$favorites.hasClass('active')) + this.$favorites.button('toggle'); + } + }, + + editHost: function() { + $('#host').addClass('edit'); + this.hostSelectize.focus(); } }); });