Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,8 @@
<ul class="CodeMirror-error">
<li class="CodeMirror-errorMessage u-lSpace--xl u-rSpace--xl">
<%- _t('components.codemirror.syntax-error') %>: <span><%- message %></span>
</li>
<li class="CodeMirror-errorDocs">
<a href="https://carto.com/developers/sql-api/" target="_black"><%- _t('components.codemirror.docs') %></a>
</li>
</ul>
@@ -0,0 +1,100 @@
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var template = require('./dataset-actions.tpl');
var UndoButtons = require('builder/components/undo-redo/undo-redo-view');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var REQUIRED_OPTS = [
'mapAction',
'previewAction',
'trackModel',
'editorModel',
'queryGeometryModel',
'querySchemaModel',
'onApply',
'onClear',
'clearSQLModel',
'applyButtonStatusModel'
];
module.exports = CoreView.extend({
className: 'Dataset-options-actions',
events: {
'click .js-createMap': '_onCreateMap',
'click .js-previewMap': '_onPreviewMap'
},
initialize: function (opts) {
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
this._initViewState();
this.listenTo(this._queryGeometryModel, 'change:status', this._setViewState);
this.listenTo(this._querySchemaModel, 'change:status', this._updateApplyLoadingButtonLoading);
this.listenTo(this._viewState, 'change', this.render);
},
render: function () {
this.clearSubViews();
this.$el.html(
template({
hasGeometry: this._viewState.get('hasGeom'),
canCreateMap: true
})
);
this._initViews();
return this;
},
_initViewState: function () {
this._viewState = new Backbone.Model({
hasGeom: true
});
this._setViewState();
},
_initViews: function () {
var view = new UndoButtons({
className: 'u-rSpace--xl',
trackModel: this._trackModel,
editorModel: this._editorModel,
clearModel: this._clearSQLModel,
applyStatusModel: this._applyButtonStatusModel,
applyButton: true,
clearButton: true,
onApplyClick: this._handleApply.bind(this),
onClearClick: this._handleClear.bind(this)
});
this.$('.js-createMap').before(view.render().el);
this.addView(view);
},
_handleApply: function () {
if (this._onApply) { this._onApply(); }
},
_handleClear: function () {
if (this._onClear) { this._onClear(); }
},
_onCreateMap: function () {
if (this._mapAction) { this._mapAction(); }
},
_onPreviewMap: function () {
if (this._previewAction) { this._previewAction(); }
},
_updateApplyLoadingButtonLoading: function () {
this._applyButtonStatusModel.set('loading', this._querySchemaModel.isFetching());
},
_setViewState: function () {
var self = this;
this._queryGeometryModel.hasValueAsync()
.then(function (hasGeom) {
self._viewState.set('hasGeom', hasGeom);
});
}
});
@@ -0,0 +1,70 @@
var CoreView = require('backbone/core-view');
var _ = require('underscore');
var Backbone = require('backbone');
var template = require('./dataset-actions.tpl');
// 'mapAction' is not required because a viewer user can't create maps
var REQUIRED_OPTS = {
previewAction: true,
queryGeometryModel: true,
mapAction: false
};
module.exports = CoreView.extend({
className: 'Dataset-options-actions',
events: {
'click .js-createMap': '_onCreateMap',
'click .js-previewMap': '_onPreviewMap'
},
initialize: function (opts) {
_.each(REQUIRED_OPTS, function (isRequired, item) {
if (opts[item]) {
this['_' + item] = opts[item];
} else if (isRequired) {
throw new Error(item + ' is required');
}
}, this);
this._initViewState();
this.listenTo(this._queryGeometryModel, 'change:status', this._setViewState);
this.listenTo(this._viewState, 'change', this.render);
},
render: function () {
this.clearSubViews();
this.$el.html(
template({
hasGeometry: this._viewState.get('hasGeometry'),
canCreateMap: !!this._mapAction
})
);
return this;
},
_initViewState: function () {
this._viewState = new Backbone.Model({
hasGeometry: true
});
this._setViewState();
},
_onPreviewMap: function () {
this._previewAction && this._previewAction();
},
_onCreateMap: function () {
this._mapAction && this._mapAction();
},
_setViewState: function () {
this._queryGeometryModel.hasValueAsync()
.then(function (hasGeom) {
this._viewState.set('hasGeometry', hasGeom);
}.bind(this))
.catch(function () {
this._viewState.set('hasGeometry', false);
}.bind(this));
}
});
@@ -0,0 +1,10 @@
<% if (hasGeometry) { %>
<button class="u-rSpace--xl Dataset-tablePreview js-previewMap">
<span class="u-upperCase CDB-Button-Text CDB-Text CDB-Size-small"><%- _t('dataset.preview-map.preview') %></span>
</button>
<% } %>
<% if (canCreateMap) { %>
<button class="CDB-Button CDB-Button--primary u-upperCase js-createMap">
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-small"><%- _t('dataset.create-map.title') %></span>
</button>
<% } %>
@@ -0,0 +1,287 @@
var Backbone = require('backbone');
var $ = require('jquery');
var CoreView = require('backbone/core-view');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var CreationModalView = require('builder/components/modals/creation/modal-creation-view');
var DatasetBaseView = require('builder/components/dataset/dataset-base-view');
var errorParser = require('builder/helpers/error-parser');
var PanelWithOptionsView = require('builder/components/view-options/panel-with-options-view');
var TabPaneView = require('builder/components/tab-pane/tab-pane-view');
var TabPaneCollection = require('builder/components/tab-pane/tab-pane-collection');
var Toggler = require('builder/components/toggler/toggler-view');
var DatasetEditorView = require('./dataset-sql-view');
var ActionView = require('./dataset-actions-view');
var PreviewMapView = require('./preview-map-view');
var ActionViewEdition = require('./dataset-actions-edition-view');
var DataSQLModel = require('builder/dataset/data-sql-model');
var SQLNotifications = require('builder/sql-notifications');
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
var MetricsTypes = require('builder/components/metrics/metrics-types');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var REQUIRED_OPTS = [
'analysisDefinitionNodeModel',
'configModel',
'editorModel',
'layerDefinitionModel',
'modals',
'onToggleEdition',
'router',
'userModel',
'visModel'
];
module.exports = DatasetBaseView.extend({
initialize: function (opts) {
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
this._querySchemaModel = this._analysisDefinitionNodeModel.querySchemaModel;
if (!this._layerDefinitionModel.sqlModel) {
var sqlHistory = this._layerDefinitionModel.options && this._layerDefinitionModel.options.sql_history;
this._layerDefinitionModel.sqlModel = new DataSQLModel({
content: this._querySchemaModel.get('query')
}, {
history: sqlHistory || []
});
}
DatasetBaseView.prototype.initialize.call(this, {
layerDefinitionModel: this._layerDefinitionModel,
editorModel: this._editorModel,
configModel: this._configModel,
querySchemaModel: this._querySchemaModel
});
this._tableModel = this._analysisDefinitionNodeModel.getTableModel();
this._queryGeometryModel = this._analysisDefinitionNodeModel.queryGeometryModel;
this._canCreateMap = this._userModel.hasCreateMapsFeature();
this._parseSQL = this._internalParseSQL.bind(this);
SQLNotifications.track(this);
this._togglerModel = new Backbone.Model({
labels: [_t('dataset.data'), _t('dataset.sql')],
active: this._editorModel.isEditing(),
disabled: this._editorModel.isDisabled()
});
this._configPanes();
this._initBinds();
},
render: function () {
this.clearSubViews();
this.$el.empty();
this._initViews();
this._checkClearButton();
return this;
},
_initBinds: function () {
this.listenTo(this._editorModel, 'change:edition', this._onChangeEdition);
this.listenTo(this._editorModel, 'change:disabled', this._onChangeDisabled);
this.listenTo(this._togglerModel, 'change:active', this._onTogglerChanged);
this.listenTo(this._querySchemaModel, 'change:query_errors', this._showErrors);
this.listenTo(this._visModel, 'change:name', this._onChangeName);
this.listenTo(this._sqlModel, 'undo redo', function () {
this._codemirrorModel.set('content', this._sqlModel.get('content'));
});
},
_onChangeName: function (model, name) {
this._codemirrorModel.set('content', this._analysisDefinitionNodeModel.getDefaultQuery());
},
_initViews: function () {
var self = this;
var panelWithOptionsView = new PanelWithOptionsView({
editorModel: self._editorModel,
createContentView: function () {
return new TabPaneView({
collection: self._collectionPane
});
},
createControlView: function () {
return new Toggler({
model: self._togglerModel
});
},
createActionView: function () {
return new TabPaneView({
collection: self._collectionPane,
createContentKey: 'createActionView'
});
}
});
this.$el.append(panelWithOptionsView.render().el);
this.addView(panelWithOptionsView);
},
_onChangeEdition: function () {
this._onToggleEdition();
var edition = this._editorModel.get('edition');
var index = edition ? 1 : 0;
this._collectionPane.at(index).set({ selected: true });
this._togglerModel.set({ active: edition });
},
_onChangeDisabled: function () {
var disabled = this._editorModel.get('disabled');
this._togglerModel.set({ disabled: disabled });
},
_onTogglerChanged: function () {
var checked = this._togglerModel.get('active');
this._editorModel.set({ edition: checked });
},
_configPanes: function () {
var self = this;
var tabPaneTabs = [{
selected: !this._editorModel.get('edition'),
createContentView: function () {
return new CoreView();
},
createActionView: function () {
var actionViewOptions = {
queryGeometryModel: self._queryGeometryModel,
previewAction: self._previewMap.bind(self)
};
if (self._canCreateMap) {
actionViewOptions.mapAction = self._createMap.bind(self);
}
return new ActionView(actionViewOptions);
}
}, {
selected: this._editorModel.get('edition'),
createContentView: function () {
return new DatasetEditorView({
editorModel: self._editorModel,
codemirrorModel: self._codemirrorModel,
onApplyEvent: self._parseSQL,
layerDefinitionModel: self._layerDefinitionModel,
querySchemaModel: self._querySchemaModel
});
},
createActionView: function () {
var actionViewOptions = {
clearSQLModel: self._clearSQLModel,
trackModel: self._sqlModel,
editorModel: self._editorModel,
queryGeometryModel: self._queryGeometryModel,
querySchemaModel: self._querySchemaModel,
onApply: self._parseSQL,
previewAction: self._previewMap.bind(self),
onClear: self._clearSQL.bind(self),
applyButtonStatusModel: self._applyButtonStatusModel
};
if (self._canCreateMap) {
actionViewOptions.mapAction = self._createMap.bind(self);
}
return new ActionViewEdition(actionViewOptions);
}
}];
this._collectionPane = new TabPaneCollection(tabPaneTabs);
},
_runQuery: function (query, callback) {
this._querySchemaModel.set({
query: query,
status: 'unfetched'
});
this._queryGeometryModel.set({
query: query,
simple_geom: '',
status: 'unfetched'
}, {
silent: true
});
this._querySchemaModel.fetch({
success: callback
});
this._queryGeometryModel.fetch();
},
_saveSQL: function () {
var content = this._codemirrorModel.get('content');
this._sqlModel.set('content', content);
this._querySchemaModel.set('query_errors', []);
if (this._tableModel.hasWriteAccess(this._userModel)) {
this._layerDefinitionModel.save({
sql: content
});
MetricsTracker.track(MetricsTypes.APPLIED_SQL, {
dataset_id: this._tableModel.get('id'),
sql: content
});
}
SQLNotifications.showNotification({
status: 'success',
info: _t('notifications.sql.success'),
closable: true
});
this._checkClearButton();
},
_defaultSQL: function () {
return this._analysisDefinitionNodeModel.getDefaultQuery();
},
_previewMap: function () {
var previewMap = new PreviewMapView({
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
configModel: this._configModel,
modals: this._modals,
userModel: this._userModel,
visModel: this._visModel
});
$('body').append(previewMap.render().el);
this.addView(previewMap);
},
_createMap: function () {
var self = this;
var tableName = this._tableModel.getUnquotedName();
this._modals.create(function (modalModel) {
return new CreationModalView({
modalModel: modalModel,
loadingTitle: _t('dataset.create-map.loading', { tableName: tableName }),
errorTitle: _t('dataset.create-map.error', { tableName: tableName }),
runAction: function (opts) {
var newVisModel = new VisDefinitionModel({
name: self._visModel.get('name') + ' ' + _t('editor.map')
}, {
configModel: self._configModel
});
newVisModel.save({
source_visualization_id: self._visModel.get('id')
}, {
success: function (visModel) {
window.location = visModel.builderURL();
},
error: function (mdl, e) {
opts.error && opts.error(errorParser(e));
}
});
}
});
});
}
});
@@ -0,0 +1,4 @@
<ul class="Options-bar u-flex u-justifySpace js-theme">
<li class="CDB-InfoBox-footerItem js-controls"></li>
<li class="CDB-InfoBox-footerItem CDB-InfoBox-footerItem--right js-actions"></li>
</ul>
@@ -0,0 +1,58 @@
var CoreView = require('backbone/core-view');
var CodeMirrorView = require('builder/components/code-mirror/code-mirror-view');
var ErrorTemplate = require('./code-mirror-error.tpl');
var FactoryHints = require('builder/editor/editor-hints/factory-hints');
var SQLHints = require('builder/editor/editor-hints/sql-hints');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var REQUIRED_OPTS = [
'codemirrorModel',
'onApplyEvent',
'querySchemaModel',
'layerDefinitionModel'
];
module.exports = CoreView.extend({
module: 'dataset:dataset-options:dataset-sql-view',
className: 'Editor-content Dataset-editor',
initialize: function (opts) {
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
FactoryHints.init({
querySchemaModel: this._querySchemaModel,
layerDefinitionModel: this._layerDefinitionModel,
tokens: SQLHints
});
},
render: function () {
this.clearSubViews();
this.$el.empty();
this._initViews();
return this;
},
_initViews: function () {
var hints = FactoryHints.reset().hints;
this.codeMirrorView = new CodeMirrorView({
className: 'Dataset-codemirror',
model: this._codemirrorModel,
hints: hints,
mode: 'text/x-pgsql',
tips: [
_t('editor.data.code-mirror.tip')
],
errorTemplate: ErrorTemplate
});
this.codeMirrorView.bind('codeSaved', this._triggerCodeSaved, this);
this.addView(this.codeMirrorView);
this.$el.append(this.codeMirrorView.render().el);
},
_triggerCodeSaved: function (code, view) {
this._onApplyEvent && this._onApplyEvent();
}
});
@@ -0,0 +1,117 @@
var CoreView = require('backbone/core-view');
var $ = require('jquery');
var cdb = require('internal-carto.js');
var CreationModalView = require('builder/components/modals/creation/modal-creation-view');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var errorParser = require('builder/helpers/error-parser');
var template = require('./preview-map.tpl');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var ESC_KEY_CODE = 27;
var REQUIRED_OPTS = [
'analysisDefinitionNodeModel',
'configModel',
'modals',
'userModel',
'visModel'
];
module.exports = CoreView.extend({
className: 'PreviewMap',
events: {
'click .js-back': 'clean',
'click .js-createMap': '_createMap'
},
initialize: function (opts) {
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
this._tableModel = this._analysisDefinitionNodeModel.getTableModel();
this._querySchemaModel = this._analysisDefinitionNodeModel.querySchemaModel;
this._syncModel = this._tableModel.getSyncModel();
this._canCreateMap = this._userModel.hasCreateMapsFeature();
this._onKeyDown = this._onKeyDown.bind(this);
this._initKeydownBinds();
},
render: function () {
this.$el.html(
template({
isOwner: this._tableModel.isOwner(this._userModel),
isSync: this._tableModel.isSync(),
syncState: this._syncModel.get('state'),
name: this._tableModel.get('name'),
isCustomQueryApplied: this._analysisDefinitionNodeModel.isCustomQueryApplied(),
canCreateMap: this._canCreateMap
})
);
this._initViews();
return this;
},
_initViews: function () {
this._map = cdb.createVis(
this.$('.js-map'),
this._visModel.vizjsonURL(),
{
legends: false,
authToken: this._configModel.get('auth_tokens')
}
);
},
_initKeydownBinds: function () {
$(document).bind('keydown', this._onKeyDown);
},
_destroyKeydownBinds: function () {
$(document).unbind('keydown', this._onKeyDown);
},
_onKeyDown: function (ev) {
var keyCode = ev.which;
if (keyCode === ESC_KEY_CODE) {
this.clean();
}
},
_createMap: function () {
var self = this;
var tableName = this._tableModel.getUnquotedName();
this._modals.create(function (modalModel) {
return new CreationModalView({
modalModel: modalModel,
loadingTitle: _t('dataset.create-map.loading', { tableName: tableName }),
errorTitle: _t('dataset.create-map.error', { tableName: tableName }),
runAction: function (opts) {
var newVisModel = new VisDefinitionModel({
name: self._visModel.get('name') + ' ' + _t('editor.map')
}, {
configModel: self._configModel
});
newVisModel.save({
source_visualization_id: self._visModel.get('id')
}, {
success: function (visModel) {
window.location = visModel.builderURL();
},
error: function (mdl, e) {
opts.error && opts.error(errorParser(e));
}
});
}
});
});
},
clean: function () {
this._destroyKeydownBinds();
CoreView.prototype.clean.call(this);
}
});
@@ -0,0 +1,24 @@
<div class="PreviewMap-canvas">
<div class="PreviewMap-map js-map"></div>
<div class="PreviewMap-info u-flex u-justifySpace u-alignCenter js-info">
<div class="PreviewMap-infoName u-flex u-alignCenter">
<% if (isSync && isOwner) { %>
<span class="SyncInfo-state SyncInfo-state--<%- syncState %> u-rSpace--m js-syncState"></span>
<% } %>
<h2 class="u-ellipsis CDB-Text CDB-Size-large u-rSpace--m is-light"><%- name %></h2>
<% if (isCustomQueryApplied) { %>
<span class="CDB-Tag CDB-Tag--opaque u-secondaryTextColor CDB-Text CDB-Size-medium u-upperCase"><%- _t('dataset.sql') %></span>
<% } %>
</div>
<div class="PreviewMap-infoActions">
<button class="u-upperCase CDB-Text CDB-Size-small u-lSpace--xl u-actionTextColor js-back">
<%- _t('dataset.preview-map.back') %>
</button>
<% if (canCreateMap) { %>
<button class="CDB-Button CDB-Button--primary u-upperCase u-lSpace--xl js-createMap">
<span class="CDB-Button-Text CDB-Text CDB-Size-small"><%- _t('dataset.create-map.title') %></span>
</button>
<% } %>
</div>
</div>
</div>