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,18 @@
<div class="u-flex u-alignCenter Modal-basemapContainer">
<h3 class="CDB-Text CDB-Size-large u-mainTextColor Modal-titleBasemap"><%- _t('components.modals.add-basemap.mapbox.insert') %></h3>
<div class="CDB-Text u-flex u-alignCenter ">
<label class="Metadata-label Metadata-label--big CDB-Text CDB-Size-small is-semibold u-upperCase u-ellipsis"><%- _t('components.modals.add-basemap.mapbox.enter') %></label>
<div class="Form-rowData Form-rowData--longer">
<input type="text" class="CDB-InputText CDB-Text js-url" value="<%- url %>" placeholder="<%- _t('components.modals.add-basemap.xyz.eg') %> https://api.mapbox.com/styles/v1/username/basemap/tiles/256/{z}/{x}/{y}@2x?access_token=access_token">
<div class="XYZPanel-error CDB-InfoTooltip CDB-InfoTooltip--left is-error CDB-Text CDB-Size-medium CDB-InfoTooltip-text js-error <%- lastErrorMsg ? 'is-visible' : '' %>"><%- lastErrorMsg %></div>
</div>
</div>
<div class="u-flex">
<label class="Metadata-label Metadata-label--big CDB-Text CDB-Size-small is-semibold u-upperCase u-ellipsis"></label>
<div class="Form-rowData Form-rowData--noMinHeight Form-rowData--longer">
<p class="CDB-Text CDB-Size-small Form-rowInfoText--block u-altTextColor">
Learn how to get your Mapbox Style URL <a href="https://www.mapbox.com/help/carto/" target="_blank">here</a>
</p>
</div>
</div>
</div>
@@ -0,0 +1,71 @@
var Backbone = require('backbone');
var _ = require('underscore');
var MapboxView = require('./mapbox-view');
var MapboxToTileLayerFactory = require('./mapbox-to-tile-layer-factory');
/**
* View model for Mapbox tab content.
*/
module.exports = Backbone.Model.extend({
defaults: {
name: 'mapbox',
label: 'Mapbox',
currentView: 'enterURL', //, validatingInputs, valid
lastErrorMsg: '', // set if fails to save
layer: undefined // will be set when valid
},
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',
layer: undefined
});
return new MapboxView({
model: this,
submitButton: this._submitButton,
modalFooter: this._modalFooter
});
},
hasAlreadyAddedLayer: function (userLayers) {
var urlTemplate = this.get('layer').get('urlTemplate');
return _.any(userLayers.isCustomCategory(), function (customLayer) {
return customLayer.get('urlTemplate') === urlTemplate;
});
},
validateInputs: function (url) {
this.set({
currentView: 'validatingInputs',
url: url
});
var self = this;
var mf = new MapboxToTileLayerFactory({
url: url
});
mf.createTileLayer({
success: function (tileLayer) {
self.set('layer', tileLayer);
self.trigger('saveBasemap');
},
error: function (errorMsg) {
self.set({
currentView: 'enterURL',
lastErrorMsg: errorMsg
});
}
});
}
});
@@ -0,0 +1,194 @@
/* global Image, location */
var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
/**
* Factory to create a CustomBaselayerModel from a given Integration URL for Mapbox.
*/
module.exports = Backbone.Model.extend({
defaults: {
url: ''
},
_MAPBOX: {
version: 4,
https: 'https://dnv9my2eseobd.cloudfront.net',
base: 'https://a.tiles.mapbox.com/'
},
/**
* @param {Object} callbacks
* success {Function} given a new TileLayer object
* error {Function} given an error explanation
*/
createTileLayer: function (callbacks) {
var val = this.get('url');
var url = this._lowerXYZ(val);
var type = 'json';
var subdomains = ['a', 'b', 'c'];
var mapbox_id;
// Detects the URL's type
if (url.indexOf('{x}') < 0 && url.indexOf('tiles.mapbox.com') !== -1) {
mapbox_id = this._getMapBoxMapID(url);
if (mapbox_id) {
type = 'mapbox_id';
url = mapbox_id;
}
} else if (url.indexOf('{x}') !== -1) {
type = 'xyz';
url = url.replace(/\{s\}/g, function () {
return subdomains[Math.floor(Math.random() * 3)];
})
.replace(/\{x\}/g, '0')
.replace(/\{y\}/g, '0')
.replace(/\{z\}/g, '0');
} else if (url && url.indexOf('http') < 0 && url.match(/(.*?)\.(.*)/) != null && url.match(/(.*?)\.(.*)/).length === 3) {
type = 'mapbox_id';
mapbox_id = val;
} else { // If not, check https
url = this._fixHTTPS(url);
}
var self = this;
var image;
if (type === 'mapbox') {
callbacks.success(this._newTileLayer({ tiles: [url] }));
} else if (type === 'xyz') {
image = new Image();
image.onload = function () {
callbacks.success(self._newTileLayer({
tiles: [self._lowerXYZ(val)]
}));
};
image.onerror = function () {
callbacks.error(self._errorToMsg());
};
image.src = url;
} else if (type === 'mapbox_id') {
var base_url = this._MAPBOX.base + 'v' + this._MAPBOX.version + '/' + mapbox_id;
var tile_url = base_url + '/{z}/{x}/{y}.png';
var json_url = base_url + '.json';
// JQuery has a faulty implementation of the getJSON method and doesn't return
// a 404, so we use a timeout. TODO: replace with CORS
var errorTimeout = setTimeout(function () {
callbacks.error(self._errorToMsg());
}, 5000);
$.ajax({
url: json_url,
success: function (data) {
clearTimeout(errorTimeout);
callbacks.success(self._newTileLayer({
tiles: [tile_url],
attribution: data.attribution,
minzoom: data.minzoom,
maxzoom: data.maxzoom,
name: data.name
}));
},
error: function (e) {
clearTimeout(errorTimeout);
callbacks.error(self._errorToMsg(e));
}
});
} else {
callbacks.error(this._errorToMsg());
}
},
_newTileLayer: function (data) {
// Check if the respond is an array
// In that case, get only the first
if (_.isArray(data) && _.size(data) > 0) {
data = _.first(data);
}
var url = data.tiles[0];
var attribution = data.attribution || null;
var layer = new CustomBaselayerModel({
urlTemplate: url,
attribution: attribution,
maxZoom: data.maxzoom || 21,
minZoom: data.minzoom || 0,
name: data.name || '',
category: 'Mapbox',
type: 'Tiled'
});
layer.set('className', layer._generateClassName(url));
return layer;
},
_errorToMsg: function (error) {
if (typeof error === 'object' || !error) {
if (error && error.status && error.status === 401) {
return _t('components.modals.add-basemap.mapbox.error');
} else {
return _t('components.modals.add-basemap.mapbox.invalid');
}
}
return error;
},
_lowerXYZ: function (url) {
return url.replace(/\{S\}/g, '{s}')
.replace(/\{X\}/g, '{x}')
.replace(/\{Y\}/g, '{y}')
.replace(/\{Z\}/g, '{z}');
},
// Extracts the Mapbox MapId from a Mapbox URL
_getMapBoxMapID: function (url) {
// http://d.tiles.mapbox.com/v3/{user}.{map}/3/4/3.png
// http://a.tiles.mapbox.com/v3/{user}.{map}/page.html
// http://a.tiles.mapbox.com/v4/{user}.{map}.*
var reg1 = /https?:\/\/[a-z]?\.?tiles\.mapbox.com\/v(\d)\/([^\/.]*)\.([^\/.]*)/;
// https://tiles.mapbox.com/{user}/edit/{map}?newmap&preset=Streets#3/0.00/-0.09
var reg2 = /https?:\/\/tiles\.mapbox\.com\/(.*?)\/edit\/(.*?)(\?|#)/;
var match = '';
// Check first expresion
match = url.match(reg1);
if (match && match[1] && match[2]) {
return match[2] + '.' + match[3];
}
// Check second expresion
match = url.match(reg2);
if (match && match[1] && match[2]) {
return match[1] + '.' + match[2];
}
},
/**
* return a https url if the current application is loaded from https
*/
_fixHTTPS: function (url, loc) {
loc = loc || location;
// fix the url to https or http
if (url.indexOf('https') !== 0 && loc.protocol === 'https:') {
// search for mapping
var i = url.indexOf('mapbox.com');
if (i !== -1) {
return this._MAPBOX.https + url.substr(i + 'mapbox.com'.length);
}
return url.replace(/http/, 'https');
}
return url;
}
});
@@ -0,0 +1,117 @@
var CoreView = require('backbone/core-view');
var ViewFactory = require('builder/components/view-factory');
var renderLoading = require('builder/components/loading/render-loading');
var enterUrl = require('./enter-url.tpl');
/**
* Represents the Mapbox tab content.
*/
module.exports = CoreView.extend({
events: {
'keydown': '_onKeyDown',
'keyup': '_onKeyUp'
},
initialize: 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._onClickBinded = this._onClickOK.bind(this);
this._bindSubmitButton();
this._initBinds();
},
render: function () {
this.clearSubViews();
var view;
switch (this.model.get('currentView')) {
case 'validatingInputs':
this._disableModalFooter(true);
view = ViewFactory.createByHTML(
renderLoading({
title: _t('components.modals.add-basemap.validating')
})
);
break;
case 'enterURL':
default:
this._disableModalFooter(false);
view = ViewFactory.createByHTML(
enterUrl({
url: this.model.get('url'),
lastErrorMsg: this.model.get('lastErrorMsg')
})
);
}
this.addView(view);
this.$el.append(view.render().el);
this._updateOkBtn();
this._onKeyUp();
return this;
},
_initBinds: function () {
this.model.bind('change', this.render, this);
},
_hasValues: function () {
return this._urlVal();
},
_urlVal: function () {
return this.$('.js-url').val();
},
_onClickOK: function (e) {
this.killEvent(e);
if (this._hasValues()) {
var url = this._urlVal();
this.model.validateInputs(url);
}
},
_disableModalFooter: function (disable) {
this._modalFooter.toggleClass('is-disabled', disable);
},
_updateOkBtn: function () {
this._submitButton.find('span').text(_t('components.modals.add-basemap.add-btn'));
},
_onKeyDown: function (e) {
e.stopPropagation();
this.$('.js-error').removeClass('is-visible');
},
_onKeyUp: function (e) {
e && e.stopPropagation();
this._submitButton.toggleClass('is-disabled', !this._hasValues());
},
_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);
}
});