176 lines
5.7 KiB
JavaScript
176 lines
5.7 KiB
JavaScript
var _ = require('underscore');
|
|
var cdb = require('internal-carto.js');
|
|
var Dashboard = require('deep-insights/api/dashboard.js');
|
|
var DashboardView = require('deep-insights/dashboard-view');
|
|
var WidgetsCollection = require('deep-insights/widgets/widgets-collection');
|
|
var WidgetsService = require('deep-insights/widgets-service');
|
|
var URLHelper = require('deep-insights/api/url-helper');
|
|
|
|
/**
|
|
* Translates a vizJSON v3 datastructure into a working dashboard which will be rendered in given selector.
|
|
*
|
|
* @param {String} selector e.g. "#foobar-id", ".some-class"
|
|
* @param {Object} vizJSON JSON datastructure
|
|
* @param {Object} opts (Optional) flags, see 3rd param for cdb.createVis for available ones. Keys used here:
|
|
* renderMenu: {Boolean} If true (default), render a top-level menu on the left side.
|
|
* @return {Object} with keys:
|
|
* dashboardView: root (backbone) view of the dashboard
|
|
* vis: the instantiated vis map, same result as given from cdb.createVis()
|
|
*/
|
|
var createDashboard = function (selector, vizJSON, opts, callback) {
|
|
var dashboardEl = document.querySelector(selector);
|
|
if (!dashboardEl) throw new Error('no element found with selector ' + selector);
|
|
|
|
// Default options
|
|
opts = opts || {};
|
|
opts.renderMenu = _.isBoolean(opts.renderMenu)
|
|
? opts.renderMenu
|
|
: true;
|
|
opts.autoStyle = _.isBoolean(opts.autoStyle)
|
|
? opts.autoStyle
|
|
: false;
|
|
|
|
var widgets = new WidgetsCollection();
|
|
|
|
var model = new cdb.core.Model({
|
|
title: vizJSON.title,
|
|
description: vizJSON.description,
|
|
updatedAt: vizJSON.updated_at,
|
|
userName: vizJSON.user.fullname,
|
|
userProfileURL: vizJSON.user.profile_url,
|
|
userAvatarURL: vizJSON.user.avatar_url,
|
|
renderMenu: opts.renderMenu,
|
|
autoStyle: opts.autoStyle,
|
|
showLogo: opts.cartodb_logo,
|
|
initialPosition: {
|
|
bounds: vizJSON.bounds
|
|
}
|
|
});
|
|
var dashboardView = new DashboardView({
|
|
el: dashboardEl,
|
|
widgets: widgets,
|
|
model: model
|
|
});
|
|
var dashboardState = opts.state || URLHelper.getStateFromCurrentURL();
|
|
if (dashboardState && !_.isEmpty(dashboardState.map)) {
|
|
if (_.isArray(dashboardState.map.center)) {
|
|
vizJSON.center = dashboardState.map.center;
|
|
}
|
|
if (_.isNumber(dashboardState.map.zoom)) {
|
|
vizJSON.zoom = dashboardState.map.zoom;
|
|
}
|
|
if (dashboardState.map.ne && dashboardState.map.sw) {
|
|
vizJSON.bounds = [dashboardState.map.ne, dashboardState.map.sw];
|
|
}
|
|
}
|
|
|
|
var vis = cdb.createVis(dashboardView.$('#map'), vizJSON, _.extend(opts, {
|
|
skipMapInstantiation: true
|
|
}));
|
|
|
|
vis.once('load', function (vis) {
|
|
var widgetsState = (dashboardState && dashboardState.widgets) || {};
|
|
|
|
// Create widgets
|
|
var widgetsService = new WidgetsService(widgets, vis.dataviews);
|
|
|
|
var widgetModelsMap = {
|
|
formula: widgetsService.createFormulaModel.bind(widgetsService),
|
|
histogram: widgetsService.createHistogramModel.bind(widgetsService),
|
|
'time-series': widgetsService.createTimeSeriesModel.bind(widgetsService),
|
|
category: widgetsService.createCategoryModel.bind(widgetsService)
|
|
};
|
|
vizJSON.widgets.forEach(function (widget) {
|
|
// Flatten the data structure given in vizJSON, the widgetsService will use whatever it needs and ignore the rest
|
|
var attrs = _.extend({}, widget, widget.options);
|
|
var newWidgetModel = widgetModelsMap[widget.type];
|
|
var state = widgetsState[widget.id];
|
|
|
|
if (_.isFunction(newWidgetModel)) {
|
|
// Find the Layer that the Widget should be created for.
|
|
var layer;
|
|
var source;
|
|
if (widget.layer_id) {
|
|
layer = vis.map.layers.get(widget.layer_id);
|
|
} else if (Number.isInteger(widget.layerIndex)) {
|
|
// TODO Since namedmap doesn't have ids we need to map in another way, here using index
|
|
// should we solve this in another way?
|
|
layer = vis.map.layers.at(widget.layerIndex);
|
|
}
|
|
if (widget.source && widget.source.id) {
|
|
source = vis.analysis.findNodeById(widget.source.id);
|
|
attrs.source = source;
|
|
}
|
|
|
|
newWidgetModel(attrs, layer, state);
|
|
} else {
|
|
cdb.log.error('No widget found for type ' + widget.type);
|
|
}
|
|
});
|
|
|
|
dashboardView.render();
|
|
|
|
var callbackObj = {
|
|
dashboardView: dashboardView,
|
|
widgets: widgetsService,
|
|
areWidgetsInitialised: function () {
|
|
var widgetsCollection = widgetsService.getCollection();
|
|
if (widgetsCollection.size() > 0) {
|
|
return widgetsCollection.hasInitialState();
|
|
}
|
|
return true;
|
|
},
|
|
vis: vis
|
|
};
|
|
|
|
vis.instantiateMap({
|
|
success: function () {
|
|
callback && callback(null, callbackObj);
|
|
},
|
|
error: function (errorMessage) {
|
|
callback && callback(new Error(errorMessage), callbackObj);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports = function (selector, vizJSON, opts, callback) {
|
|
var args = arguments;
|
|
var fn = args[args.length - 1];
|
|
|
|
if (_.isFunction(fn)) {
|
|
callback = fn;
|
|
}
|
|
|
|
function _load (vizJSON) {
|
|
createDashboard(selector, vizJSON, opts, function (error, dashboard) {
|
|
var _dashboard = new Dashboard(dashboard);
|
|
|
|
if (opts.share_urls) {
|
|
_dashboard.onStateChanged(
|
|
_.debounce(
|
|
function (state, url) {
|
|
window.history.replaceState('Object', 'Title', url);
|
|
},
|
|
500
|
|
)
|
|
);
|
|
}
|
|
|
|
callback && callback(error, _dashboard);
|
|
});
|
|
}
|
|
|
|
if (typeof vizJSON === 'string') {
|
|
cdb.core.Loader.get(vizJSON, function (data) {
|
|
if (data) {
|
|
_load(data, opts);
|
|
} else {
|
|
callback && callback(new Error('Error fetching viz.json file: ' + vizJSON));
|
|
}
|
|
});
|
|
} else {
|
|
_load(vizJSON, opts);
|
|
}
|
|
};
|