Merge remote-tracking branch 'upstream/master' into fix/remove-componentWillUpdate

Conflicts:
	src/components/map/MapboxGlMap.jsx
	src/components/modals/ExportModal.jsx
This commit is contained in:
orangemug 2018-09-10 14:46:52 +01:00
commit 26ff9f63bb
43 changed files with 277 additions and 502 deletions

View file

@ -5,9 +5,9 @@
"test": {
"plugins": [
["istanbul", {
exclude: ["node_modules/**", "test/**"]
"exclude": ["node_modules/**", "test/**"]
}]
]
}
}
}
}

View file

@ -14,25 +14,6 @@ var OUTPATH = artifacts.pathSync("/build");
module.exports = {
entry: {
app: './src/index.jsx',
vendor: [
'file-saver',
'mapbox-gl/dist/mapbox-gl.js',
"lodash.clonedeep",
"lodash.throttle",
'color',
'react',
"react-dom",
"react-color",
"react-file-reader-input",
"react-collapse",
"react-height",
"react-icon-base",
"react-motion",
"react-sortable-hoc",
"request",
//TODO: Icons raise multi vendor errors?
//"react-icons",
]
},
output: {
path: OUTPATH,
@ -55,7 +36,6 @@ module.exports = {
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[chunkhash].vendor.js' }),
new WebpackCleanupPlugin(),
new webpack.DefinePlugin({
'process.env': {

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "maputnik",
"version": "1.4.0",
"version": "1.5.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "maputnik",
"version": "1.4.0",
"version": "1.5.0",
"description": "A MapboxGL visual style editor",
"main": "''",
"scripts": {
@ -10,8 +10,7 @@
"test-watch": "cross-env NODE_ENV=test wdio config/wdio.conf.js --watch",
"start": "webpack-dev-server --progress --profile --colors --config config/webpack.config.js",
"lint": "eslint --ext js --ext jsx {src,test}",
"lint-styles": "stylelint 'src/styles/*.scss'",
"nsp": "nsp check --reporter summary"
"lint-styles": "stylelint 'src/styles/*.scss'"
},
"repository": {
"type": "git",
@ -27,7 +26,6 @@
"codemirror": "^5.37.0",
"color": "^3.0.0",
"file-saver": "^1.3.8",
"github-api": "^3.0.0",
"jsonlint": "github:josdejong/jsonlint#85a19d7",
"lodash.capitalize": "^4.2.1",
"lodash.clamp": "^4.0.3",
@ -37,28 +35,24 @@
"mapbox-gl": "^0.47.0",
"mapbox-gl-inspect": "^1.3.1",
"maputnik-design": "github:maputnik/design",
"mousetrap": "^1.6.1",
"ol-mapbox-style": "^2.10.1",
"ol": "^4.6.5",
"prop-types": "^15.6.0",
"react": "^16.3.2",
"react-addons-pure-render-mixin": "^15.6.2",
"react-aria-menubutton": "^5.1.1",
"react-aria-modal": "^2.12.1",
"react-autobind": "^1.0.6",
"react-autocomplete": "^1.7.2",
"react-codemirror2": "^4.2.1",
"react-collapse": "^4.0.3",
"react-color": "^2.14.1",
"react-copy-to-clipboard": "^5.0.1",
"react-dom": "^16.3.2",
"react-file-reader-input": "^1.1.4",
"react-height": "^3.0.0",
"react-icon-base": "^2.1.1",
"react-icons": "^2.2.7",
"react-motion": "^0.5.2",
"react-sortable-hoc": "^0.6.8",
"reconnecting-websocket": "^3.2.2",
"request": "^2.85.0",
"url": "^0.11.0"
},
"jshintConfig": {
@ -117,7 +111,6 @@
"babel-preset-react": "^6.24.1",
"babel-register": "^6.26.0",
"babel-runtime": "^6.26.0",
"base64-loader": "^1.0.0",
"copy-webpack-plugin": "^4.5.1",
"cors": "^2.8.4",
"cross-env": "^5.1.4",
@ -135,7 +128,7 @@
"mkdirp": "^0.5.1",
"mocha": "^5.1.1",
"node-sass": "^4.9.0",
"nsp": "^3.1.0",
"raw-loader": "^0.5.1",
"react-hot-loader": "^3.1.1",
"sass-loader": "^7.0.1",
"selenium-standalone": "^6.14.0",
@ -147,7 +140,6 @@
"uglifyjs-webpack-plugin": "^1.2.4",
"uuid": "^3.1.0",
"wdio-mocha-framework": "^0.5.13",
"wdio-phantomjs-service": "^0.2.2",
"wdio-selenium-standalone-service": "0.0.10",
"wdio-spec-reporter": "^0.1.2",
"webdriverio": "^4.12.0",

View file

@ -1,12 +1,11 @@
import autoBind from 'react-autobind';
import React from 'react'
import Mousetrap from 'mousetrap'
import cloneDeep from 'lodash.clonedeep'
import clamp from 'lodash.clamp'
import {arrayMove} from 'react-sortable-hoc'
import url from 'url'
import MapboxGlMap from './map/MapboxGlMap'
import OpenLayers3Map from './map/OpenLayers3Map'
import LayerList from './layers/LayerList'
import LayerEditor from './layers/LayerEditor'
import Toolbar from './Toolbar'
@ -25,7 +24,7 @@ import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
import style from '../libs/style'
import { initialStyleUrl, loadStyleUrl } from '../libs/urlopen'
import { undoMessages, redoMessages } from '../libs/diffmessage'
import { loadDefaultStyle, StyleStore } from '../libs/stylestore'
import { StyleStore } from '../libs/stylestore'
import { ApiStyleStore } from '../libs/apistore'
import { RevisionStore } from '../libs/revisions'
import LayerWatcher from '../libs/layerwatcher'
@ -54,6 +53,8 @@ function updateRootSpec(spec, fieldName, newValues) {
export default class App extends React.Component {
constructor(props) {
super(props)
autoBind(this);
this.revisionStore = new RevisionStore()
this.styleStore = new ApiStyleStore({
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, false)
@ -176,7 +177,8 @@ export default class App extends React.Component {
survey: localStorage.hasOwnProperty('survey') ? false : true
},
mapOptions: {
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries")
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes")
},
mapFilter: queryObj["color-blindness-emulation"],
}
@ -186,14 +188,31 @@ export default class App extends React.Component {
})
}
handleKeyPress(e) {
if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
if(e.metaKey && e.shiftKey && e.keyCode === 90) {
this.onRedo(e);
}
else if(e.metaKey && e.keyCode === 90) {
this.onUndo(e);
}
}
else {
if(e.ctrlKey && e.keyCode === 90) {
this.onUndo(e);
}
else if(e.ctrlKey && e.keyCode === 89) {
this.onRedo(e);
}
}
}
componentDidMount() {
Mousetrap.bind(['mod+z'], this.onUndo.bind(this));
Mousetrap.bind(['mod+y', 'mod+shift+z'], this.onRedo.bind(this));
window.addEventListener("keydown", this.handleKeyPress);
}
componentWillUnmount() {
Mousetrap.unbind(['mod+z'], this.onUndo.bind(this));
Mousetrap.unbind(['mod+y', 'mod+shift+z'], this.onRedo.bind(this));
window.removeEventListener("keydown", this.handleKeyPress);
}
saveStyle(snapshotStyle) {
@ -216,7 +235,7 @@ export default class App extends React.Component {
})
}
onStyleChanged(newStyle, save=true) {
onStyleChanged = (newStyle, save=true) => {
const errors = styleSpec.validate(newStyle, styleSpec.latest)
if(errors.length === 0) {
@ -243,7 +262,7 @@ export default class App extends React.Component {
this.fetchSources();
}
onUndo() {
onUndo = () => {
const activeStyle = this.revisionStore.undo()
const messages = undoMessages(this.state.mapStyle, activeStyle)
this.saveStyle(activeStyle)
@ -253,7 +272,7 @@ export default class App extends React.Component {
})
}
onRedo() {
onRedo = () => {
const activeStyle = this.revisionStore.redo()
const messages = redoMessages(this.state.mapStyle, activeStyle)
this.saveStyle(activeStyle)
@ -263,7 +282,7 @@ export default class App extends React.Component {
})
}
onMoveLayer(move) {
onMoveLayer = (move) => {
let { oldIndex, newIndex } = move;
let layers = this.state.mapStyle.layers;
oldIndex = clamp(oldIndex, 0, layers.length-1);
@ -281,7 +300,7 @@ export default class App extends React.Component {
this.onLayersChange(layers);
}
onLayersChange(changedLayers) {
onLayersChange = (changedLayers) => {
const changedStyle = {
...this.state.mapStyle,
layers: changedLayers
@ -289,7 +308,7 @@ export default class App extends React.Component {
this.onStyleChanged(changedStyle)
}
onLayerDestroy(layerId) {
onLayerDestroy = (layerId) => {
let layers = this.state.mapStyle.layers;
const remainingLayers = layers.slice(0);
const idx = style.indexOfLayer(remainingLayers, layerId)
@ -297,7 +316,7 @@ export default class App extends React.Component {
this.onLayersChange(remainingLayers);
}
onLayerCopy(layerId) {
onLayerCopy = (layerId) => {
let layers = this.state.mapStyle.layers;
const changedLayers = layers.slice(0)
const idx = style.indexOfLayer(changedLayers, layerId)
@ -308,7 +327,7 @@ export default class App extends React.Component {
this.onLayersChange(changedLayers)
}
onLayerVisibilityToggle(layerId) {
onLayerVisibilityToggle = (layerId) => {
let layers = this.state.mapStyle.layers;
const changedLayers = layers.slice(0)
const idx = style.indexOfLayer(changedLayers, layerId)
@ -323,7 +342,7 @@ export default class App extends React.Component {
}
onLayerIdChange(oldId, newId) {
onLayerIdChange = (oldId, newId) => {
const changedLayers = this.state.mapStyle.layers.slice(0)
const idx = style.indexOfLayer(changedLayers, oldId)
@ -335,7 +354,7 @@ export default class App extends React.Component {
this.onLayersChange(changedLayers)
}
onLayerChanged(layer) {
onLayerChanged = (layer) => {
const changedLayers = this.state.mapStyle.layers.slice(0)
const idx = style.indexOfLayer(changedLayers, layer.id)
changedLayers[idx] = layer
@ -343,7 +362,7 @@ export default class App extends React.Component {
this.onLayersChange(changedLayers)
}
changeInspectMode() {
changeInspectMode = () => {
this.setState({
inspectModeEnabled: !this.state.inspectModeEnabled
})
@ -370,7 +389,9 @@ export default class App extends React.Component {
console.warn("Failed to normalizeSourceURL: ", err);
}
fetch(url)
fetch(url, {
mode: 'cors',
})
.then((response) => {
return response.json();
})
@ -422,12 +443,12 @@ export default class App extends React.Component {
// Check if OL3 code has been loaded?
if(renderer === 'ol3') {
mapElement = <OpenLayers3Map {...mapProps} />
mapElement = <div>TODO</div>
} else {
mapElement = <MapboxGlMap {...mapProps}
inspectModeEnabled={this.state.inspectModeEnabled}
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
onLayerSelect={this.onLayerSelect.bind(this)} />
onLayerSelect={this.onLayerSelect} />
}
const elementStyle = {};
@ -440,7 +461,7 @@ export default class App extends React.Component {
</div>
}
onLayerSelect(layerId) {
onLayerSelect = (layerId) => {
const idx = style.indexOfLayer(this.state.mapStyle.layers, layerId)
this.setState({ selectedLayerIndex: idx })
}
@ -467,19 +488,19 @@ export default class App extends React.Component {
mapStyle={this.state.mapStyle}
inspectModeEnabled={this.state.inspectModeEnabled}
sources={this.state.sources}
onStyleChanged={this.onStyleChanged.bind(this)}
onStyleOpen={this.onStyleChanged.bind(this)}
onInspectModeToggle={this.changeInspectMode.bind(this)}
onStyleChanged={this.onStyleChanged}
onStyleOpen={this.onStyleChanged}
onInspectModeToggle={this.changeInspectMode}
onToggleModal={this.toggleModal.bind(this)}
/>
const layerList = <LayerList
onMoveLayer={this.onMoveLayer.bind(this)}
onLayerDestroy={this.onLayerDestroy.bind(this)}
onLayerCopy={this.onLayerCopy.bind(this)}
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
onLayersChange={this.onLayersChange.bind(this)}
onLayerSelect={this.onLayerSelect.bind(this)}
onMoveLayer={this.onMoveLayer}
onLayerDestroy={this.onLayerDestroy}
onLayerCopy={this.onLayerCopy}
onLayerVisibilityToggle={this.onLayerVisibilityToggle}
onLayersChange={this.onLayersChange}
onLayerSelect={this.onLayerSelect}
selectedLayerIndex={this.state.selectedLayerIndex}
layers={layers}
sources={this.state.sources}
@ -493,12 +514,12 @@ export default class App extends React.Component {
sources={this.state.sources}
vectorLayers={this.state.vectorLayers}
spec={this.state.spec}
onMoveLayer={this.onMoveLayer.bind(this)}
onLayerChanged={this.onLayerChanged.bind(this)}
onLayerDestroy={this.onLayerDestroy.bind(this)}
onLayerCopy={this.onLayerCopy.bind(this)}
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
onLayerIdChange={this.onLayerIdChange.bind(this)}
onMoveLayer={this.onMoveLayer}
onLayerChanged={this.onLayerChanged}
onLayerDestroy={this.onLayerDestroy}
onLayerCopy={this.onLayerCopy}
onLayerVisibilityToggle={this.onLayerVisibilityToggle}
onLayerIdChange={this.onLayerIdChange}
/> : null
const bottomPanel = (this.state.errors.length + this.state.infos.length) > 0 ? <MessagePanel
@ -514,24 +535,24 @@ export default class App extends React.Component {
/>
<SettingsModal
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged.bind(this)}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.settings}
onOpenToggle={this.toggleModal.bind(this, 'settings')}
/>
<ExportModal
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged.bind(this)}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.export}
onOpenToggle={this.toggleModal.bind(this, 'export')}
/>
<OpenModal
isOpen={this.state.isOpen.open}
onStyleOpen={this.onStyleChanged.bind(this)}
onStyleOpen={this.onStyleChanged}
onOpenToggle={this.toggleModal.bind(this, 'open')}
/>
<SourcesModal
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged.bind(this)}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.sources}
onOpenToggle={this.toggleModal.bind(this, 'sources')}
/>

View file

@ -1,19 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
import FileReaderInput from 'react-file-reader-input'
import classnames from 'classnames'
import MdFileDownload from 'react-icons/lib/md/file-download'
import MdFileUpload from 'react-icons/lib/md/file-upload'
import OpenIcon from 'react-icons/lib/md/open-in-browser'
import SettingsIcon from 'react-icons/lib/md/settings'
import MdInfo from 'react-icons/lib/md/info'
import SourcesIcon from 'react-icons/lib/md/layers'
import MdSave from 'react-icons/lib/md/save'
import MdStyle from 'react-icons/lib/md/style'
import MdMap from 'react-icons/lib/md/map'
import MdInsertEmoticon from 'react-icons/lib/md/insert-emoticon'
import MdFontDownload from 'react-icons/lib/md/font-download'
import HelpIcon from 'react-icons/lib/md/help-outline'
import InspectionIcon from 'react-icons/lib/md/find-in-page'
import SurveyIcon from 'react-icons/lib/md/assignment-turned-in'
@ -21,7 +13,6 @@ import SurveyIcon from 'react-icons/lib/md/assignment-turned-in'
import logoImage from 'maputnik-design/logos/logo-color.svg'
import pkgJson from '../../package.json'
import style from '../libs/style'
class IconText extends React.Component {
static propTypes = {
@ -107,16 +98,13 @@ export default class Toolbar extends React.Component {
onToggleModal: PropTypes.func,
}
constructor(props) {
super(props)
this.state = {
isOpen: {
settings: false,
sources: false,
open: false,
add: false,
export: false,
}
state = {
isOpen: {
settings: false,
sources: false,
open: false,
add: false,
export: false,
}
}

View file

@ -19,17 +19,14 @@ class ColorField extends React.Component {
default: PropTypes.string,
}
constructor(props) {
super(props)
this.state = {
pickerOpened: false,
}
state = {
pickerOpened: false
}
//TODO: I much rather would do this with absolute positioning
//but I am too stupid to get it to work together with fixed position
//and scrollbars so I have to fallback to JavaScript
calcPickerOffset() {
calcPickerOffset = () => {
const elem = this.colorInput
if(elem) {
const pos = elem.getBoundingClientRect()
@ -45,7 +42,7 @@ class ColorField extends React.Component {
}
}
togglePicker() {
togglePicker = () => {
this.setState({ pickerOpened: !this.state.pickerOpened })
}
@ -85,7 +82,7 @@ class ColorField extends React.Component {
/>
<div
className="maputnik-color-picker-offset"
onClick={this.togglePicker.bind(this)}
onClick={this.togglePicker}
style={{
zIndex: -1,
position: 'fixed',
@ -108,7 +105,7 @@ class ColorField extends React.Component {
spellCheck="false"
className="maputnik-color"
ref={(input) => this.colorInput = input}
onClick={this.togglePicker.bind(this)}
onClick={this.togglePicker}
style={this.props.style}
name={this.props.name}
placeholder={this.props.default}

View file

@ -17,7 +17,7 @@ export default class DocLabel extends React.Component {
<div className="maputnik-doc-popup">
{this.props.doc}
</div>
</div >
</div>
</label>
}
}

View file

@ -32,7 +32,7 @@ export default class FunctionSpecProperty extends React.Component {
]),
}
addStop() {
addStop = () => {
const stops = this.props.value.stops.slice(0)
const lastStop = stops[stops.length - 1]
if (typeof lastStop[0] === "object") {
@ -53,7 +53,7 @@ export default class FunctionSpecProperty extends React.Component {
this.props.onChange(this.props.fieldName, changedValue)
}
deleteStop(stopIdx) {
deleteStop = (stopIdx) => {
const stops = this.props.value.stops.slice(0)
stops.splice(stopIdx, 1)
@ -69,7 +69,7 @@ export default class FunctionSpecProperty extends React.Component {
this.props.onChange(this.props.fieldName, changedValue)
}
makeZoomFunction() {
makeZoomFunction = () => {
const zoomFunc = {
stops: [
[6, this.props.value],
@ -79,7 +79,7 @@ export default class FunctionSpecProperty extends React.Component {
this.props.onChange(this.props.fieldName, zoomFunc)
}
makeDataFunction() {
makeDataFunction = () => {
const dataFunc = {
property: "",
type: "categorical",
@ -102,8 +102,8 @@ export default class FunctionSpecProperty extends React.Component {
fieldName={this.props.fieldName}
fieldSpec={this.props.fieldSpec}
value={this.props.value}
onDeleteStop={this.deleteStop.bind(this)}
onAddStop={this.addStop.bind(this)}
onDeleteStop={this.deleteStop}
onAddStop={this.addStop}
/>
)
}
@ -114,8 +114,8 @@ export default class FunctionSpecProperty extends React.Component {
fieldName={this.props.fieldName}
fieldSpec={this.props.fieldSpec}
value={this.props.value}
onDeleteStop={this.deleteStop.bind(this)}
onAddStop={this.addStop.bind(this)}
onDeleteStop={this.deleteStop}
onAddStop={this.addStop}
/>
)
}
@ -126,8 +126,8 @@ export default class FunctionSpecProperty extends React.Component {
fieldName={this.props.fieldName}
fieldSpec={this.props.fieldSpec}
value={this.props.value}
onZoomClick={this.makeZoomFunction.bind(this)}
onDataClick={this.makeDataFunction.bind(this)}
onZoomClick={this.makeZoomFunction}
onDataClick={this.makeDataFunction}
/>
)
}

View file

@ -42,7 +42,7 @@ export default class PropertyGroup extends React.Component {
spec: PropTypes.object.isRequired,
}
onPropertyChange(property, newValue) {
onPropertyChange = (property, newValue) => {
const group = getGroupName(this.props.spec, this.props.layer.type, property)
this.props.onChange(group , property, newValue)
}
@ -56,7 +56,7 @@ export default class PropertyGroup extends React.Component {
const fieldValue = fieldName in paint ? paint[fieldName] : layout[fieldName]
return <FunctionSpecField
onChange={this.onPropertyChange.bind(this)}
onChange={this.onPropertyChange}
key={fieldName}
fieldName={fieldName}
value={fieldValue === undefined ? fieldSpec.default : fieldValue}

View file

@ -1,6 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import color from 'color'
import ColorField from './ColorField'
import NumberInput from '../inputs/NumberInput'

View file

@ -53,12 +53,8 @@ export default class ZoomProperty extends React.Component {
]),
}
constructor() {
super()
this.state = {
refs: {}
}
state = {
refs: {}
}
componentDidMount() {

View file

@ -9,9 +9,6 @@ import SingleFilterEditor from './SingleFilterEditor'
import FilterEditorBlock from './FilterEditorBlock'
import Button from '../Button'
import DeleteIcon from 'react-icons/lib/md/delete'
import AddIcon from 'react-icons/lib/fa/plus'
function hasCombiningFilter(filter) {
return combiningFilterOps.indexOf(filter[0]) >= 0
}
@ -60,7 +57,7 @@ export default class CombiningFilterEditor extends React.Component {
this.props.onChange(newFilter)
}
addFilterItem() {
addFilterItem = () => {
const newFilterItem = this.combiningFilter().slice(0)
newFilterItem.push(['==', 'name', ''])
this.props.onChange(newFilterItem)
@ -105,7 +102,7 @@ export default class CombiningFilterEditor extends React.Component {
<Button
data-wd-key="layer-filter-button"
className="maputnik-add-filter"
onClick={this.addFilterItem.bind(this)}>
onClick={this.addFilterItem}>
Add filter
</Button>
</div>

View file

@ -14,18 +14,15 @@ class AutocompleteInput extends React.Component {
keepMenuWithinWindowBounds: PropTypes.bool
}
state = {
maxHeight: MAX_HEIGHT
}
static defaultProps = {
onChange: () => {},
options: [],
}
constructor(props) {
super(props);
this.state = {
maxHeight: MAX_HEIGHT
};
}
calcMaxHeight() {
if(this.props.keepMenuWithinWindowBounds) {
const maxHeight = window.innerHeight - this.autocompleteMenuEl.getBoundingClientRect().top;
@ -38,6 +35,7 @@ class AutocompleteInput extends React.Component {
}
}
}
componentDidMount() {
this.calcMaxHeight();
}

View file

@ -27,14 +27,13 @@ class DynamicArrayInput extends React.Component {
return this.props.value || this.props.default || []
}
addValue() {
addValue = () => {
const values = this.values.slice(0)
if (this.props.type === 'number') {
values.push(0)
} else {
values.push("")
}
this.props.onChange(values)
}
@ -77,7 +76,7 @@ class DynamicArrayInput extends React.Component {
{inputs}
<Button
className="maputnik-array-add-value"
onClick={this.addValue.bind(this)}
onClick={this.addValue}
>
Add value
</Button>

View file

@ -51,7 +51,7 @@ class NumberInput extends React.Component {
return true
}
resetValue() {
resetValue = () => {
// Reset explicitly to default value if value has been cleared
if(this.state.value === "") {
return this.changeValue(this.props.default)
@ -74,7 +74,7 @@ class NumberInput extends React.Component {
placeholder={this.props.default}
value={this.state.value}
onChange={e => this.changeValue(e.target.value)}
onBlur={this.resetValue.bind(this)}
onBlur={this.resetValue}
/>
}
}

View file

@ -16,9 +16,6 @@ import LayerSourceLayerBlock from './LayerSourceLayerBlock'
import MoreVertIcon from 'react-icons/lib/md/more-vert'
import InputBlock from '../inputs/InputBlock'
import MultiButtonInput from '../inputs/MultiButtonInput'
import { changeType, changeProperty } from '../../libs/layer'
import layout from '../../config/layout.json'
@ -36,7 +33,7 @@ function layoutGroups(layerType) {
title: 'JSON Editor',
type: 'jsoneditor'
}
return [layerGroup, filterGroup].concat(layout[layerType].groups).concat([editorGroup])
return [layerGroup, filterGroup].concat(layout[layerType].groups).concat([editorGroup])
}
/** Layer editor supporting multiple types of layers. */

View file

@ -2,13 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import Button from '../Button'
import LayerListGroup from './LayerListGroup'
import LayerListItem from './LayerListItem'
import AddIcon from 'react-icons/lib/md/add-circle-outline'
import AddModal from '../modals/AddModal'
import style from '../../libs/style.js'
import {SortableContainer, SortableHandle} from 'react-sortable-hoc';
const layerListPropTypes = {
@ -45,15 +42,12 @@ class LayerListContainer extends React.Component {
onLayerSelect: () => {},
}
constructor(props) {
super(props)
this.state = {
collapsedGroups: {},
areAllGroupsExpanded: false,
isOpen: {
add: false,
}
}
state = {
collapsedGroups: {},
areAllGroupsExpanded: false,
isOpen: {
add: false,
}
}
toggleModal(modalName) {
@ -65,7 +59,7 @@ class LayerListContainer extends React.Component {
})
}
toggleLayers() {
toggleLayers = () => {
let idx=0
let newGroups=[]
@ -179,7 +173,7 @@ class LayerListContainer extends React.Component {
<div className="maputnik-multibutton">
<button
id="skip-menu"
onClick={this.toggleLayers.bind(this)}
onClick={this.toggleLayers}
className="maputnik-button">
{this.state.areAllGroupsExpanded === true ? "Collapse" : "Expand"}
</button>

View file

@ -1,6 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import Color from 'color'
import classnames from 'classnames'
import CopyIcon from 'react-icons/lib/md/content-copy'
@ -9,7 +8,6 @@ import VisibilityOffIcon from 'react-icons/lib/md/visibility-off'
import DeleteIcon from 'react-icons/lib/md/delete'
import LayerIcon from '../icons/LayerIcon'
import LayerEditor from './LayerEditor'
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
@SortableHandle

View file

@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import AutocompleteInput from '../inputs/AutocompleteInput'
class LayerSourceBlock extends React.Component {

View file

@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import AutocompleteInput from '../inputs/AutocompleteInput'
class LayerSourceLayer extends React.Component {

View file

@ -1,7 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import LayerIcon from '../icons/LayerIcon'
function groupFeaturesBySourceLayer(features) {

View file

@ -5,7 +5,6 @@ import MapboxGl from 'mapbox-gl'
import MapboxInspect from 'mapbox-gl-inspect'
import FeatureLayerPopup from './FeatureLayerPopup'
import FeaturePropertyPopup from './FeaturePropertyPopup'
import style from '../../libs/style.js'
import tokens from '../../config/tokens.json'
import colors from 'mapbox-gl-inspect/lib/colors'
import Color from 'color'
@ -15,6 +14,9 @@ import 'mapbox-gl/dist/mapbox-gl.css'
import '../../mapboxgl.css'
import '../../libs/mapbox-rtl'
const IS_SUPPORTED = MapboxGl.supported();
function renderPropertyPopup(features) {
var mountNode = document.createElement('div');
ReactDOM.render(<FeaturePropertyPopup features={features} />, mountNode)
@ -82,6 +84,8 @@ export default class MapboxGlMap extends React.Component {
}
updateMapFromProps(props) {
if(!IS_SUPPORTED) return;
if(!this.state.map) return
const metadata = props.mapStyle.metadata || {}
MapboxGl.accessToken = metadata['maputnik:mapbox_access_token'] || tokens.mapbox
@ -94,6 +98,8 @@ export default class MapboxGlMap extends React.Component {
}
componentDidUpdate(prevProps) {
if(!IS_SUPPORTED) return;
const map = this.state.map;
this.updateMapFromProps(this.props);
@ -106,9 +112,12 @@ export default class MapboxGlMap extends React.Component {
}
map.showTileBoundaries = this.props.options.showTileBoundaries;
map.showCollisionBoxes = this.props.options.showCollisionBoxes;
}
componentDidMount() {
if(!IS_SUPPORTED) return;
const mapOpts = {
...this.props.options,
container: this.container,
@ -119,6 +128,7 @@ export default class MapboxGlMap extends React.Component {
const map = new MapboxGl.Map(mapOpts);
map.showTileBoundaries = mapOpts.showTileBoundaries;
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
const zoom = new ZoomControl;
map.addControl(zoom, 'top-right');
@ -164,9 +174,20 @@ export default class MapboxGlMap extends React.Component {
}
render() {
return <div
className="maputnik-map"
ref={x => this.container = x}
></div>
if(IS_SUPPORTED) {
return <div
className="maputnik-map"
ref={x => this.container = x}
></div>
}
else {
return <div
className="maputnik-map maputnik-map--error"
>
<div className="maputnik-map__error-message">
Error: Cannot load MapboxGL, WebGL is either unsupported or disabled
</div>
</div>
}
}
}

View file

@ -1,7 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import style from '../../libs/style.js'
import isEqual from 'lodash.isequal'
import { loadJSON } from '../../libs/urlopen'
import 'ol/ol.css'

View file

@ -2,8 +2,6 @@ import React from 'react'
import PropTypes from 'prop-types'
import Button from '../Button'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import Modal from './Modal'
import LayerTypeBlock from '../layers/LayerTypeBlock'
@ -22,7 +20,7 @@ class AddModal extends React.Component {
sources: PropTypes.object.isRequired,
}
addLayer() {
addLayer = () => {
const changedLayers = this.props.layers.slice(0)
const layer = {
id: this.state.id,
@ -151,7 +149,7 @@ class AddModal extends React.Component {
}
<Button
className="maputnik-add-layer-button"
onClick={this.addLayer.bind(this)}
onClick={this.addLayer}
data-wd-key="add-layer"
>
Add Layer

View file

@ -11,205 +11,8 @@ import Modal from './Modal'
import MdFileDownload from 'react-icons/lib/md/file-download'
import TiClipboard from 'react-icons/lib/ti/clipboard'
import style from '../../libs/style'
import GitHub from 'github-api'
import { CopyToClipboard } from 'react-copy-to-clipboard'
class Gist extends React.Component {
static propTypes = {
mapStyle: PropTypes.object.isRequired,
onStyleChanged: PropTypes.func.isRequired,
}
constructor(props) {
super(props);
this.state = {
preview: false,
public: false,
saving: false,
latestGist: null,
}
}
static getDerivedStateFromProps(props, state) {
return {
preview: !!(props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']
};
}
onSave() {
this.setState({
...this.state,
saving: true
});
const preview = this.state.preview;
const mapboxToken = (this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token'];
const mapStyleStr = preview ?
styleSpec.format(stripAccessTokens(style.replaceAccessTokens(this.props.mapStyle))) :
styleSpec.format(stripAccessTokens(this.props.mapStyle));
const styleTitle = this.props.mapStyle.name || 'Style';
const htmlStr = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>`+styleTitle+` Preview</title>
<link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v0.44.0/mapbox-gl.css" />
<script src="https://api.mapbox.com/mapbox-gl-js/v0.44.0/mapbox-gl.js"></script>
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = '${mapboxToken}';
var map = new mapboxgl.Map({
container: 'map',
style: 'style.json',
attributionControl: true,
hash: true
});
map.addControl(new mapboxgl.NavigationControl());
</script>
</body>
</html>
`
const files = {
"style.json": {
content: mapStyleStr
}
}
if(preview) {
files["index.html"] = {
content: htmlStr
}
}
const gh = new GitHub();
let gist = gh.getGist(); // not a gist yet
gist.create({
public: this.state.public,
description: styleTitle,
files: files
}).then(function({data}) {
return gist.read();
}).then(function({data}) {
this.setState({
...this.state,
latestGist: data,
saving: false,
});
}.bind(this));
}
onPreviewChange(value) {
this.setState({
...this.state,
preview: value
})
}
onPublicChange(value) {
this.setState({
...this.state,
public: value
})
}
changeMetadataProperty(property, value) {
const changedStyle = {
...this.props.mapStyle,
metadata: {
...this.props.mapStyle.metadata,
[property]: value
}
}
this.props.onStyleChanged(changedStyle)
}
renderPreviewLink() {
const gist = this.state.latestGist;
const user = gist.user || 'anonymous';
const preview = !!gist.files['index.html'];
if(preview) {
return <span><a target="_blank" rel="noopener noreferrer" href={"https://bl.ocks.org/"+user+"/"+gist.id}>Preview</a>,{' '}</span>
}
return null;
}
renderLatestGist() {
const gist = this.state.latestGist;
const saving = this.state.saving;
if(saving) {
return <p>Saving...</p>
} else if(gist) {
const user = gist.user || 'anonymous';
const rawGistLink = "https://gist.githubusercontent.com/" + user + "/" + gist.id + "/raw/" + gist.history[0].version + "/style.json"
const maputnikStyleLink = "https://maputnik.github.io/editor/?style=" + rawGistLink
return <div className="maputnik-render-gist">
<p>
Latest saved gist:{' '}
{this.renderPreviewLink(this)}
<a target="_blank" rel="noopener noreferrer" href={"https://gist.github.com/" + user + "/" + gist.id}>Source</a>
</p>
<p>
<CopyToClipboard text={maputnikStyleLink}>
<span>Share this style: <Button><TiClipboard size={18} /></Button></span>
</CopyToClipboard>
<StringInput value={maputnikStyleLink} />
</p>
</div>
}
}
render() {
return <div className="maputnik-export-gist">
<Button onClick={this.onSave.bind(this)}>
<MdFileDownload />
Save to Gist (anonymous)
</Button>
<div className="maputnik-modal-sub-section">
<CheckboxInput
value={this.state.public}
name='gist-style-public'
onChange={this.onPublicChange.bind(this)}
/>
<span> Public gist</span>
</div>
<div className="maputnik-modal-sub-section">
<CheckboxInput
value={this.state.preview}
name='gist-style-preview'
onChange={this.onPreviewChange.bind(this)}
/>
<span> Include preview</span>
</div>
{this.state.preview ?
<div>
<InputBlock
label={"OpenMapTiles Access Token: "}>
<StringInput
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}/>
</InputBlock>
<InputBlock
label={"Mapbox Access Token: "}>
<StringInput
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}/>
</InputBlock>
<a target="_blank" rel="noopener noreferrer" href="https://openmaptiles.com/hosting/">Get your free access token</a>
</div>
: null}
{this.renderLatestGist()}
</div>
}
}
function stripAccessTokens(mapStyle) {
const changedMetadata = { ...mapStyle.metadata }
@ -293,10 +96,6 @@ class ExportModal extends React.Component {
</Button>
</div>
<div className="maputnik-modal-section hide">
<h4>Save style</h4>
<Gist mapStyle={this.props.mapStyle} onStyleChanged={this.props.onStyleChanged}/>
</div>
</Modal>
}
}

View file

@ -13,10 +13,6 @@ class LoadingModal extends React.Component {
message: PropTypes.node.isRequired,
}
constructor(props) {
super(props);
}
underlayOnClick(e) {
// This stops click events falling through to underlying modals.
e.stopPropagation();

View file

@ -4,7 +4,6 @@ import LoadingModal from './LoadingModal'
import Modal from './Modal'
import Button from '../Button'
import FileReaderInput from 'react-file-reader-input'
import request from 'request'
import FileUploadIcon from 'react-icons/lib/md/file-upload'
import AddIcon from 'react-icons/lib/md/add-circle-outline'
@ -74,42 +73,48 @@ class OpenModal extends React.Component {
}
}
onStyleSelect(styleUrl) {
onStyleSelect = (styleUrl) => {
this.clearError();
const reqOpts = {
url: styleUrl,
withCredentials: false,
}
const activeRequest = request(reqOpts, (error, response, body) => {
const activeRequest = fetch(styleUrl, {
mode: 'cors',
credentials: "same-origin"
})
.then(function(response) {
return response.json();
})
.then((body) => {
this.setState({
activeRequest: null,
activeRequestUrl: null
});
if (!error && response.statusCode == 200) {
const mapStyle = style.ensureStyleValidity(JSON.parse(body))
console.log('Loaded style ', mapStyle.id)
this.props.onStyleOpen(mapStyle)
this.onOpenToggle()
} else {
console.warn('Could not open the style URL', styleUrl)
}
const mapStyle = style.ensureStyleValidity(body)
console.log('Loaded style ', mapStyle.id)
this.props.onStyleOpen(mapStyle)
this.onOpenToggle()
})
.catch((err) => {
this.setState({
activeRequest: null,
activeRequestUrl: null
});
console.error(err);
console.warn('Could not open the style URL', styleUrl)
})
this.setState({
activeRequest: activeRequest,
activeRequestUrl: reqOpts.url
activeRequestUrl: styleUrl
})
}
onOpenUrl() {
onOpenUrl = () => {
const url = this.styleUrlElement.value;
this.onStyleSelect(url);
}
onUpload(_, files) {
onUpload = (_, files) => {
const [e, file] = files[0];
const reader = new FileReader();
@ -146,7 +151,7 @@ class OpenModal extends React.Component {
url={style.url}
title={style.title}
thumbnailUrl={style.thumbnail}
onSelect={this.onStyleSelect.bind(this)}
onSelect={this.onStyleSelect}
/>
})
@ -170,7 +175,7 @@ class OpenModal extends React.Component {
<section className="maputnik-modal-section">
<h2>Upload Style</h2>
<p>Upload a JSON style from your computer.</p>
<FileReaderInput onChange={this.onUpload.bind(this)} tabIndex="-1">
<FileReaderInput onChange={this.onUpload} tabIndex="-1">
<Button className="maputnik-upload-button"><FileUploadIcon /> Upload</Button>
</FileReaderInput>
</section>
@ -182,7 +187,7 @@ class OpenModal extends React.Component {
</p>
<input data-wd-key="open-modal.url.input" type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
<div>
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl.bind(this)}>Open URL</Button>
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl}>Open URL</Button>
</div>
</section>

View file

@ -15,10 +15,6 @@ class SettingsModal extends React.Component {
onOpenToggle: PropTypes.func.isRequired,
}
constructor(props) {
super(props);
}
changeStyleProperty(property, value) {
const changedStyle = {
...this.props.mapStyle,
@ -107,7 +103,7 @@ class SettingsModal extends React.Component {
data-wd-key="modal-settings.maputnik:renderer"
options={[
['mbgljs', 'MapboxGL JS'],
['ol3', 'Open Layers 3'],
// ['ol3', 'Open Layers 3'],
]}
value={metadata['maputnik:renderer'] || 'mbgljs'}
onChange={this.changeMetadataProperty.bind(this, 'maputnik:renderer')}

View file

@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import Button from '../Button'
import Modal from './Modal'
@ -11,10 +10,6 @@ class ShortcutsModal extends React.Component {
onOpenToggle: PropTypes.func.isRequired,
}
constructor(props) {
super(props);
}
render() {
const help = [
{

View file

@ -12,8 +12,6 @@ class SurveyModal extends React.Component {
onOpenToggle: PropTypes.func.isRequired,
}
constructor(props) { super(props); }
onClick = () => {
window.open('https://gregorywolanski.typeform.com/to/cPgaSY', '_blank');

View file

@ -1,8 +1,8 @@
import lodash from 'lodash'
import throttle from 'lodash.throttle'
// Throttle for 3 seconds so when a user enables it they don't have to refresh the page.
const reducedMotionEnabled = lodash.throttle(() => {
const reducedMotionEnabled = throttle(() => {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches
}, 3000);

View file

@ -1,4 +1,3 @@
import request from 'request'
import style from './style.js'
import ReconnectingWebSocket from 'reconnecting-websocket'
@ -14,15 +13,20 @@ export class ApiStyleStore {
}
init(cb) {
request(localUrl + '/styles', (error, response, body) => {
if (!error && body && response.statusCode == 200) {
const styleIds = JSON.parse(body)
this.latestStyleId = styleIds[0]
this.notifyLocalChanges()
cb(null)
} else {
cb(new Error('Can not connect to style API'))
}
fetch(localUrl + '/styles', {
mode: 'cors',
})
.then(function(response) {
return response.json();
})
.then(function(body) {
const styleIds = body;
this.latestStyleId = styleIds[0]
this.notifyLocalChanges()
cb(null)
})
.catch(function() {
cb(new Error('Can not connect to style API'))
})
}
@ -44,8 +48,14 @@ export class ApiStyleStore {
latestStyle(cb) {
if(this.latestStyleId) {
request(localUrl + '/styles/' + this.latestStyleId, (error, response, body) => {
cb(style.ensureStyleValidity(JSON.parse(body)))
fetch(localUrl + '/styles/' + this.latestStyleId, {
mode: 'cors',
})
.then(function(response) {
return response.json();
})
.then(function(body) {
cb(style.ensureStyleValidity(body))
})
} else {
throw new Error('No latest style available. You need to init the api backend first.')
@ -55,11 +65,15 @@ export class ApiStyleStore {
// Save current style replacing previous version
save(mapStyle) {
const id = mapStyle.id
request.put({
url: localUrl + '/styles/' + id,
json: true,
body: mapStyle
}, (error, response, body) => {
fetch(localUrl + '/styles/' + id, {
method: "PUT",
mode: 'cors',
headers: {
"Content-Type": "application/json; charset=utf-8",
},
body: JSON.stringify(mapStyle)
})
.catch(function(error) {
if(error) console.error(error)
})
return mapStyle

View file

@ -1,9 +1,9 @@
import MapboxGl from 'mapbox-gl/dist/mapbox-gl.js'
import MapboxGl from 'mapbox-gl'
// Load mapbox-gl-rtl-text using object urls without needing http://localhost for AJAX.
const data = require("base64-loader?mimetype=text/javascript!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.js");
const data = require("raw-loader?mimetype=text/javascript!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.js");
const blob = new window.Blob([window.atob(data)]);
const blob = new window.Blob([data]);
const objectUrl = window.URL.createObjectURL(blob, {
type: "text/javascript"
});

View file

@ -1,22 +1,19 @@
import request from 'request'
import npmurl from 'url'
function loadJSON(url, defaultValue, cb) {
request({
url: url,
withCredentials: false,
}, (error, response, body) => {
if (!error && body && response.statusCode == 200) {
try {
cb(JSON.parse(body))
} catch(err) {
console.error(err)
cb(defaultValue)
}
} else {
console.warn('Can not metadata for ' + url)
cb(defaultValue)
}
fetch(url, {
mode: 'cors',
credentials: "same-origin"
})
.then(function(response) {
return response.json();
})
.then(function(body) {
cb(body)
})
.catch(function() {
console.warn('Can not metadata for ' + url)
cb(defaultValue)
})
}

View file

@ -1,4 +1,3 @@
import React from 'react';
import deref from '@mapbox/mapbox-gl-style-spec/deref'
import tokens from '../config/tokens.json'
@ -54,27 +53,27 @@ function indexOfLayer(layers, layerId) {
return null
}
function getAccessToken(key, mapStyle, opts) {
if(key === "thunderforest_transport" || key === "thunderforest_outdoors") {
key = "thunderforest";
function getAccessToken(sourceName, mapStyle, opts) {
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
sourceName = "thunderforest"
}
const metadata = mapStyle.metadata || {}
let accessToken = metadata['maputnik:'+key+'_access_token'];
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
if(opts.allowFallback && !accessToken) {
accessToken = tokens[key];
accessToken = tokens[sourceName]
}
return accessToken;
}
function replaceSourceAccessToken(mapStyle, key, opts={}) {
const source = mapStyle.sources[key]
function replaceSourceAccessToken(mapStyle, sourceName, opts={}) {
const source = mapStyle.sources[sourceName]
if(!source) return mapStyle
if(!source.hasOwnProperty("url")) return mapStyle
const accessToken = getAccessToken(key, mapStyle, opts)
const accessToken = getAccessToken(sourceName, mapStyle, opts)
if(!accessToken) {
// Early exit.
@ -83,7 +82,7 @@ function replaceSourceAccessToken(mapStyle, key, opts={}) {
const changedSources = {
...mapStyle.sources,
[key]: {
[sourceName]: {
...source,
url: source.url.replace('{key}', accessToken)
}
@ -92,21 +91,20 @@ function replaceSourceAccessToken(mapStyle, key, opts={}) {
...mapStyle,
sources: changedSources
}
return changedStyle
}
function replaceAccessTokens(mapStyle, opts={}) {
let changedStyle = mapStyle;
let changedStyle = mapStyle
Object.keys(mapStyle.sources).forEach((tokenKey) => {
changedStyle = replaceSourceAccessToken(changedStyle, tokenKey, opts);
Object.keys(mapStyle.sources).forEach((sourceName) => {
changedStyle = replaceSourceAccessToken(changedStyle, sourceName, opts);
})
if(mapStyle.glyphs && mapStyle.glyphs.match(/\.tileserver\.org/)) {
if (mapStyle.glyphs && mapStyle.glyphs.match(/\.tilehosting\.com/)) {
changedStyle = {
...changedStyle,
glyphs: mapStyle.glyphs ? mapStyle.glyphs.replace('{key}', getAccessToken("openmaptiles", mapStyle, opts)) : mapStyle.glyphs
glyphs: mapStyle.glyphs.replace('{key}', getAccessToken("openmaptiles", mapStyle, opts))
}
}

View file

@ -1,8 +1,6 @@
import { colorizeLayers } from './style.js'
import style from './style.js'
import { loadStyleUrl } from './urlopen'
import publicSources from '../config/styles.json'
import request from 'request'
const storagePrefix = "maputnik"
const stylePrefix = 'style'

View file

@ -1,4 +1,3 @@
import request from 'request'
import url from 'url'
import style from './style.js'
@ -9,34 +8,40 @@ export function initialStyleUrl() {
export function loadStyleUrl(styleUrl, cb) {
console.log('Loading style', styleUrl)
request({
url: styleUrl,
withCredentials: false,
}, (error, response, body) => {
if (!error && response.statusCode == 200) {
cb(style.ensureStyleValidity(JSON.parse(body)))
} else {
console.warn('Could not fetch default style', styleUrl)
cb(style.emptyStyle)
}
fetch(styleUrl, {
mode: 'cors',
credentials: "same-origin"
})
.then(function(response) {
return response.json();
})
.then(function(body) {
cb(style.ensureStyleValidity(body))
})
.catch(function() {
console.warn('Could not fetch default style', styleUrl)
cb(style.emptyStyle)
})
}
export function loadJSON(url, defaultValue, cb) {
request({
url: url,
withCredentials: false,
}, (error, response, body) => {
if (!error && body && response.statusCode == 200) {
try {
cb(JSON.parse(body))
} catch(err) {
console.error(err)
cb(defaultValue)
}
} else {
console.error('Can not load JSON from ' + url)
fetch(url, {
mode: 'cors',
credentials: "same-origin"
})
.then(function(response) {
return response.json();
})
.then(function(body) {
try {
cb(body)
} catch(err) {
console.error(err)
cb(defaultValue)
}
})
.catch(function() {
console.error('Can not load JSON from ' + url)
cb(defaultValue)
})
}

View file

@ -1,5 +1,6 @@
// MAP
.maputnik-map {
display: flex;
position: fixed !important;
top: $toolbar-height + $toolbar-offset;
right: 0;
@ -10,6 +11,16 @@
- 200px /* layer list */
- 350px /* layer editor */
);
&--error {
align-items: center;
justify-content: center;
}
&__error-message {
margin: 16px;
text-align: center;
}
}
// DOC LABEL

View file

@ -1,7 +1,6 @@
var assert = require("assert");
var config = require("../../config/specs");
var helper = require("../helper");
var wd = require("../../wd-helper");
describe.skip("history", function() {

View file

@ -1,6 +1,4 @@
var assert = require('assert');
var config = require("../config/specs");
var geoServer = require("../geojson-server");
var helper = require("./helper");
require("./util/webdriverio-ext");

View file

@ -1,5 +1,3 @@
var assert = require('assert');
var wd = require("../../wd-helper");
var config = require("../../config/specs");
var helper = require("../helper");

View file

@ -1,4 +1,3 @@
var artifacts = require("../../artifacts");
var config = require("../../config/specs");
var helper = require("../helper");
var wd = require("../../wd-helper");