1 module.exports = ArvListComponent;
3 var m = require('mithril')
4 , ArvadosClient = require('arvados/client')
5 , BaseController = require('app/base-ctrl')
6 , Filter = require('app/filter')
7 , FilterSet = require('app/filterset')
8 , util = require('app/util');
10 function ArvListComponent(connection, arvModelName, contentModule) {
11 this.controller = Controller;
14 Controller.prototype = new BaseController();
15 function Controller() {
16 this.vm = new ViewModel();
18 this.vm.filterSetCtrl = new this.vm.filterSet.controller(this);
20 Controller.prototype.getMoreItems =
21 function getMoreItems() {
22 return this.vm.getMoreItems.apply(this.vm, arguments);
24 Controller.prototype.eof =
28 Controller.prototype.currentFilter =
29 function currentFilter(key, attr, operator, operand) {
30 if (arguments.length > 1) {
31 this.vm.filters[key] = [attr, operator, operand];
32 util.debounce(500, this.vm.resetContent).
33 then(this.vm.resetContent);
35 return this.vm.filters[key];
38 function ViewModel() {
40 vm.init = function() {
41 vm.arvModelName = arvModelName || m.route.param('modelName');
42 vm.connection = connection || ArvadosClient.make(m.route.param('connection'));
44 vm.filterSet = new FilterSet(
45 [['any', Filter.AnyText],
46 ['type', Filter.ObjectType, {attr:'uuid'}]]);
49 vm.listOrders = ['created_at desc'];
52 vm.resetContent = function() {
54 // Forget about current/stale request. TODO: abort the xhr.
58 vm.eof = m.prop(false);
59 vm.items = m.prop([]);
60 vm.itemViews = m.prop([]);
61 vm.beforeRender = function() {
62 // On first render, trigger a scroll event to make the
63 // first page of content appear. The scroll handler can
64 // ignore this if (for example) the content view is
66 vm.beforeRender = function() {};
67 window.setTimeout(function() {
68 window.dispatchEvent(new Event('scroll'));
72 vm.apiFilters = function() {
74 Object.keys(vm.filters).map(function(key) {
76 filters.push(vm.filters[key]);
80 vm.makeItemViews = function() {
81 vm.itemViews(vm.items().map(function(item) {
82 return contentModule.view.bind(
84 new contentModule.controller({item:item}));
87 vm.getMoreItems = function() {
89 if (vm.inflight || vm.eof())
91 inflight = m.deferred();
92 vm.connection.api(vm.arvModelName, 'list', {
93 filters: vm.apiFilters(),
95 offset: vm.items().length,
97 }).then(function(newItems) {
98 if (inflight !== vm.inflight) {
99 // This request has already been superseded by a
103 vm.eof(newItems.length === 0 ||
104 (typeof newItems.offset === 'number' &&
105 newItems.items_available === newItems.offset + newItems.length));
106 vm.items(vm.items().concat(newItems));
107 }, vm.eof).then(vm.makeItemViews).then(function() {
108 // Give the new items a chance to render before
109 // resolving the promise. This makes it possible for
110 // the resolve callback to measure the DOM after the
111 // new elements have been added (notably, in order to
112 // keep fetching pages until the scroll threshold is
114 window.setTimeout(inflight.resolve, 50);
117 return (vm.inflight = inflight).promise;
122 function View(ctrl) {
123 ctrl.vm.beforeRender();
125 ctrl.vm.filterSet ? ctrl.vm.filterSet.view(ctrl.vm.filterSetCtrl) : '',
126 ctrl.vm.itemViews().map(function(v) {
129 ctrl.vm.eof() ? '' : m('.row', {style: 'background: #ffffdd'}, [
130 m('.col-sm-12', {style: 'text-align: center'}, ['...loading...'])