StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
10 KiB
474 lines
10 KiB
'use strict';
|
|
|
|
var Base = require('base');
|
|
var debug = require('debug')('base:templates:list');
|
|
var plugin = require('./plugins');
|
|
var Group = require('./group');
|
|
var utils = require('./utils');
|
|
|
|
/**
|
|
* Expose `List`
|
|
*/
|
|
|
|
module.exports = exports = List;
|
|
|
|
/**
|
|
* Create an instance of `List` with the given `options`.
|
|
* Lists differ from collections in that items are stored
|
|
* as an array, allowing items to be paginated, sorted,
|
|
* and grouped.
|
|
*
|
|
* ```js
|
|
* var list = new List();
|
|
* list.addItem('foo', {content: 'bar'});
|
|
* ```
|
|
* @param {Object} `options`
|
|
* @api public
|
|
*/
|
|
|
|
function List(options) {
|
|
if (!(this instanceof List)) {
|
|
return new List(options);
|
|
}
|
|
|
|
Base.call(this);
|
|
|
|
this.is('list');
|
|
this.define('isCollection', true);
|
|
this.use(utils.option());
|
|
this.use(utils.plugin());
|
|
this.init(options || {});
|
|
}
|
|
|
|
/**
|
|
* Inherit `Base` and load static plugins
|
|
*/
|
|
|
|
plugin.static(Base, List, 'List');
|
|
|
|
/**
|
|
* Initalize `List` defaults
|
|
*/
|
|
|
|
List.prototype.init = function(opts) {
|
|
debug('initalizing');
|
|
|
|
// add constructors to the instance
|
|
this.define('Item', opts.Item || List.Item);
|
|
this.define('View', opts.View || List.View);
|
|
|
|
// decorate the instance
|
|
this.use(plugin.init);
|
|
this.use(plugin.renameKey());
|
|
this.use(plugin.context);
|
|
this.use(utils.engines());
|
|
this.use(utils.helpers());
|
|
this.use(utils.routes());
|
|
this.use(plugin.item('item', 'Item', {emit: false}));
|
|
this.use(plugin.item('view', 'View', {emit: false}));
|
|
|
|
// decorate `isItem` and `isView`, in case custom class is passed
|
|
plugin.is(this.Item);
|
|
plugin.is(this.View);
|
|
|
|
this.queue = [];
|
|
this.items = [];
|
|
this.keys = [];
|
|
|
|
// if an instance of `List` of `Views` is passed, load it now
|
|
if (Array.isArray(opts) || opts.isList) {
|
|
this.options = opts.options || {};
|
|
this.addList(opts.items || opts);
|
|
|
|
} else if (opts.isCollection) {
|
|
this.options = opts.options;
|
|
this.addItems(opts.views);
|
|
|
|
} else {
|
|
this.options = opts;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add an item to `list.items`. This is identical to [setItem](#setItem)
|
|
* except `addItem` returns the `item`, add `setItem` returns the instance
|
|
* of `List`.
|
|
*
|
|
* ```js
|
|
* collection.addItem('foo', {content: 'bar'});
|
|
* ```
|
|
*
|
|
* @param {String|Object} `key` Item key or object
|
|
* @param {Object} `value` If key is a string, value is the item object.
|
|
* @developer The `item` method is decorated onto the collection using the `item` plugin
|
|
* @return {Object} returns the `item` instance.
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.addItem = function(key, value) {
|
|
var item = this.item(key, value);
|
|
utils.setInstanceNames(item, 'Item');
|
|
|
|
debug('adding item "%s"', item.key);
|
|
|
|
if (this.options.pager === true) {
|
|
List.addPager(item, this.items);
|
|
}
|
|
|
|
this.keys.push(item.key);
|
|
if (typeof item.use === 'function') {
|
|
this.run(item);
|
|
}
|
|
|
|
this.extendItem(item);
|
|
this.emit('load', item, this);
|
|
this.emit('item', item, this);
|
|
|
|
this.items.push(item);
|
|
return item;
|
|
};
|
|
|
|
/**
|
|
* Add an item to `list.items`. This is identical to [addItem](#addItem)
|
|
* except `addItem` returns the `item`, add `setItem` returns the instance
|
|
* of `List`.
|
|
*
|
|
* ```js
|
|
* var items = new Items(...);
|
|
* items.setItem('a.html', {path: 'a.html', contents: '...'});
|
|
* ```
|
|
* @param {String} `key`
|
|
* @param {Object} `value`
|
|
* @return {Object} Returns the instance of `List` to support chaining.
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.setItem = function(/*key, value*/) {
|
|
this.addItem.apply(this, arguments);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Load multiple items onto the collection.
|
|
*
|
|
* ```js
|
|
* collection.addItems({
|
|
* 'a.html': {content: '...'},
|
|
* 'b.html': {content: '...'},
|
|
* 'c.html': {content: '...'}
|
|
* });
|
|
* ```
|
|
* @param {Object|Array} `items`
|
|
* @return {Object} returns the instance for chaining
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.addItems = function(items) {
|
|
if (Array.isArray(items)) {
|
|
return this.addList.apply(this, arguments);
|
|
}
|
|
|
|
this.emit('addItems', items);
|
|
if (this.loaded) {
|
|
this.loaded = false;
|
|
return this;
|
|
}
|
|
|
|
this.visit('addItem', items);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Load an array of items or the items from another instance of `List`.
|
|
*
|
|
* ```js
|
|
* var foo = new List(...);
|
|
* var bar = new List(...);
|
|
* bar.addList(foo);
|
|
* ```
|
|
* @param {Array} `items` or an instance of `List`
|
|
* @param {Function} `fn` Optional sync callback function that is called on each item.
|
|
* @return {Object} returns the List instance for chaining
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.addList = function(list, fn) {
|
|
this.emit.call(this, 'addList', list);
|
|
if (this.loaded) {
|
|
this.loaded = false;
|
|
return this;
|
|
}
|
|
|
|
if (!Array.isArray(list)) {
|
|
throw new TypeError('expected list to be an array.');
|
|
}
|
|
|
|
if (typeof fn !== 'function') {
|
|
fn = utils.identity;
|
|
}
|
|
|
|
var len = list.length, i = -1;
|
|
while (++i < len) {
|
|
var item = list[i];
|
|
fn(item);
|
|
this.addItem(item.path, item);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Return true if the list has the given item (name).
|
|
*
|
|
* ```js
|
|
* list.addItem('foo.html', {content: '...'});
|
|
* list.hasItem('foo.html');
|
|
* //=> true
|
|
* ```
|
|
* @param {String} `key`
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.hasItem = function(key) {
|
|
return this.getIndex(key) !== -1;
|
|
};
|
|
|
|
/**
|
|
* Get a the index of a specific item from the list by `key`.
|
|
*
|
|
* ```js
|
|
* list.getIndex('foo.html');
|
|
* //=> 1
|
|
* ```
|
|
* @param {String} `key`
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.getIndex = function(key) {
|
|
if (typeof key === 'undefined') {
|
|
throw new Error('expected a string or instance of Item');
|
|
}
|
|
|
|
if (List.isItem(key)) {
|
|
key = key.key;
|
|
}
|
|
|
|
var idx = this.keys.indexOf(key);
|
|
if (idx !== -1) {
|
|
return idx;
|
|
}
|
|
|
|
idx = this.keys.indexOf(this.renameKey(key));
|
|
if (idx !== -1) {
|
|
return idx;
|
|
}
|
|
|
|
var items = this.items;
|
|
var len = items.length;
|
|
|
|
while (len--) {
|
|
var item = items[len];
|
|
var prop = this.renameKey(key, item);
|
|
if (utils.matchFile(prop, item)) {
|
|
return len;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Get a specific item from the list by `key`.
|
|
*
|
|
* ```js
|
|
* list.getItem('foo.html');
|
|
* //=> '<Item <foo.html>>'
|
|
* ```
|
|
* @param {String} `key` The item name/key.
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.getItem = function(key) {
|
|
var idx = this.getIndex(key);
|
|
if (idx !== -1) {
|
|
return this.items[idx];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Proxy for `getItem`
|
|
*
|
|
* ```js
|
|
* list.getItem('foo.html');
|
|
* //=> '<Item "foo.html" <buffer e2 e2 e2>>'
|
|
* ```
|
|
* @param {String} `key` Pass the key of the `item` to get.
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.getView = function() {
|
|
return this.getItem.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Remove an item from the list.
|
|
*
|
|
* ```js
|
|
* list.deleteItem('a.html');
|
|
* ```
|
|
* @param {Object|String} `key` Pass an `item` instance (object) or `item.key` (string).
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.deleteItem = function(item) {
|
|
var idx = this.getIndex(item);
|
|
if (idx !== -1) {
|
|
this.items.splice(idx, 1);
|
|
this.keys.splice(idx, 1);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Decorate each item on the list with additional methods
|
|
* and properties. This provides a way of easily overriding
|
|
* defaults.
|
|
*
|
|
* @param {Object} `item`
|
|
* @return {Object} Instance of item for chaining
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.extendItem = function(item) {
|
|
plugin.view(this, item);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Group all list `items` using the given property,
|
|
* properties or compare functions. See [group-array][]
|
|
* for the full range of available features and options.
|
|
*
|
|
* ```js
|
|
* var list = new List();
|
|
* list.addItems(...);
|
|
* var groups = list.groupBy('data.date', 'data.slug');
|
|
* ```
|
|
* @return {Object} Returns the grouped items.
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.groupBy = function() {
|
|
var args = [].slice.call(arguments);
|
|
var fn = utils.groupBy;
|
|
|
|
// Make `items` the first argument for group-array
|
|
args.unshift(this.items.slice());
|
|
|
|
// group the `items` and return the result
|
|
return new Group(fn.apply(fn, args));
|
|
};
|
|
|
|
/**
|
|
* Sort all list `items` using the given property,
|
|
* properties or compare functions. See [array-sort][]
|
|
* for the full range of available features and options.
|
|
*
|
|
* ```js
|
|
* var list = new List();
|
|
* list.addItems(...);
|
|
* var result = list.sortBy('data.date');
|
|
* //=> new sorted list
|
|
* ```
|
|
* @return {Object} Returns a new `List` instance with sorted items.
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.sortBy = function() {
|
|
var args = [].slice.call(arguments);
|
|
var last = args[args.length - 1];
|
|
var opts = this.options.sort || {};
|
|
|
|
// extend `list.options.sort` global options with local options
|
|
if (last && typeof last === 'object' && !Array.isArray(last)) {
|
|
opts = utils.extend({}, opts, args.pop());
|
|
}
|
|
|
|
// create the args to pass to array-sort
|
|
args.unshift(this.items.slice());
|
|
args.push(opts);
|
|
|
|
// sort the `items` array, then sort `keys`
|
|
var items = utils.sortBy.apply(utils.sortBy, args);
|
|
var list = new List(this.options);
|
|
list.addItems(items);
|
|
return list;
|
|
};
|
|
|
|
/**
|
|
* Resolve the layout to use for the given `item`
|
|
*
|
|
* @param {Object} `item`
|
|
* @return {String} Returns the name of the layout to use.
|
|
*/
|
|
|
|
List.prototype.resolveLayout = function(item) {
|
|
if (utils.isRenderable(item) && typeof item.layout === 'undefined') {
|
|
return this.option('layout');
|
|
}
|
|
return item.layout;
|
|
};
|
|
|
|
/**
|
|
* Paginate all `items` in the list with the given options,
|
|
* See [paginationator][] for the full range of available
|
|
* features and options.
|
|
*
|
|
* ```js
|
|
* var list = new List(items);
|
|
* var pages = list.paginate({limit: 5});
|
|
* ```
|
|
* @return {Object} Returns the paginated items.
|
|
* @api public
|
|
*/
|
|
|
|
List.prototype.paginate = function() {
|
|
var args = [].slice.call(arguments);
|
|
var fn = utils.paginationator;
|
|
|
|
// Make `items` the first argument for paginationator
|
|
args.unshift(this.items.slice());
|
|
|
|
// paginate the `items` and return the pages
|
|
var items = fn.apply(fn, args);
|
|
return items.pages;
|
|
};
|
|
|
|
/**
|
|
* Add paging (`prev`/`next`) information to the
|
|
* `data` object of an item.
|
|
*
|
|
* @param {Object} `item`
|
|
* @param {Array} `items` instance items.
|
|
*/
|
|
|
|
List.addPager = function addPager(item, items) {
|
|
item.data.pager = {};
|
|
item.data.pager.index = items.length;
|
|
utils.define(item.data.pager, 'current', item);
|
|
|
|
if (items.length) {
|
|
var prev = items[items.length - 1];
|
|
utils.define(item.data.pager, 'prev', prev);
|
|
utils.define(prev.data.pager, 'next', item);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Expose static properties
|
|
*/
|
|
|
|
utils.define(List, 'Item', require('vinyl-item'));
|
|
utils.define(List, 'View', require('vinyl-view'));
|
|
|