mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 09:12:52 +00:00
466 lines
14 KiB
JavaScript
466 lines
14 KiB
JavaScript
import {
|
|
event as d3_event,
|
|
select as d3_select
|
|
} from 'd3-selection';
|
|
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
|
|
|
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
|
|
|
|
import { t, textDirection } from '../util/locale';
|
|
import { tooltip } from '../util/tooltip';
|
|
|
|
import { behaviorHash } from '../behavior';
|
|
import { modeBrowse } from '../modes';
|
|
import { services } from '../services';
|
|
import { svgDefs, svgIcon } from '../svg';
|
|
import { utilGetDimensions } from '../util/dimensions';
|
|
import { utilRebind } from '../util';
|
|
|
|
import { uiAccount } from './account';
|
|
import { uiAttribution } from './attribution';
|
|
import { uiBackground } from './background';
|
|
import { uiContributors } from './contributors';
|
|
import { uiFeatureInfo } from './feature_info';
|
|
import { uiFullScreen } from './full_screen';
|
|
import { uiGeolocate } from './geolocate';
|
|
import { uiHelp } from './help';
|
|
import { uiInfo } from './info';
|
|
import { uiIntro } from './intro';
|
|
import { uiLoading } from './loading';
|
|
import { uiMapData } from './map_data';
|
|
import { uiMapInMap } from './map_in_map';
|
|
import { uiModes } from './modes';
|
|
import { uiNotice } from './notice';
|
|
import { uiRestore } from './restore';
|
|
import { uiSave } from './save';
|
|
import { uiScale } from './scale';
|
|
import { uiShortcuts } from './shortcuts';
|
|
import { uiSidebar } from './sidebar';
|
|
import { uiSpinner } from './spinner';
|
|
import { uiSplash } from './splash';
|
|
import { uiStatus } from './status';
|
|
import { uiUndoRedo } from './undo_redo';
|
|
import { uiVersion } from './version';
|
|
import { uiZoom } from './zoom';
|
|
import { uiCmd } from './cmd';
|
|
|
|
|
|
export function uiInit(context) {
|
|
var uiInitCounter = 0;
|
|
var dispatch = d3_dispatch('photoviewerResize');
|
|
|
|
|
|
function render(container) {
|
|
container
|
|
.attr('dir', textDirection);
|
|
|
|
var map = context.map();
|
|
|
|
var hash = behaviorHash(context);
|
|
hash();
|
|
|
|
if (!hash.hadHash) {
|
|
map.centerZoom([0, 0], 2);
|
|
}
|
|
|
|
container
|
|
.append('svg')
|
|
.attr('id', 'defs')
|
|
.call(svgDefs(context));
|
|
|
|
container
|
|
.append('div')
|
|
.attr('id', 'sidebar')
|
|
.attr('class', 'col4')
|
|
.call(ui.sidebar);
|
|
|
|
var content = container
|
|
.append('div')
|
|
.attr('id', 'content')
|
|
.attr('class', 'active');
|
|
|
|
var bar = content
|
|
.append('div')
|
|
.attr('id', 'bar')
|
|
.attr('class', 'fillD');
|
|
|
|
content
|
|
.append('div')
|
|
.attr('id', 'map')
|
|
.attr('dir', 'ltr')
|
|
.call(map);
|
|
|
|
content
|
|
.call(uiMapInMap(context))
|
|
.call(uiInfo(context))
|
|
.call(uiNotice(context));
|
|
|
|
bar
|
|
.append('div')
|
|
.attr('class', 'spacer col4');
|
|
|
|
var limiter = bar.append('div')
|
|
.attr('class', 'limiter');
|
|
|
|
limiter
|
|
.append('div')
|
|
.attr('class', 'button-wrap joined col3')
|
|
.call(uiModes(context), limiter);
|
|
|
|
limiter
|
|
.append('div')
|
|
.attr('class', 'button-wrap joined col1')
|
|
.call(uiUndoRedo(context));
|
|
|
|
limiter
|
|
.append('div')
|
|
.attr('class', 'button-wrap col1')
|
|
.call(uiSave(context));
|
|
|
|
bar
|
|
.append('div')
|
|
.attr('class', 'full-screen')
|
|
.call(uiFullScreen(context));
|
|
|
|
bar
|
|
.append('div')
|
|
.attr('class', 'spinner')
|
|
.call(uiSpinner(context));
|
|
|
|
|
|
var controls = bar
|
|
.append('div')
|
|
.attr('class', 'map-controls');
|
|
|
|
controls
|
|
.append('div')
|
|
.attr('class', 'map-control zoombuttons')
|
|
.call(uiZoom(context));
|
|
|
|
controls
|
|
.append('div')
|
|
.attr('class', 'map-control geolocate-control')
|
|
.call(uiGeolocate(context));
|
|
|
|
controls
|
|
.append('div')
|
|
.attr('class', 'map-control background-control')
|
|
.call(uiBackground(context));
|
|
|
|
controls
|
|
.append('div')
|
|
.attr('class', 'map-control map-data-control')
|
|
.call(uiMapData(context));
|
|
|
|
controls
|
|
.append('div')
|
|
.attr('class', 'map-control help-control')
|
|
.call(uiHelp(context));
|
|
|
|
|
|
var about = content
|
|
.append('div')
|
|
.attr('id', 'about');
|
|
|
|
about
|
|
.append('div')
|
|
.attr('id', 'attrib')
|
|
.attr('dir', 'ltr')
|
|
.call(uiAttribution(context));
|
|
|
|
about
|
|
.append('div')
|
|
.attr('class', 'api-status')
|
|
.call(uiStatus(context));
|
|
|
|
|
|
var footer = about
|
|
.append('div')
|
|
.attr('id', 'footer')
|
|
.attr('class', 'fillD');
|
|
|
|
footer
|
|
.append('div')
|
|
.attr('id', 'flash-wrap')
|
|
.attr('class', 'footer-hide');
|
|
|
|
var footerWrap = footer
|
|
.append('div')
|
|
.attr('id', 'footer-wrap')
|
|
.attr('class', 'footer-show');
|
|
|
|
footerWrap
|
|
.append('div')
|
|
.attr('id', 'scale-block')
|
|
.call(uiScale(context));
|
|
|
|
var aboutList = footerWrap
|
|
.append('div')
|
|
.attr('id', 'info-block')
|
|
.append('ul')
|
|
.attr('id', 'about-list');
|
|
|
|
if (!context.embed()) {
|
|
aboutList
|
|
.call(uiAccount(context));
|
|
}
|
|
|
|
aboutList
|
|
.append('li')
|
|
.attr('class', 'version')
|
|
.call(uiVersion(context));
|
|
|
|
var issueLinks = aboutList
|
|
.append('li');
|
|
|
|
issueLinks
|
|
.append('a')
|
|
.attr('target', '_blank')
|
|
.attr('tabindex', -1)
|
|
.attr('href', 'https://github.com/openstreetmap/iD/issues')
|
|
.call(svgIcon('#iD-icon-bug', 'light'))
|
|
.call(tooltip().title(t('report_a_bug')).placement('top'));
|
|
|
|
issueLinks
|
|
.append('a')
|
|
.attr('target', '_blank')
|
|
.attr('tabindex', -1)
|
|
.attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
|
|
.call(svgIcon('#iD-icon-translate', 'light'))
|
|
.call(tooltip().title(t('help_translate')).placement('top'));
|
|
|
|
aboutList
|
|
.append('li')
|
|
.attr('class', 'feature-warning')
|
|
.attr('tabindex', -1)
|
|
.call(uiFeatureInfo(context));
|
|
|
|
aboutList
|
|
.append('li')
|
|
.attr('class', 'user-list')
|
|
.attr('tabindex', -1)
|
|
.call(uiContributors(context));
|
|
|
|
|
|
var photoviewer = content
|
|
.append('div')
|
|
.attr('id', 'photoviewer')
|
|
.classed('al', true) // 'al'=left, 'ar'=right
|
|
.classed('hide', true);
|
|
|
|
photoviewer
|
|
.append('button')
|
|
.attr('class', 'thumb-hide')
|
|
.on('click', function () {
|
|
if (services.streetside) { services.streetside.hideViewer(); }
|
|
if (services.mapillary) { services.mapillary.hideViewer(); }
|
|
if (services.openstreetcam) { services.openstreetcam.hideViewer(); }
|
|
})
|
|
.append('div')
|
|
.call(svgIcon('#iD-icon-close'));
|
|
|
|
photoviewer
|
|
.append('button')
|
|
.attr('class', 'resize-handle-xy')
|
|
.on(
|
|
'mousedown',
|
|
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnX: true, resizeOnY: true })
|
|
);
|
|
|
|
photoviewer
|
|
.append('button')
|
|
.attr('class', 'resize-handle-x')
|
|
.on(
|
|
'mousedown',
|
|
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnX: true })
|
|
);
|
|
|
|
photoviewer
|
|
.append('button')
|
|
.attr('class', 'resize-handle-y')
|
|
.on(
|
|
'mousedown',
|
|
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnY: true })
|
|
);
|
|
|
|
var mapDimensions = map.dimensions();
|
|
|
|
// bind events
|
|
window.onbeforeunload = function() {
|
|
return context.save();
|
|
};
|
|
|
|
window.onunload = function() {
|
|
context.history().unlock();
|
|
};
|
|
|
|
d3_select(window)
|
|
.on('resize.editor', onResize);
|
|
|
|
onResize();
|
|
|
|
|
|
var pa = 80; // pan amount
|
|
var keybinding = d3_keybinding('main')
|
|
.on('⌫', function() { d3_event.preventDefault(); })
|
|
.on('←', pan([pa, 0]))
|
|
.on('↑', pan([0, pa]))
|
|
.on('→', pan([-pa, 0]))
|
|
.on('↓', pan([0, -pa]))
|
|
.on(['⇧←', uiCmd('⌘←')], pan([mapDimensions[0], 0]))
|
|
.on(['⇧↑', uiCmd('⌘↑')], pan([0, mapDimensions[1]]))
|
|
.on(['⇧→', uiCmd('⌘→')], pan([-mapDimensions[0], 0]))
|
|
.on(['⇧↓', uiCmd('⌘↓')], pan([0, -mapDimensions[1]]));
|
|
|
|
d3_select(document)
|
|
.call(keybinding);
|
|
|
|
context.enter(modeBrowse(context));
|
|
|
|
if (!uiInitCounter++) {
|
|
if (!hash.startWalkthrough) {
|
|
context.container()
|
|
.call(uiSplash(context))
|
|
.call(uiRestore(context));
|
|
}
|
|
|
|
context.container()
|
|
.call(uiShortcuts(context));
|
|
}
|
|
|
|
var osm = context.connection();
|
|
var auth = uiLoading(context).message(t('loading_auth')).blocking(true);
|
|
|
|
if (osm && auth) {
|
|
osm
|
|
.on('authLoading.ui', function() {
|
|
context.container()
|
|
.call(auth);
|
|
})
|
|
.on('authDone.ui', function() {
|
|
auth.close();
|
|
});
|
|
}
|
|
|
|
uiInitCounter++;
|
|
|
|
if (hash.startWalkthrough) {
|
|
hash.startWalkthrough = false;
|
|
context.container().call(uiIntro(context));
|
|
}
|
|
|
|
|
|
function onResize() {
|
|
mapDimensions = utilGetDimensions(content, true);
|
|
map.dimensions(mapDimensions);
|
|
|
|
// shrink photo viewer if it is too big
|
|
// (-90 preserves space at top and bottom of map used by menus)
|
|
var photoDimensions = utilGetDimensions(photoviewer, true);
|
|
if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
|
|
var setPhotoDimensions = [
|
|
Math.min(photoDimensions[0], mapDimensions[0]),
|
|
Math.min(photoDimensions[1], mapDimensions[1] - 90),
|
|
];
|
|
|
|
photoviewer
|
|
.style('width', setPhotoDimensions[0] + 'px')
|
|
.style('height', setPhotoDimensions[1] + 'px');
|
|
|
|
dispatch.call('photoviewerResize', photoviewer, setPhotoDimensions);
|
|
}
|
|
}
|
|
|
|
|
|
function pan(d) {
|
|
return function() {
|
|
d3_event.preventDefault();
|
|
context.pan(d, 100);
|
|
};
|
|
}
|
|
|
|
function buildResizeListener(target, eventName, dispatch, options) {
|
|
var resizeOnX = !!options.resizeOnX;
|
|
var resizeOnY = !!options.resizeOnY;
|
|
var minHeight = options.minHeight || 240;
|
|
var minWidth = options.minWidth || 320;
|
|
var startX;
|
|
var startY;
|
|
var startWidth;
|
|
var startHeight;
|
|
|
|
function startResize() {
|
|
var mapSize = context.map().dimensions();
|
|
|
|
if (resizeOnX) {
|
|
var maxWidth = mapSize[0];
|
|
var newWidth = clamp((startWidth + d3_event.clientX - startX), minWidth, maxWidth);
|
|
target.style('width', newWidth + 'px');
|
|
}
|
|
|
|
if (resizeOnY) {
|
|
var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
|
|
var newHeight = clamp((startHeight + startY - d3_event.clientY), minHeight, maxHeight);
|
|
target.style('height', newHeight + 'px');
|
|
}
|
|
|
|
dispatch.call(eventName, target, utilGetDimensions(target, true));
|
|
}
|
|
|
|
function clamp(num, min, max) {
|
|
return Math.max(min, Math.min(num, max));
|
|
}
|
|
|
|
function stopResize() {
|
|
d3_select(window)
|
|
.on('.' + eventName, null);
|
|
}
|
|
|
|
return function initResize() {
|
|
startX = d3_event.clientX;
|
|
startY = d3_event.clientY;
|
|
startWidth = target.node().getBoundingClientRect().width;
|
|
startHeight = target.node().getBoundingClientRect().height;
|
|
|
|
d3_select(window)
|
|
.on('mousemove.' + eventName, startResize, false)
|
|
.on('mouseup.' + eventName, stopResize, false);
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
var renderCallback;
|
|
|
|
function ui(node, callback) {
|
|
renderCallback = callback;
|
|
var container = d3_select(node);
|
|
context.container(container);
|
|
context.loadLocale(function(err) {
|
|
if (!err) {
|
|
render(container);
|
|
}
|
|
if (callback) {
|
|
callback(err);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
ui.restart = function(arg) {
|
|
context.locale(arg);
|
|
context.loadLocale(function(err) {
|
|
if (!err) {
|
|
context.container().selectAll('*').remove();
|
|
render(context.container());
|
|
if (renderCallback) renderCallback();
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
ui.sidebar = uiSidebar(context);
|
|
|
|
return utilRebind(ui, dispatch, 'on');
|
|
}
|