UNPKG

agendash

Version:
365 lines (344 loc) 11.3 kB
/* global $, _, Backbone */ $(function () { var CurrentRequestModel = Backbone.Model.extend({ defaults: { refreshInterval: 2000, overviewFilterRegex: /.*/ } }) var ActiveTitleModel = Backbone.Model.extend({}) var OverviewItemModel = Backbone.Model.extend({}) var OverviewItemCollection = Backbone.Collection.extend({ model: OverviewItemModel }) var JobItemModel = Backbone.Model.extend({ idAttribute: '_id', defaults: { selected: false } }) var JobItemCollection = Backbone.Collection.extend({ model: JobItemModel }) var ActiveTitleView = Backbone.View.extend({ el: '#active-title', initialize: function (options) { this.activeTitle = options.activeTitle _.bindAll(this, 'render') this.listenTo(this.activeTitle, 'change', this.render) }, render: function () { this.$('.active-job').text(this.activeTitle.get('job')) this.$('.active-state').text(this.activeTitle.get('state')) return this } }) var OverviewItemView = Backbone.View.extend({ model: OverviewItemModel, template: _.template($('#overview-item-template').html()), initialize: function () { _.bindAll(this, 'render', 'handleClick') }, events: { 'click [data-state]': 'handleClick' }, handleClick: function (e) { App.trigger('requestChange', { job: this.model.get('_id'), state: $(e.currentTarget).data('state') }) }, render: function () { var html = this.template(this.model.toJSON()) this.setElement(html) return this } }) var OverviewListView = Backbone.View.extend({ el: '#job-overview-list', initialize: function (options) { this.overviewItems = options.overviewItems this.currentRequest = options.currentRequest _.bindAll(this, 'render') this.listenTo(this.overviewItems, 'update', this.render) this.render() }, render: function () { var overviewFilterRegex = this.currentRequest.get('overviewFilterRegex') this.$el.empty().append(this.overviewItems.filter(function (overviewItem) { return overviewFilterRegex.test(overviewItem.get('_id')) }).map(function (overviewItem) { var overviewItemView = new OverviewItemView({ model: overviewItem }) return overviewItemView.render().$el })) return this } }) var JobItemView = Backbone.View.extend({ model: JobItemModel, tagName: 'tr', template: _.template($('#job-item-template').html()), initialize: function () { _.bindAll(this, 'render', 'handleClick') this.listenTo(this.model, 'change', this.render) }, events: { 'click': 'handleClick' }, handleClick: function (e) { this.model.set('selected', !this.model.get('selected')) }, render: function () { this.$el.html(this.template(this.model.toJSON())) this.$el.toggleClass('active', this.model.get('selected')) return this } }) var JobListView = Backbone.View.extend({ el: '#job-list', initialize: function (options) { this.jobItems = options.jobItems _.bindAll(this, 'render') this.listenTo(this.jobItems, 'update', this.render) this.render() }, render: _.throttle(function () { this.$el.empty().append(this.jobItems.map(function (jobItem) { var jobItemView = new JobItemView({ model: jobItem }) return jobItemView.render().$el })) return this }, 300) }) var SidebarView = Backbone.View.extend({ el: '#sidebar', events: { 'keyup .overview-filter': 'updateOverviewFilter', 'change .refresh-interval': 'updateRefreshInterval' }, initialize: function (options) { this.currentRequest = options.currentRequest _.bindAll(this, 'updateOverviewFilter', 'updateRefreshInterval') }, updateOverviewFilter: function (e) { // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex var str = $(e.currentTarget).val() var newFilterRegex = new RegExp(str.split('').map(function (char) { return char.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/, '\\$&') }).join('.*'), 'i') this.currentRequest.set({ overviewFilterRegex: newFilterRegex }) }, updateRefreshInterval: function (e) { this.currentRequest.set({ refreshInterval: +$(e.currentTarget).val() * 1000 }) } }) var JobDetailsPaneView = Backbone.View.extend({ el: '#details-pane', initialize: function (options) { this.jobItems = options.jobItems _.bindAll(this, 'render', 'getSelectedJobs', 'requeueJobs', 'allowDeleteJobs', 'deleteJobs') this.listenTo(this.jobItems, 'update', this.render) this.listenTo(this.jobItems, 'change', this.render) this.render() }, events: { 'click [data-action=requeue-jobs]': 'requeueJobs', 'click [data-action=delete-jobs]': 'allowDeleteJobs', 'click [data-action=delete-jobs].deleteable': 'deleteJobs' }, getSelectedJobs: function () { return this.jobItems.where({selected: true}) }, render: function () { var selectedJobCount = this.getSelectedJobs().length this.$('.number-selected').text(selectedJobCount) this.$el.toggle(!!selectedJobCount) this.$('[data-action=delete-jobs]').removeClass('deleteable').text('Delete selected') return this }, requeueJobs: function () { var selectedJobIds = this.getSelectedJobs().map((j) => j.get('_id')) postJobs('requeue', selectedJobIds) .success(function () { App.trigger('refreshData') }) }, allowDeleteJobs: function () { this.$('[data-action=delete-jobs]').addClass('deleteable').text('Confirm delete selection') }, deleteJobs: function () { var selectedJobIds = this.getSelectedJobs().map((j) => j.get('_id')) postJobs('delete', selectedJobIds) .success(function () { App.trigger('refreshData') }) } }) var JobDetailsListView = Backbone.View.extend({ el: '#details-list', initialize: function (options) { this.jobItems = options.jobItems _.bindAll(this, 'render') this.listenTo(this.jobItems, 'update', this.render) this.listenTo(this.jobItems, 'change', this.render) this.render() }, render: _.throttle(function () { var selectedJobs = this.jobItems .where({selected: true}) .map(function (jobItem) { var jobDetailsView = new JobDetailsView({ model: jobItem }) return jobDetailsView.render().$el }) this.$el.empty().toggle(!!selectedJobs.length).append(selectedJobs) return this }, 300) }) var JobDetailsView = Backbone.View.extend({ model: JobItemModel, template: _.template($('#job-item-details-template').html()), initialize: function () { _.bindAll(this, 'render', 'close', 'requeueJob', 'allowDeleteJob', 'deleteJob') this.listenTo(this.model, 'change', this.render) }, events: { 'click .close': 'close', 'click [data-action=requeue]': 'requeueJob', 'click [data-action=delete]': 'allowDeleteJob', 'click [data-action=delete].deleteable': 'deleteJob' }, showJob: function (jobItem) { this.model = jobItem this.$el.empty().append(this.jobItemDetailsTemplate(jobItem.toJSON())) }, requeueJob: function (e) { postJobs('requeue', [this.model.get('job')._id]) .success(function () { $(e.currentTarget).remove() App.trigger('refreshData') }) }, allowDeleteJob: function (e) { $(e.currentTarget).addClass('deleteable').text('Confirm deletion') }, deleteJob: function (e) { postJobs('delete', [this.model.get('job')._id]) .success(function () { App.trigger('refreshData') }) }, close: function () { this.model.set('selected', false) }, render: function () { this.$el.html(this.template(this.model.toJSON())) return this } }) var SelectJobsView = Backbone.View.extend({ el: '#select-jobs', initialize: function (options) { this.jobItems = options.jobItems _.bindAll(this, 'selectAll', 'selectNone') }, events: { 'click [data-action=select-all]': 'selectAll', 'click [data-action=select-none]': 'selectNone' }, selectAll: function () { this.jobItems.forEach(function (jobItem) { jobItem.set({selected: true}) }) }, selectNone: function () { this.jobItems.forEach(function (jobItem) { jobItem.set({selected: false}) }) } }) var AppView = Backbone.View.extend({ el: '#app', initialize: function () { _.bindAll(this, 'handleRequestChange', 'handleShowJobDetails', 'fetchData', 'resultsFetched' ) this.activeTitle = new ActiveTitleModel() this.currentRequest = new CurrentRequestModel() this.overviewItems = new OverviewItemCollection() this.jobItems = new JobItemCollection() this.activeTitleView = new ActiveTitleView({ activeTitle: this.activeTitle }) this.sidebarView = new SidebarView({ currentRequest: this.currentRequest }) this.overviewListView = new OverviewListView({ currentRequest: this.currentRequest, overviewItems: this.overviewItems }) this.jobListView = new JobListView({ jobItems: this.jobItems }) this.jobDetailsPaneView = new JobDetailsPaneView({ jobItems: this.jobItems }) this.jobDetailsListView = new JobDetailsListView({ jobItems: this.jobItems }) this.selectJobsView = new SelectJobsView({ jobItems: this.jobItems }) this.listenTo(this, 'requestChange', this.handleRequestChange) this.listenTo(this, 'showJobDetails', this.handleShowJobDetails) this.listenTo(this, 'refreshData', this.fetchData) this.listenTo(this.currentRequest, 'change', this.fetchData) this.fetchData() }, handleRequestChange: function (newRequest) { this.currentRequest.set(newRequest) }, handleShowJobDetails: function (jobItem) { this.jobDetailsView.showJob(jobItem) }, fetchData: function () { this._fetchRequest && this._fetchRequest.abort() this._fetchTimeout && clearTimeout(this._fetchTimeout) this._fetchRequest = $.get('api', { job: this.currentRequest.get('job'), state: this.currentRequest.get('state') }).success(this.resultsFetched) }, resultsFetched: function (results) { this.overviewItems.set(results.overview) this.activeTitle.set({ job: results.currentRequest.job, state: results.currentRequest.state }) this.jobItems.set(results.jobs) this._fetchTimeout = setTimeout(this.fetchData, this.currentRequest.get('refreshInterval')) } }) var App = new AppView() function postJobs (action, jobIds) { return $.ajax({ type: 'POST', url: 'api/jobs/' + action, data: JSON.stringify({jobIds: jobIds}), contentType: 'application/json', dataType: 'json' }) } })