Merge branch 'master' into new-note

This commit is contained in:
Thomas Hervey
2018-07-19 18:38:26 -04:00
committed by GitHub
17 changed files with 458 additions and 68 deletions

View File

@@ -71,7 +71,7 @@ path.stroke.tag-barrier {
/* bridges */
path.casing.tag-bridge {
stroke-opacity: 0.6;
stroke: #000;
stroke: #000 !important;
stroke-linecap: butt;
stroke-dasharray: none;
}

View File

@@ -19,16 +19,52 @@
border-radius: 0;
padding: 5px;
position: absolute;
right: 0;
top: 0;
z-index: 48;
right: 5px;
top: 5px;
z-index: 50;
}
#photoviewer button.resize-handle-xy {
border-radius: 0;
position: absolute;
top: 0;
right: 0;
z-index: 49;
cursor: nesw-resize;
height: 25px;
width: 25px;
}
#photoviewer button.resize-handle-x {
border-radius: 0;
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: 48;
cursor: ew-resize;
height: auto;
width: 6px;
}
#photoviewer button.resize-handle-y {
border-radius: 0;
position: absolute;
top: 0;
right: 0;
z-index: 48;
cursor: ns-resize;
height: 6px;
width: 100%;
}
.photo-wrapper,
.photo-wrapper img {
width: 100%;
height: 100%;
overflow: hidden;
object-fit: cover;
}
.photo-wrapper .photo-attribution {
@@ -186,7 +222,7 @@
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
pading: 0 5px;
padding: 0 5px;
}
.ms-wrapper .photo-attribution .image-view-link {
text-align: left;
@@ -262,6 +298,8 @@ label.streetside-hires {
}
.osc-image-wrap {
width: 100%;
height: 100%;
transform-origin:0 0;
-ms-transform-origin:0 0;
-webkit-transform-origin:0 0;

View File

@@ -127,12 +127,16 @@
border-radius: 20px;
}
.comment-main {
padding: 10px;
padding: 10px 10px 10px 0;
flex: 1 1 100%;
flex-flow: column nowrap;
overflow: hidden;
overflow-wrap: break-word;
}
[dir='rtl'] .comment-main {
padding: 10px 0 10px 10px;
}
.comment-metadata {
flex-flow: row nowrap;
justify-content: space-between;
@@ -154,14 +158,18 @@
border-left: none;
}
#new-comment-input {
.note-save {
padding: 10px;
}
.note-save #new-comment-input {
width: 100%;
height: 100px;
max-height: 300px;
min-height: 100px;
}
.note-save-section {
.note-save .detail-section {
margin: 10px 0;
}

View File

@@ -607,8 +607,13 @@ button.save.has-count .count::before {
margin-right: 5px;
}
[dir='rtl'] .icon.pre-text {
margin-left: 5px;
margin-right: 0;
margin-left: 5px;
margin-right: 0;
}
.icon.pre-text.user-icon {
margin-left: 5px;
margin-right: 5px;
}
.icon.light {
@@ -1320,6 +1325,12 @@ a.hide-toggle {
border: 1px solid #ccc;
}
/* no scrollbars */
.inspector-hover div {
overflow-x: hidden;
overflow-y: hidden;
}
/* hide and remove from layout */
.inspector-hidden,
.inspector-hover label input[type="checkbox"],
@@ -3841,7 +3852,6 @@ svg.mouseclick use.right {
}
/* Save Mode
------------------------------------------------------- */
.mode-save a.user-info {
@@ -3870,6 +3880,7 @@ svg.mouseclick use.right {
color: #fff;
}
.note-save .field-warning,
.mode-save .field-warning {
background: #ffb;
border: 1px solid #ccc;
@@ -3877,6 +3888,7 @@ svg.mouseclick use.right {
padding: 10px;
}
.note-save .field-warning:empty,
.mode-save .field-warning:empty {
display: none;
}

View File

@@ -280,8 +280,8 @@ en:
localized_translation_language: Choose language
localized_translation_name: Name
zoom_in_edit: Zoom in to edit
login: login
logout: logout
login: Log In
logout: Log Out
loading_auth: "Connecting to OpenStreetMap..."
report_a_bug: Report a bug
help_translate: Help translate
@@ -638,6 +638,9 @@ en:
new: New Note
newDescription: "Describe the issue."
newNote: Add Note
login: You must log in to change or comment on this note.
upload_explanation: "Your comments will be publicly visible to all OpenStreetMap users."
upload_explanation_with_user: "Your comments as {user} will be publicly visible to all OpenStreetMap users."
help:
title: Help
key: H

View File

@@ -358,8 +358,8 @@
"localized_translation_name": "Name"
},
"zoom_in_edit": "Zoom in to edit",
"login": "login",
"logout": "logout",
"login": "Log In",
"logout": "Log Out",
"loading_auth": "Connecting to OpenStreetMap...",
"report_a_bug": "Report a bug",
"help_translate": "Help translate",
@@ -773,6 +773,9 @@
"new": "New Note",
"newDescription": "Describe the issue.",
"newNote": "Add Note"
"login": "You must log in to change or comment on this note.",
"upload_explanation": "Your comments will be publicly visible to all OpenStreetMap users.",
"upload_explanation_with_user": "Your comments as {user} will be publicly visible to all OpenStreetMap users."
},
"help": {
"title": "Help",

View File

@@ -410,6 +410,13 @@ export default {
// load mapillary signs sprite
var defs = context.container().select('defs');
defs.call(svgDefs(context).addSprites, ['mapillary-sprite']);
// Register viewer resize handler
context.ui().on('photoviewerResize', function() {
if (_mlyViewer) {
_mlyViewer.resize();
}
});
},

View File

@@ -34,17 +34,17 @@ import {
} from '../util';
var apibase = 'https://openstreetcam.org',
maxResults = 1000,
tileZoom = 14,
dispatch = d3_dispatch('loadedImages'),
imgZoom = d3_zoom()
.extent([[0, 0], [320, 240]])
.translateExtent([[0, 0], [320, 240]])
.scaleExtent([1, 15])
.on('zoom', zoomPan),
_oscCache,
_oscSelectedImage;
var apibase = 'https://openstreetcam.org';
var maxResults = 1000;
var tileZoom = 14;
var dispatch = d3_dispatch('loadedImages');
var imgZoom = d3_zoom()
.extent([[0, 0], [320, 240]])
.translateExtent([[0, 0], [320, 240]])
.scaleExtent([1, 15])
.on('zoom', zoomPan);
var _oscCache;
var _oscSelectedImage;
function abortRequest(i) {
@@ -129,12 +129,12 @@ function loadNextTilePage(which, currZoom, url, tile) {
var maxPages = maxPageAtZoom(currZoom);
var nextPage = cache.nextPage[tile.id] || 1;
var params = utilQsString({
ipp: maxResults,
page: nextPage,
// client_id: clientId,
bbTopLeft: [bbox.maxY, bbox.minX].join(','),
bbBottomRight: [bbox.minY, bbox.maxX].join(',')
}, true);
ipp: maxResults,
page: nextPage,
// client_id: clientId,
bbTopLeft: [bbox.maxY, bbox.minX].join(','),
bbBottomRight: [bbox.minY, bbox.maxX].join(',')
}, true);
if (nextPage > maxPages) return;
@@ -367,6 +367,16 @@ export default {
.attr('class', 'osc-image-wrap');
// Register viewer resize handler
context.ui().on('photoviewerResize', function(dimensions) {
imgZoom = d3_zoom()
.extent([[0, 0], dimensions])
.translateExtent([[0, 0], dimensions])
.scaleExtent([1, 15])
.on('zoom', zoomPan);
});
function rotate(deg) {
return function() {
if (!_oscSelectedImage) return;

View File

@@ -669,6 +669,14 @@ export default {
.attr('src', context.asset(pannellumViewerJS));
// Register viewer resize handler
context.ui().on('photoviewerResize', function() {
if (_pannellumViewer) {
_pannellumViewer.resize();
}
});
function step(stepBy) {
return function() {
var viewer = d3_select('#photoviewer');

View File

@@ -167,7 +167,7 @@ export function uiCommit(context) {
userLink
.append('a')
.attr('class','user-info')
.attr('class', 'user-info')
.text(user.display_name)
.attr('href', osm.userURL(user.display_name))
.attr('tabindex', -1)

View File

@@ -2,6 +2,7 @@ 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';
@@ -13,6 +14,7 @@ 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';
@@ -45,6 +47,7 @@ import { uiCmd } from './cmd';
export function uiInit(context) {
var uiInitCounter = 0;
var dispatch = d3_dispatch('photoviewerResize');
function render(container) {
@@ -256,7 +259,33 @@ export function uiInit(context) {
.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();
};
@@ -265,30 +294,13 @@ export function uiInit(context) {
context.history().unlock();
};
var mapDimensions = map.dimensions();
function onResize() {
mapDimensions = utilGetDimensions(content, true);
map.dimensions(mapDimensions);
}
d3_select(window)
.on('resize.editor', onResize);
onResize();
function pan(d) {
return function() {
d3_event.preventDefault();
context.pan(d, 100);
};
}
// pan amount
var pa = 80;
var pa = 80; // pan amount
var keybinding = d3_keybinding('main')
.on('⌫', function() { d3_event.preventDefault(); })
.on('←', pan([pa, 0]))
@@ -316,8 +328,8 @@ export function uiInit(context) {
.call(uiShortcuts(context));
}
var osm = context.connection(),
auth = uiLoading(context).message(t('loading_auth')).blocking(true);
var osm = context.connection();
var auth = uiLoading(context).message(t('loading_auth')).blocking(true);
if (osm && auth) {
osm
@@ -336,6 +348,85 @@ export function uiInit(context) {
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);
};
}
}
@@ -370,5 +461,5 @@ export function uiInit(context) {
ui.sidebar = uiSidebar(context);
return ui;
return utilRebind(ui, dispatch, 'on');
}

View File

@@ -66,7 +66,7 @@ export function uiNoteComments() {
mainEnter
.append('div')
.attr('class', 'comment-text')
.text(function(d) { return d.text; });
.html(function(d) { return d.html; });
comments
.call(replaceAvatars);
@@ -101,6 +101,7 @@ export function uiNoteComments() {
if (!s) return null;
var detected = utilDetect();
var options = { day: 'numeric', month: 'short', year: 'numeric' };
s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
var d = new Date(s);
if (isNaN(d.getTime())) return null;
return d.toLocaleDateString(detected.locale, options);

View File

@@ -1,5 +1,8 @@
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { t } from '../util/locale';
import { services } from '../services';
@@ -53,29 +56,42 @@ export function uiNoteEditor(context) {
.attr('class', 'body')
.merge(body);
body.selectAll('.note-editor')
.data([0])
.enter()
var editor = body.selectAll('.note-editor')
.data([0]);
editor = editor.enter()
.append('div')
.attr('class', 'modal-section note-editor')
.merge(editor)
.call(noteHeader.note(_note))
.call(noteComments.note(_note))
.call(noteSave);
.call(noteSaveSection);
selection.selectAll('.footer')
.data([0])
.enter()
var footer = selection.selectAll('.footer')
.data([0]);
footer = footer.enter()
.append('div')
.attr('class', 'footer')
.merge(footer)
.call(uiViewOnOSM(context).what(_note))
.call(uiNoteReport(context).note(_note));
// rerender the note editor on any auth change
var osm = services.osm;
if (osm) {
osm.on('change.note-save', function() {
selection.call(noteEditor);
});
}
}
function noteSave(selection) {
function noteSaveSection(selection) {
var isSelected = (_note && _note.id === context.selectedNoteID());
var noteSave = selection.selectAll('.note-save-section')
var noteSave = selection.selectAll('.note-save')
.data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
// exit
@@ -85,7 +101,7 @@ export function uiNoteEditor(context) {
// enter
var noteSaveEnter = noteSave.enter()
.append('div')
.attr('class', 'note-save-section save-section cf');
.attr('class', 'note-save save-section cf');
noteSaveEnter
.append('h4')
@@ -107,6 +123,7 @@ export function uiNoteEditor(context) {
// update
noteSave = noteSaveEnter
.merge(noteSave)
.call(userDetails)
.call(noteSaveButtons);
@@ -128,7 +145,100 @@ export function uiNoteEditor(context) {
}
function userDetails(selection) {
var detailSection = selection.selectAll('.detail-section')
.data([0]);
detailSection = detailSection.enter()
.append('div')
.attr('class', 'detail-section')
.merge(detailSection);
var osm = services.osm;
if (!osm) return;
// Add warning if user is not logged in
var hasAuth = osm.authenticated();
var authWarning = detailSection.selectAll('.auth-warning')
.data(hasAuth ? [] : [0]);
authWarning.exit()
.transition()
.duration(200)
.style('opacity', 0)
.remove();
var authEnter = authWarning.enter()
.insert('div', '.tag-reference-body')
.attr('class', 'field-warning auth-warning')
.style('opacity', 0);
authEnter
.call(svgIcon('#iD-icon-alert', 'inline'));
authEnter
.append('span')
.text(t('note.login'));
authEnter
.append('a')
.attr('target', '_blank')
.call(svgIcon('#iD-icon-out-link', 'inline'))
.append('span')
.text(t('login'))
.on('click.note-login', function() {
d3_event.preventDefault();
osm.authenticate();
});
authEnter
.transition()
.duration(200)
.style('opacity', 1);
var prose = detailSection.selectAll('.note-save-prose')
.data(hasAuth ? [0] : []);
prose.exit()
.remove();
prose = prose.enter()
.append('p')
.attr('class', 'note-save-prose')
.text(t('note.upload_explanation'))
.merge(prose);
osm.userDetails(function(err, user) {
if (err) return;
var userLink = d3_select(document.createElement('div'));
if (user.image_url) {
userLink
.append('img')
.attr('src', user.image_url)
.attr('class', 'icon pre-text user-icon');
}
userLink
.append('a')
.attr('class', 'user-info')
.text(user.display_name)
.attr('href', osm.userURL(user.display_name))
.attr('tabindex', -1)
.attr('target', '_blank');
prose
.html(t('note.upload_explanation_with_user', { user: userLink.html() }));
});
}
function noteSaveButtons(selection) {
var osm = services.osm;
var hasAuth = osm && osm.authenticated();
var isSelected = (_note && _note.id === context.selectedNoteID());
var buttonSection = selection.selectAll('.buttons')
.data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
@@ -168,6 +278,7 @@ export function uiNoteEditor(context) {
.merge(buttonEnter);
buttonSection.select('.status-button') // select and propagate data
.attr('disabled', (hasAuth ? null : true))
.text(function(d) {
var action = (d.status === 'open' ? 'close' : 'open');
var andComment = (d.newComment ? '_comment' : '');
@@ -186,7 +297,7 @@ export function uiNoteEditor(context) {
buttonSection.select('.comment-button') // select and propagate data
.attr('disabled', function(d) {
return (d.status === 'open' && d.newComment) ? null : true;
return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
})
.on('click.save', function(d) {
this.blur(); // avoid keeping focus on the button - #4641

View File

@@ -75,7 +75,7 @@
"osm-community-index": "0.4.5",
"phantomjs-prebuilt": "~2.1.11",
"request": "^2.85.0",
"rollup": "~0.60.0",
"rollup": "~0.63.2",
"rollup-plugin-commonjs": "^9.0.0",
"rollup-plugin-includepaths": "~0.2.3",
"rollup-plugin-json": "^3.0.0",

BIN
test/data/mvttest.pbf Normal file

Binary file not shown.

View File

@@ -116,6 +116,7 @@
<script src='spec/svg/layers.js'></script>
<script src='spec/svg/lines.js'></script>
<script src='spec/svg/midpoints.js'></script>
<script src='spec/svg/mvt.js'></script>
<script src='spec/svg/osm.js'></script>
<script src='spec/svg/points.js'></script>
<script src='spec/svg/svg.js'></script>

97
test/spec/svg/mvt.js Normal file
View File

@@ -0,0 +1,97 @@
describe('iD.svgMvt', function () {
var context;
var surface;
var dispatch = d3.dispatch('change');
var projection = iD.geoRawMercator()
.translate([6934098.868981334, 4092682.5519805425])
.scale(iD.geoZoomToScale(17))
.clipExtent([[0, 0], [1000, 1000]]);
var gj = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'id': 316973311,
'geometry': {
'type': 'Point',
'coordinates': [
-74.38928604125977,
40.150275473401365
]
},
'properties': {
'abbr': 'N.J.',
'area': 19717.8,
'name': 'New Jersey',
'name_en': 'New Jersey',
'osm_id': 316973311
}
}
]
};
beforeEach(function () {
context = iD.coreContext();
d3.select(document.createElement('div'))
.attr('id', 'map')
.call(context.map().centerZoom([-74.389286, 40.1502754], 17));
surface = context.surface();
});
it('creates layer-mvt', function () {
var render = iD.svgMvt(projection, context, dispatch);
surface.call(render);
var layers = surface.selectAll('g.layer-mvt').nodes();
expect(layers.length).to.eql(1);
});
it('draws geojson', function () {
var render = iD.svgMvt(projection, context, dispatch).geojson(gj);
surface.call(render);
var path = surface.selectAll('path.mvt');
expect(path.nodes().length).to.eql(1);
expect(path.attr('d')).to.match(/^M.*z$/);
});
describe('#url', function() {
it('handles pbf url', function () {
var url = '../../data/mvttest.pbf';
var render = iD.svgMvt(projection, context, dispatch).url(url);
surface.call(render);
var path = surface.selectAll('path.mvt');
expect(path.nodes().length).to.eql(1);
expect(path.attr('d')).to.match(/^M.*z$/);
});
});
describe('#showLabels', function() {
it('shows labels by default', function () {
var render = iD.svgMvt(projection, context, dispatch).geojson(gj);
surface.call(render);
var label = surface.selectAll('text.mvtlabel');
expect(label.nodes().length).to.eql(1);
expect(label.text()).to.eql('New Jersey');
var halo = surface.selectAll('text.mvtlabel-halo');
expect(halo.nodes().length).to.eql(1);
expect(halo.text()).to.eql('New Jersey');
});
it('hides labels with showLabels(false)', function () {
var render = iD.svgMvt(projection, context, dispatch).geojson(gj).showLabels(false);
surface.call(render);
expect(surface.selectAll('text.mvtlabel').empty()).to.be.ok;
expect(surface.selectAll('text.mvtlabel-halo').empty()).to.be.ok;
});
});
});