Initial commit
This commit is contained in:
+18
@@ -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>
|
||||
+71
@@ -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
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
+194
@@ -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);
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user