1 var Filex = Filex || {};
6 // ------------------------------------------------------------------------
8 // ------------------------------------------------------------------------
10 var OfflineModel = Backbone.Model.extend({
11 // prevent syncing with server
17 Filex.File = OfflineModel.extend({
30 isHidden: function() {
31 return this.get('name')[0] == '.';
35 this.set('checked', !this.get('checked'));
39 Filex.Directory = Filex.File.extend({
49 Filex.PathBit = OfflineModel.extend({
56 // ------------------------------------------------------------------------
58 // ------------------------------------------------------------------------
60 Filex.FileList = Backbone.Collection.extend({
63 model: function(attrs, options) {
64 switch (attrs['type']) {
66 return new Filex.Directory(attrs, options);
68 return new Filex.File(attrs, options);
70 console.error('Unknown model type:', attrs['type']);
74 comparator: function(a, b) {
75 if (a.isFile() == b.isFile())
76 return a.get('name').toLowerCase().localeCompare(b.get('name').toLowerCase());
78 return (a.isFile() && b.isDir()) ? 1 : -1;
82 return this.filter(function (item) {
83 return item.isHidden();
87 visible: function () {
88 return this.filter(function (item) {
89 return !item.isHidden();
94 Filex.Path = Backbone.Collection.extend({
98 return this.pluck('name').join('/').replace(/^\/+/, '/');
101 append: function(name) {
102 return this.pluck('name').concat(name).join('/').replace(/^\/+/, '/');
107 // ------------------------------------------------------------------------
109 // ------------------------------------------------------------------------
111 Filex.Router = Backbone.Router.extend({
118 // ------------------------------------------------------------------------
120 // ------------------------------------------------------------------------
122 Filex.ListingItem = Backbone.View.extend({
129 initialize: function(options) {
130 this.view = options.view;
132 this.listenTo(this.model, 'change:checked', this.toggleChecked);
133 this.listenTo(this.model, 'remove', this.remove);
134 this.listenTo(this.model, 'hidden', this.toggleHidden);
137 template: function() {
138 var templateSelector = this.model.isDir() ? '#dir-template' : '#file-template';
140 return _.template($(templateSelector).html());
144 var data = this.model.toJSON();
146 if (this.model.isDir()) {
147 data['url'] = this.view.host + '/' + this.view.path.append(this.model.get('name'))
150 data['params'] = $.param({
151 host: this.view.host,
152 path: this.view.path.append(this.model.get('name'))
155 data['cid'] = this.model.cid;
157 this.$el.html(this.template()(data));
163 toggleHidden: function() {
164 var isHidden = this.model.isHidden() && !this.view.showHidden();
165 this.$el.toggleClass('hidden', isHidden);
167 if (isHidden && this.model.get('checked'))
171 toggleChecked: function(obj, value) {
172 this.$el.toggleClass('active', value);
173 this.$el.find('input[type="checkbox"]').prop('checked', value);
177 if (e.target.className != 'link')
182 Filex.Breadcrumb = Backbone.View.extend({
185 initialize: function() {
186 this.listenTo(this.model, 'change:active', this.render);
187 this.listenTo(this.model, 'remove', this.remove);
191 if (this.model.get('active')) {
192 this.$el.addClass('active');
193 this.$el.text(this.model.get('name'));
196 this.$el.html($('<a/>', {
197 href: '#' + this.model.get('url'),
198 text: this.model.get('name')
206 Filex.FilexView = Backbone.View.extend({
210 'change #show-hidden': 'toggleHidden',
211 'click #select-all': 'selectAll',
212 'click #btn-refresh': 'reloadFiles',
213 'click #btn-favorites': 'toggleFavorites',
214 'click #btn-host': 'editHost'
217 initialize: function(options) {
218 this.$noItems = $('#no-items');
219 this.$error = $('#error');
220 this.$showHidden = $('#show-hidden');
221 this.$selectAll = $('#select-all');
222 this.$favorites = $('#btn-favorites');
223 this.$host = $('#btn-host');
225 this.path = new Filex.Path();
226 this.files = new Filex.FileList();
227 this.router = new Filex.Router();
229 this.listenTo(this.path, 'add', this.addPath);
230 this.listenTo(this.path, 'reset', this.resetPath);
231 this.listenTo(this.files, 'reset', this.resetFiles);
232 this.listenTo(this.files, 'change:checked', this.updateSelectAll);
233 this.listenTo(this.router, 'route:load', this.load);
235 // used in selectize callbacks
237 optionTemplate = _.template('<div><div><%= host %></div><div class="small text-muted"><%= path %></div></div>');
239 this.$('#host-selector').selectize({
240 optgroupField: 'group',
242 searchField: ['host', 'path'],
244 {field: 'host', direction: 'asc'},
245 {field: 'path', direction: 'asc'}
247 options: options.locations,
249 {value: 'sys', label: 'Podstawowe'},
250 {value: 'usr', label: 'Użytkownika'}
252 lockOptgroupOrder: true,
253 create: function(input, callback) {
254 var $form = $('#favorite-form'),
255 parts = input.split('/'),
256 callback_called = false;
259 $form.find('.alert-danger').remove();
260 $form.find('#id_host').val(parts.shift());
263 $form.find('#id_path').val(parts.join('/'));
266 $form.on('submit', function(e) {
268 $btn = $this.find('[type="submit"]');
271 $btn.button('loading');
272 $this.find('.alert-danger').remove();
274 $.post($this.attr('action'), $this.serialize(), function(data) {
276 callback_called = true;
279 $btn.button('reset');
280 }, 'json').fail(function(xhr) {
281 console.error(arguments);
282 var error = (xhr.responseJSON || {}).error || undefined;
285 error = 'Podana lokalizacja jest już zapisana'
287 else if (xhr.status == 400) {
288 error = 'Niepoprawna nazwa hosta lub ścieżka'
291 if (typeof error === 'string') {
293 'class': 'alert alert-danger',
294 html: '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> ' + error
295 }).prependTo($this.find('.modal-body'));
296 $btn.button('reset');
299 $btn.button('error');
304 $form.one('hide.bs.modal', function() {
305 if (!callback_called)
313 option: function(item) {
314 return optionTemplate(item);
316 option_create : function(data, escape) {
317 return '<div class="create">Dodaj <em>' + escape(data.input) + '</em>…</div>';
320 onItemAdd: function(value) {
321 view.router.navigate('#' + value, {trigger: true});
325 $('#host').removeClass('edit');
329 this.hostSelectize = this.$('#host-selector')[0].selectize;
332 Backbone.history.start();
336 this.updateSelectAll();
337 this.updateFavorites();
338 this.$noItems.toggle(!Boolean(this.visibleFiles().length));
342 load: function(location) {
346 var hostRootPath = location.split(/\/(\/|~)(.*)/), self = this;
348 this.host = hostRootPath[0];
349 this.$host.text(this.host);
351 this.path.reset([new Filex.PathBit({'name': hostRootPath[1], url: this.host + '/' + hostRootPath[1]})]);
352 _.each(hostRootPath[2].split('/'), function(item) {
354 self.path.add(new Filex.PathBit({name: item, url: self.host + '/' + self.path.append(item)}));
356 this.path.last().set('active', true);
359 this.updateFavorites();
361 localStorage.last_location = location;
364 reloadFiles: function() {
371 data: {host: this.host, path: this.path.full()},
372 success: function() {
376 error: function(collection, response) {
379 var msg = (response.responseJSON || {}).error || 'Błąd serwera';
381 view.$noItems.hide();
382 view.$error.find('.msg').text(msg);
389 addPath: function(bit) {
390 var view = new Filex.Breadcrumb({model: bit});
391 this.$('.path').append(view.render().el);
394 resetPath: function(models, options) {
395 _.each(options.previousModels, function(model) {
396 model.trigger('remove');
399 this.path.each(this.addPath, this);
402 resetFiles: function(models, options) {
403 _.each(options.previousModels, function(model) {
404 model.trigger('remove');
407 this.files.each(function(file) {
408 var view = new Filex.ListingItem({model: file, view: this});
409 this.$('tbody').append(view.render().el);
413 toggleHidden: function() {
414 this.files.each(function(item) { item.trigger('hidden'); }, this);
418 showHidden: function() {
419 return this.$showHidden[0].checked;
423 this.$el.addClass('busy');
427 this.$el.removeClass('busy');
430 visibleFiles: function() {
431 return this.showHidden() ? this.files.models : this.files.visible();
434 selectedFiles: function() {
435 return _.filter(this.visibleFiles(), function(item) {
436 return item.get('checked');
440 selectAll: function() {
441 var checked = this.$selectAll[0].checked;
443 _.each(this.visibleFiles(), function(item) {
444 item.set('checked', checked);
448 updateSelectAll: function() {
449 if (this.visibleFiles().length) {
450 this.$selectAll.prop('disabled', false);
451 this.$selectAll.prop('checked', this.selectedFiles().length == this.visibleFiles().length);
454 this.$selectAll.prop('disabled', true);
455 this.$selectAll.prop('checked', false);
459 clearSelection: function() {
460 _.each(this.visibleFiles(), function(item) {
461 item.set('checked', false);
465 toggleFavorites: function() {
466 var $btn = this.$favorites,
467 locations = this.hostSelectize,
468 is_active = $btn.hasClass('active'),
469 url = is_active ? '/filex/fav/delete/' : '/filex/fav/add/',
472 path: this.path.full()
475 $btn.button('loading');
477 $.post(url, data, 'json').done(function (response) {
478 $btn.button('reset');
481 locations.removeOption(data.host + '/' + data.path);
483 locations.addOption(response);
485 $btn.button('reset');
486 $btn.button('toggle');
488 console.error(arguments);
492 initialLoad: function() {
493 var opts = this.hostSelectize.options,
494 location = localStorage.last_location || opts[Object.keys(opts)[0]].value;
496 this.router.navigate('#' + location, {trigger: true, replace: true});
499 updateFavorites: function() {
500 var loc = this.host + '/' + this.path.full(),
501 favorites = this.hostSelectize.options;
503 if (favorites.hasOwnProperty(loc)) {
504 if (favorites[loc].group == 'sys') {
505 this.$favorites.addClass('disabled').prop('disabled', true);
506 if (this.$favorites.hasClass('active'))
507 this.$favorites.button('toggle');
510 this.$favorites.removeClass('disabled').prop('disabled', false);
511 if (!this.$favorites.hasClass('active'))
512 this.$favorites.button('toggle');
516 this.$favorites.removeClass('disabled').prop('disabled', false);
517 if (this.$favorites.hasClass('active'))
518 this.$favorites.button('toggle');
522 editHost: function() {
523 $('#host').addClass('edit');
524 this.hostSelectize.focus();