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,16 @@
<div class="u-flex u-alignCenter Modal-basemapContainer">
<h3 class="CDB-Text CDB-Size-large u-mainTextColor Modal-titleBasemap"><%- _t('components.modals.add-basemap.wms.insert') %></h3>
<div class="CDB-Text u-flex u-alignCenter">
<label class="Metadata-label Metadata-label--auto CDB-Text CDB-Size-small is-semibold u-upperCase u-ellipsis"><%- _t('components.modals.add-basemap.xyz.enter') %></label>
<div class="Form-rowData Form-rowData--longer">
<input type="text" class="has-icon CDB-InputText CDB-Text js-url" value="" placeholder="<%- _t('components.modals.add-basemap.xyz.eg') %> http://openlayers.org/en/v3.5.0/examples/data/ogcsample.xml">
<i class="CDB-IconFont CDB-IconFont-dribbble Form-inputIcon js-idle"></i>
<i class="Spinner XYZPanel-inputIcon XYZPanel-inputIcon--loader Spinner--formIcon Form-inputIcon js-validating" style="display: none;"></i>
<div class="XYZPanel-error CDB-InfoTooltip CDB-InfoTooltip--left is-error CDB-Text CDB-Size-medium CDB-InfoTooltip-text js-error <%- (layersFetched && layers.length === 0) ? 'is-visible' : '' %>">
<% if (layersFetched && layers.length === 0) { %>
<%- _t('components.modals.add-basemap.wms.invalid') %>
<% } %>
</div>
</div>
</div>
</div>
@@ -0,0 +1,50 @@
var CoreView = require('backbone/core-view');
var template = require('./select-layer.tpl');
var WMSLayersView = require('./wms-layers-view');
/**
* Sub view, to select what layer to use as basemap.
*/
module.exports = CoreView.extend({
events: {
'click .js-back': '_onClickBack'
},
initialize: function (opts) {
if (!opts.customBaselayersCollection) throw new Error('customBaselayersCollection is required');
this._customBaselayersCollection = opts.customBaselayersCollection;
},
render: function () {
this.clearSubViews();
this.$el.html(
template({
searchQuery: this.model.get('searchQuery'),
layersFound: this.model.getLayers(),
layersAvailableCount: this.model.layersAvailableCount()
})
);
this._initViews();
return this;
},
_initViews: function () {
var wmsListView = new WMSLayersView({
model: this.model,
customBaselayersCollection: this._customBaselayersCollection
});
this.addView(wmsListView);
this.$('.js-layers').append(wmsListView.render().el);
},
_onClickBack: function (e) {
this.killEvent(e);
this.model.set('currentView', 'enterURL');
}
});
@@ -0,0 +1,42 @@
<div class="u-inner">
<div class="Filters WMSSSelectLayer-Filter is-relative">
<div class="Filters-inner">
<div class="Filters-row">
<div class="Filters-group">
<div class="Filters-typeItem Filters-typeItem--searchEnabler">
<button class="Filters-searchLink CDB-Text is-semibold u-upperCase CDB-Size-medium js-search-link">
<i class="Filters-searchLinkIcon CDB-IconFont CDB-IconFont-lens"></i><%- _t('components.modals.add-layer.navigation.search') %>
</button>
</div>
</div>
<div class="Filters-typeItem Filters-typeItem--searchField">
<form class="Filters-searchForm js-search-form" action="#">
<input class="Filters-searchInput CDB-Text CDB-Size-medium js-search-input" type="text" value="<%- searchQuery %>" placeholder="<%- _t('components.modals.add-basemap.wms.placeholder', { layersFoundCount: layersFound.length, layersFoundCountPluralize: _t('components.modals.add-basemap.wms.tables-pluralize', { smart_count: layersFound.length }), layersAvailableCount: layersAvailableCount, layersAvailableCountPluralize: _t('components.modals.add-basemap.wms.tables-pluralize', { smart_count: layersAvailableCount }) }) %>" />
<button type="button" class="Filters-cleanSearch js-clean-search u-actionTextColor">
<i class="CDB-IconFont CDB-IconFont-close"></i>
</button>
</form>
</div>
<span class="Filters-separator"></span>
</div>
</div>
</div>
<% if (searchQuery && layersFound.length == 0) { %>
<div class="IntermediateInfo">
<div class="LayoutIcon">
<i class="CDB-IconFont CDB-IconFont-defaultUser"></i>
</div>
<h4 class="CDB-Text CDB-Size-large u-mainTextColor u-bSpace u-secondaryTextColor u-tSpace-xl"><%- _t('components.modals.add-basemap.wms.oh-no') %></h4>
<p class="CDB-Text CDB-Size-medium u-altTextColor"><%- _t('components.modals.add-basemap.wms.unfortunately') %></p>
</div>
<% } else { %>
<div class="js-layers"></div>
<% } %>
</div>
<button class="NavButton Dialog-backBtn js-back">
<i class="CDB-IconFont CDB-IconFont-arrowPrev"></i>
</button>
@@ -0,0 +1,163 @@
var _ = require('underscore');
var Backbone = require('backbone');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
/**
* Model for an individual WMS/WMTS layer.
*/
module.exports = Backbone.Model.extend({
defaults: {
state: 'idle' // idle, saving, saveDone, saveFail
},
url: function () {
return this._wmsService.saveLayerURL({
title: this.get('title'),
name: this.get('name'),
layer: this.get('name'),
srs: this.get('srs'),
bounding_boxes: this.get('llbbox'),
type: this.get('type'), // wms/wmts
matrix_sets: this.get('matrix_sets')
});
},
sync: function (method, model, options) {
options = options || {};
options.url = this.url(method.toLowerCase());
options.dataType = 'jsonp';
options.attrs = '_';
method = 'READ';
return Backbone.sync.apply(this, arguments);
},
initialize: function (attrs, opts) {
if (!opts.wmsService) throw new Error('wmsService is required');
this._wmsService = opts.wmsService;
},
canSave: function (customBaselayersCollection) {
return !_.any(customBaselayersCollection.isCustomCategory(), function (customLayer) {
return customLayer.get('name') === this.get('title');
}, this);
},
createProxiedLayerOrCustomBaselayerModel: function () {
this.set('state', 'saving');
this._shouldBeProxied() ? this._createProxiedLayer() : this._newCustomBaselayerModel();
},
_shouldBeProxied: function () {
if (this.get('type') === 'wmts') {
var supportedMatrixSets = this._wmsService.supportedMatrixSets(this.get('matrix_sets') || []);
return supportedMatrixSets.length > 0;
}
return true;
},
_createProxiedLayer: function () {
var self = this;
this.save({}, {
success: function () {
var proxiedBaselayerModel;
try {
proxiedBaselayerModel = self._newProxiedBaselayerModel();
} catch (e) {
}
if (proxiedBaselayerModel) {
self._setCustomBaselayerModel(proxiedBaselayerModel);
} else {
self.set('state', 'saveFail');
}
},
error: function (e) {
self.set('state', 'saveFail');
}
});
},
_newCustomBaselayerModel: function () {
var customBaselayerModel = this._byCustomURL(this._xyzURLTemplate());
customBaselayerModel.set({
name: this.get('title') || this.get('name'),
attribution: this.get('attribution'),
bounding_boxes: this.get('llbbox')
});
this._setCustomBaselayerModel(customBaselayerModel);
return customBaselayerModel;
},
_newProxiedBaselayerModel: function () {
if (!this.get('mapproxy_id')) {
throw new Error('mapproxy_id must be set');
}
var url = this._wmsService.getProxyTilesURL() + '/' + this.get('mapproxy_id') + '/wmts/map/webmercator/{z}/{x}/{y}.png';
var proxiedBaselayerModel = this._generateCustomBaselayerModel(url);
proxiedBaselayerModel.set({
name: this.get('title') || this.get('name'),
attribution: this.get('attribution'),
proxy: true,
bounding_boxes: this.get('bounding_boxes')
});
return proxiedBaselayerModel;
},
_generateCustomBaselayerModel: function (url) {
var layer = new CustomBaselayerModel({
urlTemplate: url,
attribution: null,
maxZoom: 21,
minZoom: 0,
name: '',
category: 'WMS',
tms: false,
type: 'Tiled'
});
layer.set('className', layer._generateClassName(url));
return layer;
},
_byCustomURL: function (url) {
// Minimal test for "valid URL" w/o having to complicate it with regex
if (url && url.indexOf('/') === -1) throw new TypeError('invalid URL');
// Only lowercase the placeholder variables, since the URL may contain case-sensitive data (e.g. API keys and such)
url = url.replace(/\{S\}/g, '{s}')
.replace(/\{X\}/g, '{x}')
.replace(/\{Y\}/g, '{y}')
.replace(/\{Z\}/g, '{z}');
var layer = this._generateCustomBaselayerModel(url);
return layer;
},
_xyzURLTemplate: function () {
var urlTemplate = this.get('url_template') || '';
// Convert the proxy template variables to XYZ format, http://foo.com/bar/%%(z)s/%%(x)s/%%(y)s.png"
return urlTemplate.replace(/%%\((\w)\)s/g, '{$1}');
},
_setCustomBaselayerModel: function (customBaselayerModel) {
this.set({
state: 'saveDone',
customBaselayerModel: customBaselayerModel
});
}
});
@@ -0,0 +1,41 @@
var CoreView = require('backbone/core-view');
var template = require('./wms-layer.tpl');
/**
* View for an individual layer item.
*/
module.exports = CoreView.extend({
tagName: 'li',
className: 'List-row',
events: {
'click .js-add': '_onClickAdd'
},
initialize: function (opts) {
if (!opts.customBaselayersCollection) throw new Error('customBaselayersCollection is required');
this._customBaselayersCollection = opts.customBaselayersCollection;
},
render: function () {
this.$el.html(
template({
model: this.model,
canSave: this.model.canSave(this._customBaselayersCollection)
})
);
return this;
},
_onClickAdd: function (e) {
this.killEvent(e);
if (this.model.canSave(this._customBaselayersCollection)) {
this.model.createProxiedLayerOrCustomBaselayerModel();
}
}
});
@@ -0,0 +1,6 @@
<div class="List-rowItem">
<div class="DefaultTitle <%- canSave ? '': 'is-disabled' %>"><%- model.get('title') || model.get('name') %></div>
<button class="js-add Button Button--secondary Button--secondaryTransparentBkg <%- canSave ? '' : 'is-disabled' %>">
<span>Add this</span>
</button>
</div>
@@ -0,0 +1,46 @@
var _ = require('underscore');
var Backbone = require('backbone');
var WMSLayerModel = require('./wms-layer-model');
module.exports = Backbone.Collection.extend({
model: function (attrs, opts) {
var self = opts.collection;
return new WMSLayerModel(attrs, {
wmsService: self._wmsService
});
},
parse: function (r) {
var layers = [];
if (r.layers) {
layers = _.map(r.layers, function (layer) {
return _.extend({ type: r.type || 'wms' }, layer);
});
}
return layers;
},
sync: function (method, model, options) {
options = options || {};
options.url = this.url(method.toLowerCase());
options.dataType = 'jsonp';
method = 'READ';
return Backbone.sync.apply(this, arguments);
},
url: function () {
return this._wmsService.getFetchLayersURL();
},
initialize: function (models, opts) {
if (!opts.wmsService) throw new Error('wmsService is required');
this._wmsService = opts.wmsService;
}
});
@@ -0,0 +1,38 @@
var WMSLayerView = require('./wms-layer-view');
var CoreView = require('backbone/core-view');
/**
* Sub view, to select what layer to use as basemap.
*/
module.exports = CoreView.extend({
className: 'List',
tagName: 'ul',
initialize: function (opts) {
if (!opts.customBaselayersCollection) throw new Error('customBaselayersCollection is required');
if (!this.model) throw new Error('model is required');
this._customBaselayersCollection = opts.customBaselayersCollection;
},
render: function () {
this.clearSubViews();
this.$el.append.apply(this.$el, this._renderedLayers());
return this;
},
_renderedLayers: function () {
return this.model.getLayers().map(function (layer) {
var view = new WMSLayerView({
model: layer,
customBaselayersCollection: this._customBaselayersCollection
});
this.addView(view);
return view.render().el;
}, this);
}
});
@@ -0,0 +1,128 @@
var Backbone = require('backbone');
var _ = require('underscore');
var WMSView = require('./wms-view');
var WMSLayersCollection = require('./wms-layers-collection');
var WMSService = require('builder/data/wms-service');
/**
* View model for WMS/WMTS tab content.
*/
module.exports = Backbone.Model.extend({
defaults: {
name: 'wms',
label: 'WMS/WMTS',
currentView: 'enterURL', // [fetchingLayers, selectLayer, savingLayer]
layersFetched: false,
layer: undefined // will be set when selected
},
initialize: function (attrs, opts) {
if (!opts.customBaselayersCollection) throw new Error('customBaselayersCollection is required');
this._customBaselayersCollection = opts.customBaselayersCollection;
this.wmsService = new WMSService();
this.wmsLayersCollection = new WMSLayersCollection(null, {
wmsService: this.wmsService
});
this._initBinds();
},
_initBinds: function () {
this.wmsLayersCollection.bind('change:state', this._onLayerStateChange, this);
this.wmsLayersCollection.bind('reset', function () {
this.set({
currentView: this.wmsLayersCollection.length > 0 ? 'selectLayer' : 'enterURL',
layersFetched: true
});
this.trigger('layersFetched');
}, this);
},
createView: function (opts) {
if (!opts.submitButton) throw new Error('submitButton is required');
if (!opts.modalFooter) throw new Error('modalFooter is required');
this._submitButton = opts.submitButton;
this._modalFooter = opts.modalFooter;
this.set({
currentView: 'enterURL',
layersFetched: false
});
return new WMSView({
model: this,
customBaselayersCollection: this._customBaselayersCollection,
submitButton: this._submitButton,
modalFooter: this._modalFooter
});
},
fetchLayers: function (url) {
this.set('currentView', 'fetchingLayers');
this.wmsService.setUrl(url);
this.wmsLayersCollection.fetch({
reset: true
});
},
layersAvailableCount: function () {
return _.difference(
this.wmsLayersCollection.pluck('title'),
this._customBaselayersCollection.pluck('name')
).length;
},
get: function (name) {
if (name === 'layer') {
var customBaselayerModel = this.wmsLayersCollection.find(function (mdl) {
return mdl.get('state') === 'saveDone';
});
return customBaselayerModel && customBaselayerModel.get('customBaselayerModel');
} else {
return Backbone.Model.prototype.get.apply(this, arguments);
}
},
getLayers: function () {
if (this.get('searchQuery')) {
var regExp = new RegExp(this.get('searchQuery'), 'i');
return this.wmsLayersCollection.filter(function (layer) {
return layer.get('name').match(regExp);
}, this);
} else {
return this.wmsLayersCollection;
}
},
hasAlreadyAddedLayer: function () {
// Already added layers are disabled to be saved for each layer
return false;
},
_onLayerStateChange: function (mdl, newState) {
switch (newState) {
case 'saving':
this.set('currentView', 'savingLayer');
break;
case 'saveDone':
this.set('layer', mdl.get('customBaselayerModel'));
this.trigger('saveBasemap');
break;
case 'saveFail':
this.set('currentView', 'saveFail');
break;
default:
this.set('currentView', 'selectLayer');
}
}
});
@@ -0,0 +1,198 @@
var CoreView = require('backbone/core-view');
var _ = require('underscore');
var SelectLayerView = require('./select-layer-view');
var ViewFactory = require('builder/components/view-factory');
var ErrorView = require('builder/components/error/error-view');
var renderLoading = require('builder/components/loading/render-loading');
var enterUrl = require('./enter-url.tpl');
var $ = require('jquery');
/**
* Represents the WMS/WMTS tab category.
* Current state is defined by presence (or lack of) layers
*/
module.exports = CoreView.extend({
events: {
'keydown .js-search-input': '_onKeyDown',
'submit .js-search-form': 'killEvent',
'keydown .js-url': '_onKeydown',
'paste .js-url': '_onPaste',
'click .js-clean-search': '_onCleanSearchClick',
'click .js-search-link': '_submitSearch'
},
initialize: function (opts) {
if (!opts.customBaselayersCollection) throw new Error('customBaselayersCollection is required');
if (!opts.submitButton) throw new Error('submitButton is required');
if (!opts.modalFooter) throw new Error('modalFooter is required');
this._customBaselayersCollection = opts.customBaselayersCollection;
this._submitButton = opts.submitButton;
this._modalFooter = opts.modalFooter;
this._debouncedUpdate = _.debounce(this._update.bind(this), 150);
this._onClickBinded = this._onClickOK.bind(this);
this._bindSubmitButton();
this._initBinds();
},
render: function () {
this.clearSubViews();
this._updateOkBtn();
this._disableOkBtn(true);
var view;
switch (this.model.get('currentView')) {
case 'savingLayer':
this._disableModalFooter(true);
view = ViewFactory.createByHTML(
renderLoading({
title: _t('components.modals.add-basemap.saving')
})
);
break;
case 'selectLayer':
this._disableModalFooter(true);
view = new SelectLayerView({
model: this.model,
customBaselayersCollection: this._customBaselayersCollection
});
break;
case 'saveFail':
this._disableModalFooter(true);
view = new ErrorView({
title: _t('components.modals.add-basemap.add-basemap-error')
});
break;
case 'fetchingLayers':
this._disableModalFooter(true);
view = ViewFactory.createByHTML(
renderLoading({
title: _t('components.modals.add-basemap.fetching')
})
);
break;
case 'enterURL':
default:
this._disableModalFooter(false);
view = ViewFactory.createByHTML(
enterUrl({
layersFetched: this.model.get('layersFetched'),
layers: this.model.wmsLayersCollection
})
);
break;
}
this.addView(view);
this.$el.append(view.render().el);
this.$('.js-search-input').focus();
return this;
},
_showCleanSearchButton: function () {
this.$('.js-clean-search').show();
},
_hideCleanSearchButton: function () {
this.$('.js-clean-search').hide();
},
_initBinds: function () {
this.model.bind('change', this.render, this);
this.model.bind('change', this._onChangeSearchQuery, this);
this.model.bind('layersFetched', this.render, this);
},
_onKeydown: function (e) {
e.stopPropagation();
this._debouncedUpdate();
},
_onPaste: function (e) {
e.stopPropagation();
this._debouncedUpdate();
},
_update: function (e) {
this._disableOkBtn(!this.$('.js-url').val());
this.$('.js-error').removeClass('is-visible'); // resets error state when changed
},
_disableOkBtn: function (disable) {
this._submitButton.toggleClass('is-disabled', disable);
},
_onKeyDown: function (e) {
var enterPressed = (e.keyCode === $.ui.keyCode.ENTER);
if (enterPressed) {
this.killEvent(e);
this._submitSearch();
}
},
_submitSearch: function (e) {
this.killEvent(e);
this.model.set('searchQuery', this.$('.js-search-input').val());
},
_onChangeSearchQuery: function () {
var searchQuery = this.model.get('searchQuery');
if (!searchQuery) {
this._hideCleanSearchButton();
}
},
_onCleanSearchClick: function (e) {
this.killEvent(e);
this.model.set('searchQuery', '');
},
_onClickOK: function (e) {
this.killEvent(e);
var url = this.$('.js-url').val();
if (url) {
this.model.fetchLayers(url);
}
},
_disableModalFooter: function (disable) {
this._modalFooter.toggleClass('is-disabled', disable);
},
_updateOkBtn: function () {
this._submitButton.find('span').text(_t('components.modals.add-basemap.get-layers'));
},
_bindSubmitButton: function () {
this._submitButton.on('click', this._onClickBinded);
},
_unBindSubmitButton: function () {
this._submitButton.off('click', this._onClickBinded);
},
clean: function () {
this._unBindSubmitButton();
CoreView.prototype.clean.call(this);
}
});