diff --git a/css/app.css b/css/app.css index 932ca3b52..f57611633 100644 --- a/css/app.css +++ b/css/app.css @@ -5,6 +5,7 @@ body { font:normal 12px/1.6666 'Helvetica Neue', Arial, sans-serif; margin:0; padding:0; + min-width: 768px; color:#222; /* text-rendering: optimizeLegibility; */ -webkit-font-smoothing: subpixel-antialiased; @@ -14,6 +15,11 @@ body { height: 100%; width: 100%; position: fixed; + min-width: 768px; +} + +.limiter { + max-width: 1200px; } div, textarea, input, span, ul, li, ol, a, button { @@ -86,16 +92,21 @@ a:hover { color:#154dff; } + textarea, input[type=text] { background-color: white; border:1px solid #ccc; padding:10px; + resize: vertical; } -input[type=text] { - padding:4px 10px; - height:30px; +.fillD textarea, +.fillD input[type=text] { + background-color: black; + border:1px solid rgba(255, 255, 255, .25); + color: white; + border-radius: 4px; } textarea:focus, @@ -103,6 +114,18 @@ input[type=text]:focus { background-color: #ececec; } +.fillD textarea:focus, +.fillD input[type=text]:focus { + background-color: black; + border:1px solid rgba(255, 255, 255, .5); +} + +input[type=text] { + padding:4px 10px; + height:30px; + resize: none; +} + /* tables */ table { @@ -121,6 +144,36 @@ table.tags, table.tags td, table.tags th { padding: 4px; } +/* Grid +------------------------------------------------------- */ + +.col0 { float:left; width:04.1666%; } +.col1 { float:left; width:08.3333%; } +.col2 { float:left; width:16.6666%; } +.col3 { float:left; width:25.0000%; } +.col4 { float:left; width:33.3333%; } +.col5 { float:left; width:41.6666%; } +.col6 { float:left; width:50.0000%; max-width: 600px; } +.col7 { float:left; width:58.3333%; } +.col8 { float:left; width:66.6666%; } +.col9 { float:left; width:75.0000%; } +.col10 { float:left; width:83.3333%; } +.col11 { float:left; width:91.6666%; } +.col12 { float:left; width:100.0000%; } +.margin0 { margin-left: 04.1666%; } +.margin1 { margin-left: 08.3333%; } +.margin2 { margin-left: 16.6666%; } +.margin3 { margin-left: 25.0000%; } +.margin4 { margin-left: 33.3333%; } +.margin5 { margin-left: 41.6666%; } +.margin6 { margin-left: 50.0000%; } +.margin7 { margin-left: 58.3333%; } +.margin8 { margin-left: 66.6666%; } +.margin9 { margin-left: 75.0000%; } +.margin10 { margin-left: 83.3333%; } +.margin11 { margin-left: 91.6666%; } +.margin12 { margin-left: 100.0000%; } + /* UI Lists ------------------------------------------------------- */ @@ -128,13 +181,15 @@ ul li { list-style: none;} ul.toggle-list li a { font-weight: bold; + color: #c1c1c1; padding: 10px; - border-top: 1px solid #CCC; + border-top: 1px solid white; display:block; + border-top: 1px solid rgba(0, 0, 0, .5); } -ul.toggle-list li a:hover { - background: #ececec; +ul.toggle-list li a.selected { + color: #333; } ul.toggle-list .icon { @@ -142,20 +197,15 @@ ul.toggle-list .icon { margin-right: 5px; } -a.selected { - color:#222; -} - - ul.link-list li { - float: left; display: inline-block; + float: right; margin-left: 10px; padding-left: 10px; border-left: 1px solid white; } -ul.link-list li:first-child { +ul.link-list li:last-child { border-left: 0; margin-left: 0; padding-left: 0; @@ -163,12 +213,18 @@ ul.link-list li:first-child { /* Utility Classes ------------------------------------------------------- */ +.fillL { + background: white; + color: #333; +} + +.fillL2 { + background: #f7f7f7 url(../img/background-pattern-1.png) repeat; + color: #333; +} -.fillL { background-color: white;} -.fillL2 { background: #f7f7f7 url(../img/background-pattern-1.png) repeat;} .fillD { - background-color: #222222; - background-color: rgba(0,0,0,.8); + background:rgba(0,0,0,.8); color: white; } @@ -181,9 +237,8 @@ form.hide { } .content { - background-color:#fff; border-radius: 4px; - border: 1px solid #ccc; + box-shadow: 0 0 30px 0px rgba(0, 0, 0, 0.7); } .pad1 {padding: 10px;} @@ -195,55 +250,38 @@ form.hide { button { line-height:20px; - border:1px solid #aaa; - box-shadow: inset 0 0 0px 1px #fff; + position: relative; + border:0; color:#222; background: white; font-weight:bold; - font-size:14px; + font-size:12px; display: inline-block; height:40px; cursor:url(../img/cursor-pointer.png) 6 1, auto; + border-radius:4px; } button:hover { background-color: #ececec; } +button.col3:hover { + background: #bde5aa; +} + button.active { - box-shadow: inset 0 0 0px 1px #fff, inset 0 0 6px 1px rgba(0,0,0,.35); cursor:url(../img/cursor-pointing.png) 6 1, auto; } button.active:not([disabled]) { - background-color: #ececec; - box-shadow: inset 0 0 0px 1px #fff, inset 0 0 6px 1px rgba(0,0,0,.25); -} - -button.wide, -button.narrow { - border-radius:4px; -} - -button.wide { - margin: 10px; - width: 100px; -} - -button.add-button { - width: 80px; -} - -button.narrow, -button.Browse { - width:40px; + background: #6bc641; } button.minor { border-radius:4px; height: 20px; width: 20px; - margin: 5px; border: 0; box-shadow: none; background-color: transparent; @@ -259,31 +297,34 @@ button.centered { margin-right: auto; } -.buttons-joined { +.button-wrap { display: inline-block; - margin:10px; -} - -.buttons-joined button { - border-right-width: 0; - border-radius:0; + padding-right:10px; margin: 0; } -.buttons-joined button:first-child { +.button-wrap button:only-child { width: 100%;} +.button-wrap:last-of-type { padding-right: 0;} + +.joined button { + border-right: 1px solid rgba(0,0,0,.5); + border-radius:0; +} + +.joined button:first-child { border-radius:4px 0 0 4px; } -.buttons-joined button:last-child { - border-right-width: 1px; +.joined button:last-child { + border-right-width: 0px; border-radius:0 4px 4px 0; } button.action { - background-color: #7092ff; + background: #7092ff; } button.action:hover { - background-color: #6282ee; + background: #597BE7; } button.delete { @@ -293,13 +334,6 @@ button.delete:hover { background-color: #ef5454; } -button.save { - background-color: #6bc641; - min-width: 120px; - width: auto; - position: relative; -} - button.save.has-count { padding: 9px; } @@ -310,18 +344,31 @@ button.save .count { button.save.has-count .count { display: block; - color: #6bc641; - background: #fff; - border-radius: 0 3px 3px 0; - padding: 9px; - float: right; - margin-left: 10px; - margin-top: -9px; - margin-right: -8px; + position: absolute; + left: 115%; + top: 0; + bottom: 0; + background: rgba(255,255,255,.5); + color: #333; + padding: 10px; + height: 30px; + line-height: 12px; + border-radius: 4px; + margin: auto; } -button.save:hover { - background-color: #59ac33; +button.save.has-count .count::before { + content: ""; + margin: auto; + width: 0; + height: 0; + position: absolute; + left: -6px; + top: 0; + bottom: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-right: 6px solid rgba(255,255,255,.5); } button.close { @@ -330,28 +377,14 @@ button.close { right: 10px; } -button .label { - margin-right: 3px; -} - -button.action .label { - color: white; - text-shadow: 0 -1px 0 rgba(0,0,0,.25); -} - button[disabled] { cursor:auto; - background: white; + background: rgba(255,255,255,.5); pointer-events:none; } button[disabled] .label { - color:#ccc; - text-shadow: none; -} - -button[disabled]:hover { - background: white; + color: rgba(0,0,0,.5); } /* Icons */ @@ -372,17 +405,17 @@ button[disabled]:hover { } .icon.icon-pre-text { - margin-right: 3px; + margin-right: 3px; } /* Definitions for every icon */ +.icon.browse { background-position: 0px -20px;} +.icon.add-point { background-position: -20px -20px;} +.icon.add-line { background-position: -40px -20px;} +.icon.add-area { background-position: -60px -20px;} +.icon.undo { background-position: -80px -20px;} +.icon.redo { background-position: -100px -20px;} -.icon.browse { background-position: 0px 0px;} -.icon.add-point { background-position: -20px 0px;} -.icon.add-line { background-position: -40px 0px;} -.icon.add-area { background-position: -60px 0px;} -.icon.undo { background-position: -80px 0px;} -.icon.redo { background-position: -100px 0px;} .icon.apply { background-position: -120px 0px;} .icon.save { background-position: -140px 0px;} .icon.close { background-position: -160px 0px;} @@ -395,25 +428,17 @@ button[disabled]:hover { .icon.layers { background-position: -300px 0px;} .icon.avatar { background-position: -320px 0px;} .icon.nearby { background-position: -340px 0px;} +.icon.geolocate { background-position: -360px 0px;} -.fillD .icon.browse { background-position: 0px -20px;} -.fillD .icon.add-point { background-position: -20px -20px;} -.fillD .icon.add-line { background-position: -40px -20px;} -.fillD .icon.add-area { background-position: -60px -20px;} -.fillD .icon.undo { background-position: -80px -20px;} -.fillD .icon.redo { background-position: -100px -20px;} -.fillD .icon.apply { background-position: -120px -20px;} -.fillD .icon.save { background-position: -140px -20px;} -.fillD .icon.close { background-position: -160px -20px;} -.fillD .icon.delete { background-position: -180px -20px;} -.fillD .icon.remove { background-position: -200px -20px;} -.fillD .icon.inspect { background-position: -220px -20px;} -.fillD .icon.zoom-in { background-position: -240px -20px;} -.fillD .icon.zoom-out { background-position: -260px -20px;} -.fillD .icon.geocode { background-position: -280px -20px;} -.fillD .icon.layers { background-position: -300px -20px;} -.fillD .icon.avatar { background-position: -320px -20px;} -.fillD .icon.nearby { background-position: -340px -20px;} +.icon.browse { background-position: 0px 0px;} +.icon.add-point { background-position: -20px 0px;} +.icon.add-line { background-position: -40px 0px;} +.icon.add-area { background-position: -60px 0px;} +.icon.undo { background-position: -80px 0px;} +.icon.redo { background-position: -100px 0px;} + +.fillD .icon.avatar { background-position: -320px -20px;} +.fillD .icon.nearby { background-position: -340px -20px;} button[disabled] .icon.browse { background-position: 0px -40px;} button[disabled] .icon.add-point { background-position: -20px -40px;} @@ -440,7 +465,7 @@ button[disabled] .icon.nearby { background-position: -340px -40px;} .icon.big-vertex { background-position: -120px -80px;} .icon.big-inspect { background-position: -160px -80px;} -/* Toggle Icon is special */ +/* Toggle icon is special */ .toggle.icon { background-position: 0px -180px;} a:hover .toggle.icon { background-position: -20px -180px;} .selected .toggle.icon, @@ -450,46 +475,49 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;} ------------------------------------------------------- */ #bar { - border-bottom:1px solid #ccc; position:absolute; left:0px; top:0px; right:0; height:60px; + border-radius: 0; + -webkit-transition: opacity .25s, z-index 0 0s; + -moz-transition: opacity .25s, z-index 0 0s; + transition: opacity .25s, z-index 0 0s; } -/* Special rules for toolbar icons */ - -button.Browse .label { - display: none; - } - -/* Status box */ - -.messages { - display:none; +.mode-add-point #bar, +.mode-add-line #bar, +.mode-draw-line #bar, +.mode-draw-area #bar, +.mode-add-area #bar { + opacity:0; + z-index: -9999; + -webkit-transition: opacity .25s, z-index 0 .5s; + -moz-transition: opacity .25s, z-index 0 .5s; + transition: opacity .25s, z-index 0 .5s; } /* Inspector */ .inspector-wrap { - position:absolute; - right: 0; - border-left: 1px solid #ccc; - border-bottom: 1px solid #ccc; - min-height: 60px; opacity:0; display:none; + padding-left: 10px; + max-width: 500px; +} + +.inspector { + min-height: 60px; + position: relative; + border-radius: 0 0 0 10px; } .inspector-inner { padding: 10px; - min-width:400px; } .inspector-inner.head { - border-bottom: 1px solid #ccc; - background:#fff; z-index:1; position:relative; } @@ -518,8 +546,8 @@ button.Browse .label { } .tag-row { - width: 100%; - padding-right: 70px; + width: 80%; + padding-right: 0px; position: relative; height: 30px; } @@ -560,27 +588,40 @@ button.Browse .label { .tag-row button { position: absolute; - top: 0; - right: 0; + top: 5px; + right: -60px; } .tag-row button.tag-help { - right: 30px; + right: -30px; +} +.inspector-buttons { + border-radius: 0 0 0 10px; + height: 60px; } -.inspector-buttons { - border-top: 1px solid #ccc; +.inspector-buttons .button-wrap { + width: 40%; + padding-right: 0; +} + +.inspector-buttons .minor-buttons { + padding-left: 10px; + line-height: 40px; + width: 60%; } .inspector-inner .add-tag-row { width: 100%; - padding-right: 70px; +} + +.inspector-inner .add-tag-row button { + border-radius: 0 0 4px 4px; } .inspector-inner .add-tag { - width: 50%; + width: 40%; height: 30px; - font-size: 100%; border: 1px solid #ccc; border-top: 0; } @@ -588,16 +629,31 @@ button.Browse .label { /* Map Controls */ .map-control { - left:10px; + left:0px; position:absolute; } +.map-control button { + width: 40px; + background: rgba(0,0,0,.8); + border-radius: 0 4px 4px 0; +} + +.map-control button:hover { + background: rgba(0, 0, 0, .9); +} + +.map-control button.active:hover { + background: #6bc641; +} + .map-overlay { width: 150px; position:absolute; left:50px; top:0; display: block; + border-radius: 4px; } /* Zoomer */ @@ -608,12 +664,13 @@ button.Browse .label { } .zoombuttons button.zoom-in { - border-radius:4px 4px 0 0; + border-radius:0 4px 0 0; + border-bottom: 1px solid rgba(0, 0, 0, .5); } .zoombuttons button.zoom-out { border-top:0; - border-radius:0 0 4px 4px; + border-radius:0 0 4px 0; } /* Layer Switcher */ @@ -622,28 +679,24 @@ button.Browse .label { top:210px; } -.layerswitcher-control .adjustments { - padding:5px; - opacity:0.2; -} - -.layerswitcher-control .adjustments:hover { - opacity:1; -} - -.layerswitcher-control .adjustments .reset { +.layerswitcher-control .adjustments button { + opacity:0.5; height:20px; font-size:10px; font-weight:normal; - padding:0 5px; + padding:0 5px 3px 5px; + background: white; + border: 1px solid #ddd; + border-radius: 0; +} + +.layerswitcher-control .adjustments button:hover { + opacity: 1; } .layerswitcher-control .nudge { - height:20px; width:20px; - font-size:10px; margin-right:2px; - font-weight:normal; } .opacity-options-wrapper { @@ -651,7 +704,6 @@ button.Browse .label { } .opacity-options { - border:1px solid #b0b0b0; background: url(../img/background-pattern-opacity.png) 0 0 repeat; height:20px; width:62px; @@ -670,14 +722,14 @@ button.Browse .label { .opacity-options li .select-box{ position: absolute; width:20px; - height:18px; + height:20px; z-index: 9999; } .layerswitcher-control li:hover .select-box, .layerswitcher-control li.selected .select-box { - border: 2px solid #4672ff; - background: rgba(70, 114, 255, .5); + border: 2px solid #6bc641; + background: rgba(107, 198, 65, .5); opacity: .5; } .layerswitcher-control li.selected:hover .select-box, @@ -689,7 +741,7 @@ button.Browse .label { background:#222; display:inline-block; width:20px; - height:18px; + height:20px; } /* Geocoder */ @@ -715,7 +767,7 @@ button.Browse .label { display:block; position:absolute; overflow:hidden; - top:60px; + top:0px; left:0; right:0; bottom:0; @@ -740,29 +792,18 @@ img.tile { ------------------------------------------------------- */ .about-block { - float: right; height: 40px; -} - -#about { - border-radius:3px 0 0 0; -} - -#attrib-container { position: absolute; right:0px; bottom:0px; -} - -#user-list { - margin-right: 8px; - border-radius:3px 3px 0 0; - max-width: 400px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + border-radius: 0; } +#about { text-align: right;} + #user-list a:not(:last-child):after { content: ', '; } @@ -770,10 +811,7 @@ img.tile { /* Account Information */ .user-container { - position:absolute; - left:0px; - bottom:0px; - border-radius:0 3px 0 0; + float: left; } .user-container .logout { @@ -826,6 +864,7 @@ div.typeahead a:first-child { position:absolute; width: 50%; left: 25%; + max-width: 600px; top:80px; z-index: 3; } @@ -840,15 +879,16 @@ div.typeahead a:first-child { text-align: center; } -.modal button { margin-bottom: 0;} -.modal button:first-child { margin-left: 0;} - .modal button.close-modal { float:right; - margin-right:10px; - margin-top:10px; + position: absolute; + right:5px; + top:5px; border:0; } +.modal button.close-modal:hover { + background-color: transparent; +} .shaded { z-index: 2; @@ -893,10 +933,20 @@ div.typeahead a:first-child { .modal-section { padding: 20px; - border-bottom: 1px solid #ccc; } -.modal-section:last-child { border-bottom: 0;} +.modal-section.header { + border-radius: 4px 4px 0 0; +} + +.modal-section:last-child { + border-radius: 0 0 4px 4px; +} + +.modal-section .buttons { + padding-top: 10px; + width: 100%; +} .modal-section img.wiki-image { max-width: 400px; @@ -955,18 +1005,16 @@ div.typeahead a:first-child { .tooltip { white-space: normal; position: absolute; + left: 0; right: 0; margin: auto; z-index: -1000; height: 0; padding: 5px; opacity: 0; display: block; - filter: alpha(opacity=0); - visibility: visible; } .tooltip.in { opacity: 0.8; - filter: alpha(opacity=80); z-index: 1030; height: auto; } @@ -988,16 +1036,14 @@ div.typeahead a:first-child { } .tooltip-inner { - max-width: 200px; - min-width: 100px; + text-align: left; + width: 200px; font-size: 11px; font-weight: bold; line-height: 20px; padding: 5px 10px; - color: #ffffff; - text-align: center; - text-decoration: none; - background-color: #000000; + color: #333; + background-color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; @@ -1015,7 +1061,7 @@ div.typeahead a:first-child { bottom: 0; left: 50%; margin-left: -5px; - border-top-color: #000000; + border-top-color: white; border-width: 5px 5px 0; } @@ -1023,7 +1069,7 @@ div.typeahead a:first-child { top: 50%; left: 0; margin-top: -5px; - border-right-color: #000000; + border-right-color: white; border-width: 5px 5px 5px 0; } @@ -1031,7 +1077,7 @@ div.typeahead a:first-child { top: 50%; right: 0; margin-top: -5px; - border-left-color: #000000; + border-left-color: white; border-width: 5px 0 5px 5px; } @@ -1039,7 +1085,7 @@ div.typeahead a:first-child { top: 0; left: 50%; margin-left: -5px; - border-bottom-color: #000000; + border-bottom-color: white; border-width: 0 5px 5px; } @@ -1058,3 +1104,13 @@ div.typeahead a:first-child { -moz-border-radius: 4px; border-radius: 4px; } + +/* Media Queries +------------------------------------------------------- */ + +@media only screen and (max-width: 840px) { + span.label {display: none;} + /* override hide for save button */ + .icon.icon-pre-text { margin-right: 0px;} + .save .label { display: block;} +} diff --git a/css/map.css b/css/map.css index 5d529ea55..833ff39b6 100644 --- a/css/map.css +++ b/css/map.css @@ -7,7 +7,7 @@ g.point circle { fill:#fff; } -g.point.hover circle.stroke, +g.pointer circle.stroke, g.point.selected circle.stroke { fill:#333; -webkit-transform:scale(1.2, 1.2); @@ -15,131 +15,150 @@ g.point.selected circle.stroke { transform:scale(1.2, 1.2); } -/* interactive elements */ -g.vertex circle.fill { +/* vertices */ + +g.vertex .fill { + fill:white; +} +g.vertex .stroke { + stroke:#333; + stroke-width:2; fill:white; - fill-opacity:1; } -circle.stroke, -circle.fill { - -webkit-transition: -webkit-transform 50ms linear; - transition: transform 50ms linear; - -moz-transition: stroke 50ms linear; - -webkit-transform:scale(1, 1); - -moz-transform:scale(1, 1); - transform:scale(1, 1); +svg[data-zoom="16"] g.vertex .shadow { + -webkit-transform:scale(0.8, 0.8); + -moz-transform:scale(0.8, 0.8); + transform:scale(0.8, 0.8); } - -svg[data-zoom="16"] g.vertex circle.stroke, -svg[data-zoom="16"] g.vertex circle.fill { +svg[data-zoom="16"] g.vertex .stroke, +svg[data-zoom="16"] g.vertex .fill { -webkit-transform:scale(0.6, 0.6); -moz-transform:scale(0.6, 0.6); transform:scale(0.6, 0.6); } -svg[data-zoom="17"] g.vertex circle.stroke, -svg[data-zoom="17"] g.vertex circle.fill { +svg[data-zoom="17"] g.vertex .shadow { + -webkit-transform:scale(0.9, 0.9); + -moz-transform:scale(0.9, 0.9); + transform:scale(0.9, 0.9); +} +svg[data-zoom="17"] g.vertex .stroke, +svg[data-zoom="17"] g.vertex .fill { -webkit-transform:scale(0.7, 0.7); -moz-transform:scale(0.7, 0.7); transform:scale(0.7, 0.7); } -path.casing.tag-highway { - stroke-width:10; +g.vertex.shared .shadow { + -webkit-transform:scale(1.2, 1.2); + -moz-transform:scale(1.2, 1.2); + transform:scale(1.2, 1.2); +} +g.vertex.shared .fill, +g.vertex.shared .stroke { + -webkit-transform:scale(1.1, 1.1); + -moz-transform:scale(1.1, 1.1); + transform:scale(1.1, 1.1); +} +svg[data-zoom="16"] g.vertex.shared .shadow { + -webkit-transform:scale(0.9, 0.9); + -moz-transform:scale(0.9, 0.9); + transform:scale(0.9, 0.9); +} +svg[data-zoom="16"] g.vertex.shared .fill, +svg[data-zoom="16"] g.vertex.shared .stroke { + -webkit-transform:scale(0.8, 0.8); + -moz-transform:scale(0.8, 0.8); + transform:scale(0.8, 0.8); +} +svg[data-zoom="17"] g.vertex.shared .shadow { + -webkit-transform:scale(1, 1); + -moz-transform:scale(1, 1); + transform:scale(1, 1); +} +svg[data-zoom="17"] g.vertex.shared .fill, +svg[data-zoom="17"] g.vertex.shared .stroke { + -webkit-transform:scale(0.9, 0.9); + -moz-transform:scale(0.9, 0.9); + transform:scale(0.9, 0.9); } -path.stroke.tag-highway { - stroke-width:8; -} - -svg[data-zoom="16"] path.casing.tag-highway { - stroke-width:6; -} - -svg[data-zoom="16"] path.stroke.tag-highway { - stroke-width:4; -} - -g.vertex circle.stroke { - fill:#333; -} - -g.vertex.shared circle.fill { - fill:#aff; -} -g.vertex.shared circle.stroke { - fill:#044; -} - -g.vertex.hover circle.fill { - -webkit-transform:scale(1.5, 1.5); - -moz-transform:scale(1.5, 1.5); - transform:scale(1.5, 1.5); -} - -g.vertex.hover circle.stroke { - -webkit-transform:scale(1.4, 1.4); - -moz-transform:scale(1.4, 1.4); - transform:scale(1.4, 1.4); -} - -g.vertex circle.selected.fill { - fill: #ffff00; -} -g.vertex circle.selected.stroke { - fill: #38380A; -} - -circle.midpoint { +g.vertex.shared .fill { fill:#aaa; - stroke:#333; - fill-opacity:1; - stroke-width:1; } -circle.teaser-point { - stroke-width: 2; - stroke:#1DCAFF; - fill:#D3F5FF; +g.vertex .shadow { + fill: none; + pointer-events: all; + stroke-width: 10; + -webkit-transition: -webkit-transform 100ms linear; + transition: transform 100ms linear; + -moz-transition: fill 100ms linear; +} +g.vertex.hover .shadow { + fill: #E96666; + fill-opacity: 0.3; +} +g.vertex.selected .shadow { + fill: #E96666; + fill-opacity: 0.7; } -path.casing { - stroke: #111; - stroke-linecap:round; - stroke-linejoin:bevel; - stroke-width: 3; - -webkit-transition: stroke 100ms linear; +/* midpoints */ + +g.midpoint .fill { + fill:#aaa; +} +g.midpoint .fill.hover { + fill:#fff; + stroke:#000; } -path.casing.hover { - stroke:#FF0F0F !important; - stroke-opacity:0.8; +g.midpoint .shadow { + fill: none; + pointer-events: all; + stroke-width: 10; + -webkit-transition: -webkit-transform 100ms linear; + transition: transform 100ms linear; + -moz-transition: fill 100ms linear; +} +g.midpoint .shadow.hover { + fill:#E96666; + fill-opacity: 0.3; } -path.casing.selected { - stroke:#E96666 !important; - stroke-opacity:1 !important; - stroke-width:10 !important; +/* lines */ + +path.line { + stroke-linecap: round; + stroke-linejoin: bevel; } path.stroke { - stroke: #555; - stroke-linecap:round; - stroke-linejoin:bevel; + stroke: #222; stroke-width: 2; } -path.stroke.tag-railway-rail { - stroke: white; - stroke-width: 3; - stroke-dasharray: 12,12; +path.stroke, +path.casing { + shape-rendering: optimizeSpeed; } -path.stroke.tag-railway-subway { - stroke: #444; - stroke-width: 3; - stroke-dasharray: 8,8; +path.shadow { + pointer-events: stroke; + stroke-width: 10; + -webkit-transition: stroke 100ms linear; +} + +path.shadow.hover { + stroke: #E96666; + stroke-opacity: 0.3; +} + +path.shadow.selected { + stroke: #E96666; + stroke-opacity: 0.7; } path.area, @@ -206,81 +225,209 @@ path.multipolygon.tag-amenity-parking { } /* highways */ -path.stroke.tag-highway-residential { - stroke:#fff; + +path.shadow.tag-highway { + stroke-width:16; } -path.casing.tag-highway-residential { +path.casing.tag-highway { stroke:#444; + stroke-width:10; +} +path.stroke.tag-highway { + stroke:#ccc; + stroke-width:8; +} + +svg[data-zoom="16"] path.shadow.tag-highway { + stroke-width:12; +} +svg[data-zoom="16"] path.casing.tag-highway { + stroke-width:6; +} +svg[data-zoom="16"] path.stroke.tag-highway { + stroke-width:4; +} + +path.stroke.tag-highway-motorway, +path.stroke.tag-highway-motorway_link, +path.stroke.tag-construction-motorway { + stroke:#809bc0; +} +path.casing.tag-highway-motorway, +path.casing.tag-highway-motorway_link, +path.casing.tag-construction-motorway { + stroke:#506077; +} + +path.stroke.tag-highway-trunk, +path.stroke.tag-highway-trunk_link, +path.stroke.tag-construction-trunk { + stroke:#97d397; +} +path.casing.tag-highway-trunk, +path.casing.tag-highway-trunk_link, +path.casing.tag-construction-trunk { + stroke:#477147; +} + +path.stroke.tag-highway-primary, +path.stroke.tag-highway-primary_link, +path.stroke.tag-construction-primary { + stroke:#ec989a; +} +path.casing.tag-highway-primary, +path.casing.tag-highway-primary_link, +path.casing.tag-construction-primary { + stroke:#8d4346; +} + +path.stroke.tag-highway-secondary, +path.stroke.tag-highway-secondary_link, +path.stroke.tag-construction-secondary { + stroke:#fecc8b; +} +path.casing.tag-highway-secondary, +path.casing.tag-highway-secondary_link, +path.casing.tag-construction-secondary { + stroke:#a37b48; +} + +path.stroke.tag-highway-tertiary, +path.stroke.tag-highway-tertiary_link, +path.stroke.tag-construction-tertiary { + stroke:#ffffb3; +} +path.casing.tag-highway-tertiary, +path.casing.tag-highway-tertiary_link, +path.casing.tag-construction-tertiary { + stroke:#bbb; } path.stroke.tag-highway-unclassified, -path.stroke.tag-highway-tertiary, -path.stroke.tag-highway-tertiary_link { - stroke:#FEFECB; +path.stroke.tag-construction-unclassified { + stroke:#eaeaea; +} +path.casing.tag-highway-unclassified, +path.casing.tag-construction-unclassified { + stroke:#444; } -path.casing.tag-highway-unclassified, -path.casing.tag-highway-tertiary, -path.casing.tag-highway-tertiary_link { } +path.stroke.tag-highway-residential, +path.stroke.tag-construction-residential { + stroke:#fff; +} +path.casing.tag-highway-residential, +path.casing.tag-construction-residential { + stroke:#444; +} -path.stroke.tag-highway-service { +path.stroke.tag-highway-living_street { + stroke:#ccc; + stroke-width:4; +} +path.casing.tag-highway-living_street { stroke:#fff; stroke-width:6; } + +path.stroke.tag-highway-pedestrian { + stroke:#fff; + stroke-dasharray: 2, 8; + stroke-width:4 !important; + shapeRendering: auto; +} +path.casing.tag-highway-pedestrian { + stroke:#84C382; + stroke-width:6 !important; +} + +path.stroke.tag-highway-service { + stroke:#fff; + stroke-width:4; +} path.casing.tag-highway-service { - stroke-width:8; + stroke:#666; + stroke-width:6; +} +svg[data-zoom="16"] path.stroke.tag-highway-service { + stroke-width:2; +} +svg[data-zoom="16"] path.casing.tag-highway-service { + stroke-width:4; +} + +path.stroke.tag-highway-track { + stroke: #fff; + stroke-width: 4; +} +path.casing.tag-highway-track { + stroke: #996600; + stroke-width: 6; + stroke-linecap: butt; + stroke-dasharray: 6, 6; +} +svg[data-zoom="16"] path.stroke.tag-highway-track { + stroke-width:2; +} +svg[data-zoom="16"] path.casing.tag-highway-track { + stroke-width:4; +} + +path.stroke.tag-highway-path { + stroke: #000; + stroke-width: 1 !important; + stroke-linecap: butt; + stroke-dasharray: 8, 4; +} +path.casing.tag-highway-path { + stroke-width: 1 !important; + stroke: #fff; } path.stroke.tag-highway-footway, path.stroke.tag-highway-cycleway, -path.stroke.tag-highway-steps { - stroke: #ff6257; +path.stroke.tag-highway-bridleway { stroke-width: 4; + stroke-linecap: butt; stroke-dasharray: 6, 6; } path.casing.tag-highway-footway, path.casing.tag-highway-cycleway, -path.casing.tag-highway-steps { +path.casing.tag-highway-bridleway { stroke-width: 6; stroke: #fff; } -path.stroke.tag-highway-motorway, -path.stroke.tag-highway-motorway_link { - stroke:#809BC0; +svg[data-zoom="16"] path.stroke.tag-highway-footway, +svg[data-zoom="16"] path.stroke.tag-highway-cycleway, +svg[data-zoom="16"] path.stroke.tag-highway-bridleway { + stroke-width: 2; } -path.casing.tag-highway-motorway, -path.casing.tag-highway-motorway_link { - stroke:#809BC0; - stroke-opacity:0.4; +svg[data-zoom="16"] path.casing.tag-highway-footway, +svg[data-zoom="16"] path.casing.tag-highway-cycleway, +svg[data-zoom="16"] path.casing.tag-highway-bridleway { + stroke-width: 4; } -path.stroke.tag-highway-trunk, -path.stroke.tag-highway-trunk_link { - stroke-opacity:0.4; - stroke:#7FC97F; +path.stroke.tag-highway-footway { + stroke: #996600; } -path.casing.tag-highway-trunk, -path.casing.tag-highway-trunk_link { - stroke:#7FC97F; +path.stroke.tag-highway-cycleway { + stroke: #69f; +} +path.stroke.tag-highway-bridleway { + stroke: green; } -path.stroke.tag-highway-primary, -path.stroke.tag-highway-primary_link { - stroke:#ec989a; +path.stroke.tag-highway-steps { + stroke: #ff6257; + stroke-width: 4; + stroke-linecap: butt; + stroke-dasharray: 3, 3; } -path.casing.tag-highway-primary, -path.casing.tag-highway-primary_link { - stroke:#681212; -} - -path.stroke.tag-highway-secondary, -path.stroke.tag-highway-secondary_link { - stroke:#FDBF6F; -} -path.casing.tag-highway-secondary, -path.casing.tag-highway-secondary_link { - stroke:#444; +path.casing.tag-highway-steps { + stroke-width: 6; + stroke: #fff; } path.casing.tag-bridge-yes { @@ -288,11 +435,108 @@ path.casing.tag-bridge-yes { stroke: #000; } +path.stroke.tag-highway-construction, +path.casing.tag-highway-construction { + stroke-linecap: butt; + stroke-dasharray: 7, 7; +} + +svg[data-zoom="16"] path.stroke.tag-highway-construction, +svg[data-zoom="16"] path.casing.tag-highway-construction { + stroke-linecap: butt; + stroke-dasharray: 5, 5; +} + +/* railways */ + +path.stroke.tag-railway { + stroke: #eee; + stroke-width: 2; + stroke-linecap: butt; + stroke-dasharray: 12,12; +} +path.casing.tag-railway { + stroke: #555; + stroke-width: 4; +} + +path.stroke.tag-railway-abandoned { + stroke: #eee; +} +path.casing.tag-railway-abandoned { + stroke: #999; +} + +path.stroke.tag-railway-subway { + stroke: #666; +} +path.casing.tag-railway-subway { + stroke: #222; +} + +/* waterways */ + path.stroke.tag-waterway { stroke: #10539a; + stroke-width: 2; +} +path.casing.tag-waterway { + stroke: #6AA2FF; + stroke-width: 4; +} + +path.stroke.tag-waterway-river { + stroke-width: 4; +} +path.casing.tag-waterway-river { + stroke-width: 6; +} + +svg[data-zoom="16"] path.stroke.tag-waterway-river { + stroke-width: 4; +} +svg[data-zoom="16"] path.casing.tag-waterway-river { + stroke-width: 6; +} + +path.stroke.tag-waterway-ditch { + stroke: #10539a; + stroke-width: 1; +} +path.casing.tag-waterway-ditch { + stroke: #999692; stroke-width: 3; } +/* power */ + +path.stroke.tag-power { + stroke: #939393; + stroke-width: 2; +} +path.casing.tag-power { + stroke: none; +} + +/* boundary */ + +path.stroke.tag-boundary { + stroke: #fff; + stroke-width: 2; + stroke-linecap: butt; + stroke-dasharray: 20, 5, 5, 5; +} +path.casing.tag-boundary { + stroke: #82B5FE; + stroke-width: 6; +} + +path.casing.tag-boundary-protected_area, +path.casing.tag-boundary-national_park { + stroke: #4D9849; +} + + text { font-size:10px; pointer-events: none; diff --git a/img/source/sprite.svg b/img/source/sprite.svg index f239c672f..537a84226 100644 --- a/img/source/sprite.svg +++ b/img/source/sprite.svg @@ -9,11 +9,11 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="360" + width="380" height="200" id="svg12393" version="1.1" - inkscape:version="0.48.2 r9819" + inkscape:version="0.48.1 r9760" sodipodi:docname="sprite.svg" inkscape:export-filename="/Users/saman/work_repos/iD/img/sprite.png" inkscape:export-xdpi="90" @@ -38,9 +38,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="150.66428" - inkscape:cy="90.493266" + inkscape:zoom="2.8284271" + inkscape:cx="173.1037" + inkscape:cy="123.12989" inkscape:document-units="px" inkscape:current-layer="layer12" showgrid="false" @@ -56,7 +56,7 @@ showguides="false" inkscape:guide-bbox="true" inkscape:snap-bbox="true" - inkscape:snap-nodes="true"> + inkscape:snap-nodes="false"> + + @@ -175,110 +183,15 @@ id="layer1" transform="translate(-25,-62.362183)" style="display:inline"> - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + + + - + transform="translate(-25,3.0625e-6)"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> @@ -935,26 +499,6 @@ inkscape:export-xdpi="90" inkscape:export-ydpi="90" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="fill:#ffffff;fill-opacity:1;display:inline"> + style="fill:#ffffff;fill-opacity:1;display:inline"> - - - - - - - - - - - - - - - - - - @@ -1260,30 +583,12 @@ - - - - + + + + + + + + + + + + + + + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" + d="m 90,44 -1,1 0,2 1,1 0,4 -1,1 0,2 1,1 2,0 1,-1 4,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-4 1,-1 0,-2 -1,-1 -2,0 -1,1 -4,0 -1,-1 -2,0 z m 1,1 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m 8,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m -6,2 4,0 1,1 0,4 -1,1 -4,0 -1,-1 0,-4 1,-1 z m -2,6 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m 8,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z" + id="path16225" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" + d="m 79,43.000003 -1,1 0,1.59375 -6.40625,6.40625 -1.59375,0 -1,1 0,2 1,1 2,0 1,-1 0,-1.59375 6.40625,-6.40625 1.59375,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.55228,0 1,0.44772 1,1 0,0.55229 -0.44772,1 -1,1 -0.25152,0 -0.48052,-0.0967 -0.65625,-0.25 -0.0344,-0.03002 -0.0637,-0.05934 -0.0937,-0.09375 C 79.0967,45.480522 79,45.251524 79,45.000003 c 0,-0.55228 0.44772,-1 1,-1 z m -9,9 c 0.25152,0 0.48052,0.0967 0.65625,0.25 l 0.0937,0.09375 C 71.9033,53.519484 72,53.748487 72,54.000003 c 0,0.55229 -0.44772,1 -1,1 -0.55228,0 -1,-0.44771 -1,-1 0,-0.55228 0.44772,-1 1,-1 z" + id="path16227" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/sprite.png b/img/sprite.png index 2f933d9f8..e3037eebd 100644 Binary files a/img/sprite.png and b/img/sprite.png differ diff --git a/index.html b/index.html index bbefb2c04..318bc54a8 100644 --- a/index.html +++ b/index.html @@ -73,6 +73,7 @@ + diff --git a/js/id/actions/add_relation_member.js b/js/id/actions/add_relation_member.js new file mode 100644 index 000000000..45e24a0b0 --- /dev/null +++ b/js/id/actions/add_relation_member.js @@ -0,0 +1,9 @@ +iD.actions.AddRelationMember = function(relationId, member, index) { + return function(graph) { + var relation = graph.entity(relationId), + members = relation.members.slice(); + + members.splice((index === undefined) ? members.length : index, 0, member); + return graph.replace(relation.update({members: members})); + }; +}; diff --git a/js/id/actions/reverse_way.js b/js/id/actions/reverse_way.js index 37ac53f10..a845fe844 100644 --- a/js/id/actions/reverse_way.js +++ b/js/id/actions/reverse_way.js @@ -65,7 +65,7 @@ iD.actions.ReverseWay = function(wayId) { graph.parentRelations(way).forEach(function (relation) { relation.members.forEach(function (member, index) { if (member.id === way.id && (role = {forward: 'backward', backward: 'forward'}[member.role])) { - graph = iD.actions.UpdateRelationMember(relation.id, index, {role: role})(graph); + graph = iD.actions.UpdateRelationMember(relation.id, {role: role}, index)(graph); } }); }); diff --git a/js/id/actions/split_way.js b/js/id/actions/split_way.js index 6682ab638..de399df80 100644 --- a/js/id/actions/split_way.js +++ b/js/id/actions/split_way.js @@ -1,5 +1,13 @@ -// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as -iD.actions.SplitWay = function(nodeId) { +// Split a way at the given node. +// +// For testing convenience, accepts an ID to assign to the new way. +// Normally, this will be undefined and the way will automatically +// be assigned a new ID. +// +// Reference: +// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as +// +iD.actions.SplitWay = function(nodeId, newWayId) { return function(graph) { var node = graph.entity(nodeId), parents = graph.parentWays(node); @@ -7,38 +15,44 @@ iD.actions.SplitWay = function(nodeId) { // splitting ways at intersections TODO if (parents.length !== 1) return graph; - var way = parents[0]; - - var idx = _.indexOf(way.nodes, nodeId); + var way = parents[0], + idx = _.indexOf(way.nodes, nodeId); // Create a 'b' way that contains all of the tags in the second // half of this way - var newWay = iD.Way({ tags: _.clone(way.tags), nodes: way.nodes.slice(idx) }); + var newWay = iD.Way({id: newWayId, tags: way.tags, nodes: way.nodes.slice(idx)}); graph = graph.replace(newWay); // Reduce the original way to only contain the first set of nodes - graph = graph.replace(way.update({ nodes: way.nodes.slice(0, idx + 1) }), 'changed way direction'); + graph = graph.replace(way.update({nodes: way.nodes.slice(0, idx + 1)})); - var parentRelations = graph.parentRelations(way); - - function isVia(x) { return x.role = 'via'; } - function isSelf(x) { return x.id = way.id; } - - parentRelations.forEach(function(relation) { - if (relation.tags.type === 'restriction') { - var via = _.find(relation.members, isVia); - var ownrole = _.find(relation.members, isSelf).role; - if (via && !_.contains(newWay.nodes, via.id)) { - // the new way doesn't contain the node that's important - // to the turn restriction, so we don't need to worry - // about adding it to the turn restriction. - } else { - graph = graph.replace(iD.actions.AddRelationMember(relation.id, { - role: ownrole, - id: newWay.id, - type: 'way' - })); + graph.parentRelations(way).forEach(function(relation) { + if (relation.isRestriction()) { + var via = relation.memberByRole('via'); + if (via && newWay.contains(via.id)) { + graph = iD.actions.UpdateRelationMember( + relation.id, + {id: newWay.id}, + relation.memberById(way.id).index + )(graph); } + } else { + var role = relation.memberById(way.id).role, + last = newWay.last(), + i = relation.memberById(way.id).index, + j; + + for (j = 0; j < relation.members.length; j++) { + if (relation.members[j].type === 'way' && graph.entity(relation.members[j].id).contains(last)) { + break; + } + } + + graph = iD.actions.AddRelationMember( + relation.id, + {id: newWay.id, type: 'way', role: role}, + i <= j ? i + 1 : i + )(graph); } }); diff --git a/js/id/actions/update_relation_member.js b/js/id/actions/update_relation_member.js index 67a45b6ab..29eb79afe 100644 --- a/js/id/actions/update_relation_member.js +++ b/js/id/actions/update_relation_member.js @@ -1,4 +1,4 @@ -iD.actions.UpdateRelationMember = function(relationId, index, properties) { +iD.actions.UpdateRelationMember = function(relationId, properties, index) { return function(graph) { var relation = graph.entity(relationId), members = relation.members.slice(); diff --git a/js/id/graph/graph.js b/js/id/graph/graph.js index 74caf7ceb..0d533d461 100644 --- a/js/id/graph/graph.js +++ b/js/id/graph/graph.js @@ -136,13 +136,20 @@ iD.Graph.prototype = { entity = this.entities[id]; oldentity = graph.entities[id]; if (entity !== oldentity) { - if (entity && entity.type === 'way') { - result = oldentity ? - result + + if (entity && entity.type === 'way' && + oldentity && oldentity.type === 'way') { + result = result .concat(_.difference(entity.nodes, oldentity.nodes)) .concat(_.difference(oldentity.nodes, entity.nodes)) - : result.concat(entity.nodes); + + } else if (entity && entity.type === 'way') { + result = result.concat(entity.nodes); + + } else if (oldentity && oldentity.type === 'way') { + result = result.concat(oldentity.nodes); } + result.push(id); } } diff --git a/js/id/graph/history.js b/js/id/graph/history.js index e31fa5ef9..3d027d042 100644 --- a/js/id/graph/history.js +++ b/js/id/graph/history.js @@ -109,6 +109,16 @@ iD.History = function() { }; }, + hasChanges: function() { + return !!this.numChanges(); + }, + + numChanges: function() { + return d3.sum(d3.values(this.changes()).map(function(c) { + return c.length; + })); + }, + imagery_used: function(source) { if (source) imagery_used = source; else return _.without( diff --git a/js/id/graph/relation.js b/js/id/graph/relation.js index 7d8a9aa89..dc298ecbd 100644 --- a/js/id/graph/relation.js +++ b/js/id/graph/relation.js @@ -18,6 +18,30 @@ iD.Relation = iD.Entity.extend({ return 'relation'; }, + // Return the first member with the given role. A copy of the member object + // is returned, extended with an 'index' property whose value is the member index. + memberByRole: function(role) { + for (var i = 0; i < this.members.length; i++) { + if (this.members[i].role === role) { + return _.extend({}, this.members[i], {index: i}); + } + } + }, + + // Return the first member with the given id. A copy of the member object + // is returned, extended with an 'index' property whose value is the member index. + memberById: function(id) { + for (var i = 0; i < this.members.length; i++) { + if (this.members[i].id === id) { + return _.extend({}, this.members[i], {index: i}); + } + } + }, + + isRestriction: function() { + return !!(this.tags.type && this.tags.type.match(/^restriction:?/)); + }, + // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm], // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings. // diff --git a/js/id/graph/way.js b/js/id/graph/way.js index 305f12051..9efd4fe6c 100644 --- a/js/id/graph/way.js +++ b/js/id/graph/way.js @@ -14,12 +14,24 @@ iD.Way = iD.Entity.extend({ }); }, + first: function() { + return this.nodes[0]; + }, + + last: function() { + return this.nodes[this.nodes.length - 1]; + }, + + contains: function(node) { + return this.nodes.indexOf(node) >= 0; + }, + isOneWay: function() { return this.tags.oneway === 'yes'; }, isClosed: function() { - return this.nodes.length > 0 && this.nodes[this.nodes.length - 1] === this.nodes[0]; + return this.nodes.length > 0 && this.first() === this.last(); }, // a way is an area if: diff --git a/js/id/id.js b/js/id/id.js index afcdfc3e4..4d3948107 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -22,15 +22,19 @@ window.iD = function(container) { .call(map); var bar = container.append('div') - .attr('id', 'bar').attr('class', 'fillL2'); + .attr('id', 'bar') + .attr('class','pad1 fillD'); - var buttons_joined = bar.append('div') - .attr('class', 'buttons-joined'); + var limiter = bar.append('div') + .attr('class', 'limiter'); + + var buttons_joined = limiter.append('div') + .attr('class', 'button-wrap joined col4'); var buttons = buttons_joined.selectAll('button.add-button') .data([iD.modes.Browse(), iD.modes.AddPoint(), iD.modes.AddLine(), iD.modes.AddArea()]) .enter().append('button') - .attr('class', function (mode) { return mode.title + ' add-button'; }) + .attr('class', function (mode) { return mode.title + ' add-button col3'; }) .attr('data-original-title', function (mode) { return mode.description; }) .call(bootstrap.tooltip().placement('bottom')) .on('click', function (mode) { controller.enter(mode); }); @@ -46,7 +50,7 @@ window.iD = function(container) { } } - notice = iD.ui.notice(bar + var notice = iD.ui.notice(limiter .append('div') .attr('class', 'notice')); @@ -71,55 +75,41 @@ window.iD = function(container) { container.classed("mode-" + exited.id, false); }); - var undo_buttons = bar.append('div') - .attr('class', 'buttons-joined'), + var undo_buttons = limiter.append('div') + .attr('class', 'button-wrap joined col1'), undo_tooltip = bootstrap.tooltip().placement('bottom'); undo_buttons.append('button') - .attr({ id: 'undo', 'class': 'narrow' }) + .attr({ id: 'undo', 'class': 'col6' }) .property('disabled', true) .html("") .on('click', history.undo) .call(undo_tooltip); undo_buttons.append('button') - .attr({ id: 'redo', 'class': 'narrow' }) + .attr({ id: 'redo', 'class': 'col6' }) .property('disabled', true) .html("") .on('click', history.redo) .call(undo_tooltip); - container.append('div') - .attr('class', 'user-container pad1 fillD about-block') - .append('div') - .attr('class', 'hello'); - - var save_button = bar.append('button') - .attr('class', 'save action wide') + var save_button = limiter.append('div').attr('class','button-wrap col1').append('button') + .attr('class', 'save col12') .call(iD.ui.save().map(map).controller(controller)); history.on('change.warn-unload', function() { - var changes = history.changes(), - - has_changes = !!d3.sum(d3.values(changes).map(function(c) { - return c.length; - })); - - window.onbeforeunload = has_changes ? function() { + window.onbeforeunload = history.hasChanges() ? function() { return 'You have unsaved changes.'; } : null; }); - bar.append('div') - .attr('class', 'messages'); - var zoom = container.append('div') .attr('class', 'zoombuttons map-control') .selectAll('button') .data([['zoom-in', '+', map.zoomIn, 'Zoom In'], ['zoom-out', '-', map.zoomOut, 'Zoom Out']]) .enter() .append('button') - .attr('class', function(d) { return d[0] + ' narrow'; }) + .attr('class', function(d) { return d[0]; }) .attr('title', function(d) { return d[3]; }) .on('click', function(d) { return d[2](); }) .append('span') @@ -139,21 +129,27 @@ window.iD = function(container) { .call(iD.ui.layerswitcher(map)); container.append('div') - .attr('class', 'inspector-wrap fillL') - .style('display', 'none'); + .style('display', 'none') + .attr('class', 'inspector-wrap fr col5'); - var about = container.append('div').attr('id', 'attrib-container'); + var about = container.append('div') + .attr('class','col12 about-block fillD pad1') - about.append('ul') - .attr('id','about') - .attr('class','pad1 fillD about-block link-list') - .html("
  • view code
  • " + + about.append('div') + .attr('class', 'user-container') + .append('div') + .attr('class', 'hello'); + + var aboutList = about.append('ul') + .attr('id','about') + .attr('class','link-list'); + + aboutList.html("
  • view code
  • " + "
  • report a bug
  • " + "
  • imagery provided by bing
  • "); - var contributors = about.append('div') + var contributors = aboutList.append('li') .attr('id', 'user-list') - .attr('class','about-block fillD pad1'); contributors.append('span') .attr('class', 'icon nearby icon-pre-text'); contributors.append('span') @@ -173,12 +169,12 @@ window.iD = function(container) { } } - bar.select('#undo') + limiter.select('#undo') .property('disabled', !undo) .attr('data-original-title', undo) .call(undo ? refreshTooltip : undo_tooltip.hide); - bar.select('#redo') + limiter.select('#redo') .property('disabled', !redo) .attr('data-original-title', redo) .call(redo ? refreshTooltip : undo_tooltip.hide); diff --git a/js/id/modes/browse.js b/js/id/modes/browse.js index c57b503f5..4b2ea448f 100644 --- a/js/id/modes/browse.js +++ b/js/id/modes/browse.js @@ -2,7 +2,7 @@ iD.modes.Browse = function() { var mode = { button: 'browse', id: 'browse', - title: 'Browse', + title: 'Move', description: 'Pan and zoom the map' }; diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js index cdb966351..196a4de53 100644 --- a/js/id/modes/draw_area.js +++ b/js/id/modes/draw_area.js @@ -13,7 +13,7 @@ iD.modes.DrawArea = function(wayId) { headId = (way.nodes.length == 1) ? way.nodes[0] : way.nodes[way.nodes.length - 2], - tailId = _.first(way.nodes), + tailId = way.first(), node = iD.Node({loc: map.mouseCoordinates()}); map.dblclickEnable(false) diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js index 68130d7b8..72a85ae41 100644 --- a/js/id/modes/draw_line.js +++ b/js/id/modes/draw_line.js @@ -12,8 +12,8 @@ iD.modes.DrawLine = function(wayId, direction) { way = history.graph().entity(wayId), node = iD.Node({loc: map.mouseCoordinates()}), index = (direction === 'forward') ? undefined : 0, - headId = (direction === 'forward') ? _.last(way.nodes) : _.first(way.nodes), - tailId = (direction === 'forward') ? _.first(way.nodes) : _.last(way.nodes); + headId = (direction === 'forward') ? way.last() : way.first(), + tailId = (direction === 'forward') ? way.first() : way.last(); iD.behavior.Hover()(surface); diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 62b309e29..8d59fbe43 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -75,7 +75,7 @@ iD.modes.Select = function (entity) { }).on('splitWay', function(d) { mode.history.perform( iD.actions.SplitWay(d.id), - 'split a way on a node'); + 'split a way'); }).on('remove', function() { remove(); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 1ffb5cbb9..ede0e54bb 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -45,7 +45,7 @@ iD.Map = function() { if (d3.event.button == 2) { d3.event.stopPropagation(); } - }) + }, true) .call(iD.svg.Surface()); @@ -237,10 +237,7 @@ iD.Map = function() { map.size = function(_) { if (!arguments.length) return dimensions; dimensions = _; - surface - .size(dimensions) - .selectAll('#clip-rect') - .size(dimensions); + surface.size(dimensions); background.size(dimensions); return redraw(); }; diff --git a/js/id/svg/lines.js b/js/id/svg/lines.js index a0e951591..3ae499b20 100644 --- a/js/id/svg/lines.js +++ b/js/id/svg/lines.js @@ -76,10 +76,12 @@ iD.svg.Lines = function(projection) { var lineString = iD.svg.LineString(projection); - var casing = surface.select('.layer-casing'), + var shadow = surface.select('.layer-shadow'), + casing = surface.select('.layer-casing'), stroke = surface.select('.layer-stroke'), defs = surface.select('defs'), text = surface.select('.layer-text'), + shadows = drawPaths(shadow, lines, filter, 'way line shadow', lineString, 'shadow-'), casings = drawPaths(casing, lines, filter, 'way line casing', lineString, 'casing-'), strokes = drawPaths(stroke, lines, filter, 'way line stroke', lineString, 'stroke-'); diff --git a/js/id/svg/member_classes.js b/js/id/svg/member_classes.js index b675288c3..d5745a466 100644 --- a/js/id/svg/member_classes.js +++ b/js/id/svg/member_classes.js @@ -19,7 +19,7 @@ iD.svg.MemberClasses = function(graph) { relations.forEach(function (relation) { classes += ' member-type-' + relation.tags.type; - classes += ' member-role-' + _.find(relation.members, function (member) { return member.id == d.id; }).role; + classes += ' member-role-' + relation.memberById(d.id).role; }); classes = classes.trim(); diff --git a/js/id/svg/midpoints.js b/js/id/svg/midpoints.js index 321e75c8e..7c7c7a0c3 100644 --- a/js/id/svg/midpoints.js +++ b/js/id/svg/midpoints.js @@ -22,17 +22,25 @@ iD.svg.Midpoints = function(projection) { } } - var handles = surface.select('.layer-hit').selectAll('circle.midpoint') + var groups = surface.select('.layer-hit').selectAll('g.midpoint') .filter(filter) .data(midpoints, function (d) { return [d.way, d.index].join(","); }); - handles.enter() - .append('circle') - .attr({ r: 3, 'class': 'midpoint' }); + var group = groups.enter() + .insert('g', ':first-child') + .attr('class', 'midpoint'); - handles.attr('transform', iD.svg.PointTransform(projection)); + group.append('circle') + .attr('r', 7) + .attr('class', 'shadow'); - handles.exit() + group.append('circle') + .attr('r', 3) + .attr('class', 'fill'); + + groups.attr('transform', iD.svg.PointTransform(projection)); + + groups.exit() .remove(); }; }; diff --git a/js/id/svg/surface.js b/js/id/svg/surface.js index 0da1cc4ac..5443db8c7 100644 --- a/js/id/svg/surface.js +++ b/js/id/svg/surface.js @@ -1,17 +1,9 @@ iD.svg.Surface = function() { return function drawSurface(selection) { - selection.append('defs') - .append('clipPath') - .attr('id', 'clip') - .append('rect') - .attr('id', 'clip-rect') - .attr({ x: 0, y: 0 }); + selection.append('defs'); - var clip = selection.append('g') - .attr('clip-path', 'url(#clip)'); - - var layers = clip.selectAll('.layer') - .data(['fill', 'casing', 'stroke', 'text', 'hit', 'label']); + var layers = selection.selectAll('.layer') + .data(['shadow', 'fill', 'casing', 'stroke', 'text', 'hit', 'label']); layers.enter().append('g') .attr('class', function(d) { return 'layer layer-' + d; }); diff --git a/js/id/svg/tag_classes.js b/js/id/svg/tag_classes.js index a7660e1f5..5ce585add 100644 --- a/js/id/svg/tag_classes.js +++ b/js/id/svg/tag_classes.js @@ -1,8 +1,8 @@ iD.svg.TagClasses = function() { var keys = iD.util.trueObj([ - 'highway', 'railway', 'motorway', 'amenity', 'natural', - 'landuse', 'building', 'oneway', 'bridge', 'boundary', - 'leisure' + 'highway', 'railway', 'waterway', 'power', 'motorway', 'amenity', + 'natural', 'landuse', 'building', 'oneway', 'bridge', 'boundary', + 'leisure', 'construction' ]), tagClassRe = /^tag-/; return function tagClassesSelection(selection) { diff --git a/js/id/svg/vertices.js b/js/id/svg/vertices.js index ce81dface..4a975d38b 100644 --- a/js/id/svg/vertices.js +++ b/js/id/svg/vertices.js @@ -22,12 +22,16 @@ iD.svg.Vertices = function(projection) { .attr('class', 'node vertex'); group.append('circle') - .attr('class', 'stroke') - .attr('r', 6); + .attr('r', 10) + .attr('class', 'shadow'); group.append('circle') - .attr('class', 'fill') - .attr('r', 4); + .attr('r', 6) + .attr('class', 'stroke'); + + group.append('circle') + .attr('r', 3) + .attr('class', 'fill'); groups.attr('transform', iD.svg.PointTransform(projection)) .call(iD.svg.TagClasses()) diff --git a/js/id/ui/commit.js b/js/id/ui/commit.js index 06d072275..b63150ba3 100644 --- a/js/id/ui/commit.js +++ b/js/id/ui/commit.js @@ -28,7 +28,7 @@ iD.ui.commit = function() { var changes = selection.datum(), connection = changes.connection, user = connection.user(), - header = selection.append('div').attr('class', 'header modal-section'), + header = selection.append('div').attr('class', 'header modal-section fillL'), body = selection.append('div').attr('class', 'body'); @@ -54,40 +54,44 @@ iD.ui.commit = function() { .append('div') .text(user.display_name); - header.append('h2').text('Upload Changes to OpenStreetMap'); + header.append('h2').text('Save Changes'); header.append('p').text('The changes you upload will be visible on all maps that use OpenStreetMap data.'); - var comment_section = body.append('div').attr('class','modal-section'); + // Comment Box + var comment_section = body.append('div').attr('class','modal-section fillD'); comment_section.append('textarea') .attr('class', 'changeset-comment') .attr('placeholder', 'Brief Description of your contributions'); + // Confirm / Cancel Buttons var buttonwrap = comment_section.append('div') - .attr('class', 'buttons'); + .attr('class', 'buttons cf') + .append('div') + .attr('class', 'button-wrap joined col4'); - var savebutton = buttonwrap.append('button') - .attr('class', 'action wide') - .on('click.save', function() { - event.save({ - comment: d3.select('textarea.changeset-comment').node().value + var savebutton = buttonwrap + .append('button') + .attr('class', 'save action col6 button') + .on('click.save', function() { + event.save({ + comment: d3.select('textarea.changeset-comment').node().value + }); }); - }); - savebutton.append('span').attr('class','icon save icon-pre-text'); - savebutton.append('span').attr('class','label').text('Save'); + savebutton.append('span').attr('class','label').text('Save'); var cancelbutton = buttonwrap.append('button') - .attr('class', 'cancel wide') - .on('click.cancel', function() { - event.cancel(); - }); - cancelbutton.append('span').attr('class','icon close icon-pre-text'); - cancelbutton.append('span').attr('class','label').text('Cancel'); + .attr('class', 'cancel col6 button') + .on('click.cancel', function() { + event.cancel(); + }); + cancelbutton.append('span').attr('class','icon close icon-pre-text'); + cancelbutton.append('span').attr('class','label').text('Cancel'); var warnings = body.selectAll('div.warning-section') .data(iD.validate(changes)) .enter() - .append('div').attr('class', 'modal-section warning-section'); + .append('div').attr('class', 'modal-section warning-section fillL'); warnings.append('h3') .text('Warnings'); diff --git a/js/id/ui/confirm.js b/js/id/ui/confirm.js index 11515996e..02133406b 100644 --- a/js/id/ui/confirm.js +++ b/js/id/ui/confirm.js @@ -2,12 +2,12 @@ iD.ui.confirm = function() { var modal = iD.ui.modal(); modal.select('.modal').classed('modal-alert', true); modal.select('.content') - .classed('modal-section', true) + .attr('class','modal-section fillD') .append('div') .attr('class', 'description'); var nochanges = modal.select('.content') .append('button') - .attr('class','wide action centered') + .attr('class','action centered') .on('click.confirm', function() { modal.remove(); }); diff --git a/js/id/ui/flash.js b/js/id/ui/flash.js index 7b3b00628..4ce6a3b0a 100644 --- a/js/id/ui/flash.js +++ b/js/id/ui/flash.js @@ -10,7 +10,7 @@ iD.ui.flash = function() { modal.on('click.flash', function() { modal.remove(); }); - d3.timer(function() { + setTimeout(function() { modal.remove(); return true; }, 1000); diff --git a/js/id/ui/geocoder.js b/js/id/ui/geocoder.js index 4bd8c0629..ba3e0db2f 100644 --- a/js/id/ui/geocoder.js +++ b/js/id/ui/geocoder.js @@ -41,14 +41,13 @@ iD.ui.geocoder = function() { } var button = selection.append('button') - .attr('class', 'narrow') .attr('title', 'Find A Location') .html('') .on('click', toggle); var gcForm = selection.append('form'); - gcForm.attr('class','content map-overlay hide') + gcForm.attr('class','content fillD map-overlay hide') .append('input') .attr({ type: 'text', placeholder: 'find a place' }) .on('keydown', keydown); diff --git a/js/id/ui/geolocate.js b/js/id/ui/geolocate.js index 906c031c4..803b0d49d 100644 --- a/js/id/ui/geolocate.js +++ b/js/id/ui/geolocate.js @@ -10,13 +10,13 @@ iD.ui.geolocate = function(map) { selection .attr('class', 'geolocate-control map-control') .append('button') - .attr('class', 'narrow') .attr('title', 'Show My Location') - .text('G') .on('click', function() { navigator.geolocation.getCurrentPosition( success, error); - }); + }) + .append('span') + .attr('class','icon geolocate'); }; }; diff --git a/js/id/ui/inspector.js b/js/id/ui/inspector.js index 600538e4c..850fe84c0 100644 --- a/js/id/ui/inspector.js +++ b/js/id/ui/inspector.js @@ -7,18 +7,14 @@ iD.ui.inspector = function() { function inspector(selection) { var entity = selection.datum(); - selection.html('').append('button') - .attr('class', 'narrow close') - .html("") - .on('click', function() { - event.close(entity); - }); + var inspector = selection.append('div') + .attr('class','inspector content'); - selection.append('div') - .attr('class', 'head inspector-inner') + inspector.append('div') + .attr('class', 'head inspector-inner fillL') .call(drawHead); - var inspectorbody = selection.append('div') + var inspectorbody = inspector.append('div') .attr('class', 'inspector-body'); var inspectorwrap = inspectorbody.append('div') @@ -43,7 +39,7 @@ iD.ui.inspector = function() { drawTags(entity.tags); inspectorbody.append('div') - .attr('class', 'inspector-buttons') + .attr('class', 'inspector-buttons pad1 fillD') .call(drawButtons); } @@ -57,37 +53,45 @@ iD.ui.inspector = function() { h2.append('span') .text(entity.friendlyName()); - - selection.append('a') - .attr('href', 'http://www.openstreetmap.org/browse/' + entity.type + '/' + entity.osmId()) - .attr('target', '_blank') - .text('View on OSM'); - - if (entity.type === 'way') { - selection.append('a') - .attr('href', '#') - .text('Reverse Direction') - .on('click', function() { event.changeWayDirection(entity); }); - } - - if (entity.geometry() === 'vertex') { - selection.append('a') - .attr('href', '#') - .text('Split Way') - .on('click', function() { event.splitWay(entity); }); - } } function drawButtons(selection) { - selection.append('button') - .attr('class', 'apply wide action') - .html("Close") - .on('click', apply); + var entity = selection.datum(); + var inspectorButtonWrap = selection.append('div') + .attr('class','button-wrap joined fl'); + var inspectorButton1 = inspectorButtonWrap.append('button') + .attr('class', 'apply col6 action') + .on('click', apply); + + inspectorButton1.append('span').attr('class','icon icon-pre-text apply'); + inspectorButton1.append('span').attr('class','label').text('Okay'); + + var inspectorButton2 = inspectorButtonWrap.append('button') + .attr('class', 'delete col6 action') + .on('click', function(entity) { event.remove(entity); }); + + inspectorButton2.append('span').attr('class','icon icon-pre-text delete'); + inspectorButton2.append('span').attr('class','label').text('Delete'); + + var minorButtons = selection.append('div').attr('class','minor-buttons fl'); + + minorButtons.append('a') + .attr('href', 'http://www.openstreetmap.org/browse/' + entity.type + '/' + entity.osmId()) + .attr('target', '_blank') + .text('View on OSM'); + if (entity.type === 'way') { + minorButtons.append('a') + .attr('href', '#') + .text('Reverse Direction') + .on('click', function() { event.changeWayDirection(entity); }); + } + if (entity.geometry() === 'vertex') { + minorButtons.append('a') + .attr('href', '#') + .text('Split Way') + .on('click', function() { event.splitWay(entity); }); + } - selection.append('button') - .attr('class', 'delete wide action') - .html("Delete") - .on('click', function(entity) { event.remove(entity); }); } function drawTags(tags) { @@ -139,57 +143,50 @@ iD.ui.inspector = function() { var helpBtn = row.append('button') .attr('tabindex', -1) .attr('class', 'tag-help minor') - .append('a') - .attr('tabindex', -1) - .attr('target', '_blank') - .on('click', function(d) { - var params = _.extend({}, d, { - geometry: entity.geometry() - }); - if (d.key && d.value) { - taginfo.docs(params, function(err, docs) { - var en = _.find(docs, function(d) { - return d.lang == 'en'; - }); - if (en) { - var types = []; - if (en.on_area) types.push('area'); - if (en.on_node) types.push('point'); - if (en.on_way) types.push('line'); - en.types = types; - iD.ui.modal() - .select('.content') - .datum(en) - .call(iD.ui.tagReference); - } else { - iD.ui.flash() - .select('.content') - .text('This is no documentation available for this tag combination'); - } - }); - } else if (d.key) { - taginfo.values(params, function(err, values) { - if (values.data.length) { - iD.ui.modal() - .select('.content') - .datum({ - data: values.data, - title: 'Key:' + params.key, - geometry: params.geometry - }) - .call(iD.keyReference); - } else { - iD.ui.flash() - .select('.content') - .text('This is no documentation available for this key'); - } - }); - } - d3.event.preventDefault(); - }) - .attr('href', function(d) { - return 'http://taginfo.openstreetmap.org/keys/' + d.key; + .on('click', function(d) { + var params = _.extend({}, d, { + geometry: entity.geometry() }); + if (d.key && d.value) { + taginfo.docs(params, function(err, docs) { + var en = _.find(docs, function(d) { + return d.lang == 'en'; + }); + if (en) { + var types = []; + if (en.on_area) types.push('area'); + if (en.on_node) types.push('point'); + if (en.on_way) types.push('line'); + en.types = types; + iD.ui.modal() + .select('.content') + .datum(en) + .call(iD.ui.tagReference); + } else { + iD.ui.flash() + .select('.content') + .text('This is no documentation available for this tag combination'); + } + }); + } else if (d.key) { + taginfo.values(params, function(err, values) { + if (values.data.length) { + iD.ui.modal() + .select('.content') + .datum({ + data: values.data, + title: 'Key:' + params.key, + geometry: params.geometry + }) + .call(iD.keyReference); + } else { + iD.ui.flash() + .select('.content') + .text('This is no documentation available for this key'); + } + }); + } + }); helpBtn.append('span') .attr('class', 'icon inspect'); diff --git a/js/id/ui/key_reference.js b/js/id/ui/key_reference.js index 5810018f1..1c1515150 100644 --- a/js/id/ui/key_reference.js +++ b/js/id/ui/key_reference.js @@ -4,10 +4,10 @@ iD.ui.keyReference = function(selection) { var selection = d3.select(this), data = selection.datum(), header = selection.append('div') - .attr('class','modal-section') + .attr('class','modal-section fillL') .append('h2'), body = selection.append('div') - .attr('class', 'modal-section'); + .attr('class', 'modal-section fillL2'); header.append('span').attr('class', 'icon big icon-pre-text big-' + data.geometry); header.append('span').text(data.title); diff --git a/js/id/ui/layerswitcher.js b/js/id/ui/layerswitcher.js index a2acad1b5..1d1dc4f71 100644 --- a/js/id/ui/layerswitcher.js +++ b/js/id/ui/layerswitcher.js @@ -29,11 +29,11 @@ iD.ui.layerswitcher = function(map) { function layerswitcher(selection) { var content = selection - .append('div').attr('class', 'content map-overlay hide'); + .append('div').attr('class', 'content fillD map-overlay hide'); var button = selection .append('button') - .attr('class', 'narrow') + .attr('class', 'fillD') .attr('title', 'Layer Settings') .html("") .on('click.layerswitcher-toggle', toggle); @@ -56,7 +56,7 @@ iD.ui.layerswitcher = function(map) { var opa = content .append('div') - .attr('class', 'opacity-options-wrapper fillL2'); + .attr('class', 'opacity-options-wrapper'); opa.append('h4').text('Layers'); @@ -102,7 +102,7 @@ iD.ui.layerswitcher = function(map) { content .append('ul') - .attr('class', 'toggle-list') + .attr('class', 'toggle-list fillL') .selectAll('a.layer') .data(sources) .enter() @@ -135,7 +135,7 @@ iD.ui.layerswitcher = function(map) { var adjustments = content .append('div') - .attr('class', 'adjustments'); + .attr('class', 'adjustments pad1'); var directions = [ ['←', [-1, 0]], diff --git a/js/id/ui/loading.js b/js/id/ui/loading.js index d5bd97625..6a98a917a 100644 --- a/js/id/ui/loading.js +++ b/js/id/ui/loading.js @@ -3,7 +3,7 @@ iD.ui.loading = function(message) { var loadertext = modal.select('.content') .classed('loading-modal', true) - .append('div').classed('modal-section',true); + .append('div').attr('class','modal-section fillL'); loadertext.append('img').attr('class','loader').attr('src', '/img/loader.gif'); loadertext.append('h3').text(message || ''); diff --git a/js/id/ui/save.js b/js/id/ui/save.js index 6bf8419c3..afaab5023 100644 --- a/js/id/ui/save.js +++ b/js/id/ui/save.js @@ -7,7 +7,7 @@ iD.ui.save = function() { var history = map.history(), connection = map.connection(); - selection.html("Save") + selection.html("Save") .attr('title', 'Save changes to OpenStreetMap, making them visible to other users') .property('disabled', true) .call(bootstrap.tooltip() @@ -22,7 +22,7 @@ iD.ui.save = function() { history.reset(); map.flush().redraw(); if (err) { - var desc = iD.confirm() + var desc = iD.ui.confirm() .select('.description'); desc.append('h2') .text('An error occurred while trying to save'); @@ -42,12 +42,8 @@ iD.ui.save = function() { } }); } - var changes = history.changes(); - var has_changes = d3.sum(d3.values(changes).map(function(c) { - return c.length; - })) > 0; - if (has_changes) { + if (history.hasChanges()) { connection.authenticate(function(err) { var modal = iD.ui.modal(); var changes = history.changes(); @@ -68,7 +64,7 @@ iD.ui.save = function() { .on('save', commit)); }); } else { - iD.confirm().select('.description') + iD.ui.confirm().select('.description') .append('h3').text('You don\'t have any changes to save.'); } }); @@ -77,16 +73,13 @@ iD.ui.save = function() { .attr('class', 'count'); history.on('change.save-button', function() { - var changes = history.changes(), - num_changes = d3.sum(d3.values(changes).map(function(c) { - return c.length; - })); + var hasChanges = history.hasChanges(); selection - .property('disabled', num_changes === 0) - .classed('has-count', num_changes > 0) + .property('disabled', !hasChanges) + .classed('has-count', hasChanges) .select('span.count') - .text(num_changes); + .text(history.numChanges()); }); } diff --git a/js/id/ui/success.js b/js/id/ui/success.js index cd4181bbe..d135f7d80 100644 --- a/js/id/ui/success.js +++ b/js/id/ui/success.js @@ -3,10 +3,10 @@ iD.ui.success = function() { function success(selection) { var changeset = selection.datum(), - header = selection.append('div').attr('class', 'header modal-section'), + header = selection.append('div').attr('class', 'header fillL modal-section'), body = selection.append('div').attr('class', 'body'); - var section = body.append('div').attr('class','modal-section'); + var section = body.append('div').attr('class','modal-section fillD'); header.append('h2').text('You Just Edited OpenStreetMap!'); header.append('p').text('You just improved the world\'s best free map'); @@ -27,16 +27,16 @@ iD.ui.success = function() { .text('Tweet: ' + message); var buttonwrap = section.append('div') - .attr('class', 'buttons'); + .attr('class', 'buttons cf'); var okbutton = buttonwrap.append('button') - .attr('class', 'action wide') + .attr('class', 'action col2') .on('click.save', function() { event.cancel(); }); okbutton.append('span').attr('class','icon apply icon-pre-text'); - okbutton.append('span').attr('class','label').text('OK'); + okbutton.append('span').attr('class','label').text('Okay'); } return d3.rebind(success, event, 'on'); diff --git a/js/id/ui/tag_reference.js b/js/id/ui/tag_reference.js index 55e20d8cb..eb1ee84bd 100644 --- a/js/id/ui/tag_reference.js +++ b/js/id/ui/tag_reference.js @@ -3,7 +3,7 @@ iD.ui.tagReference = function(selection) { function g(x) { return function(d) { return d[x]; }; } var selection = d3.select(this); var header = selection.append('div') - .attr('class','modal-section') + .attr('class','modal-section fillL header') .append('h2'); header.selectAll('span.icon') @@ -20,7 +20,7 @@ iD.ui.tagReference = function(selection) { .text(g('title')); referenceBody = selection.append('div') - .attr('class','modal-section'); + .attr('class','modal-section fillL2'); referenceBody .append('h5') diff --git a/js/id/ui/userpanel.js b/js/id/ui/userpanel.js index bab5e28d9..57f368d65 100644 --- a/js/id/ui/userpanel.js +++ b/js/id/ui/userpanel.js @@ -7,20 +7,26 @@ iD.ui.userpanel = function(connection) { if (connection.authenticated()) { selection.style('display', 'block'); connection.userDetails(function(user_details) { - if (user_details.image_url) { - selection.append('img') - .attr('class', 'icon icon-pre-text') - .attr('src', user_details.image_url); - } else { - selection.append('span') - .attr('class','icon avatar icon-pre-text'); - } - selection.append('span') - .append('a') + + // Link + var userLink = selection.append('a') .attr('href', connection.url() + '/user/' + user_details.display_name) .attr('target', '_blank') - .text(user_details.display_name); + + // Add thumbnail or dont + if (user_details.image_url) { + userLink.append('img') + .attr('class', 'icon icon-pre-text') + .attr('src', user_details.image_url); + } else { + userLink.append('span') + .attr('class','icon avatar icon-pre-text'); + } + + // Add user name + userLink.append('span').attr('class','label').text(user_details.display_name); + selection .append('a') .attr('class', 'logout') diff --git a/test/index.html b/test/index.html index 0777751d5..425258d20 100644 --- a/test/index.html +++ b/test/index.html @@ -66,6 +66,7 @@ + @@ -124,6 +125,7 @@ + diff --git a/test/index_packaged.html b/test/index_packaged.html index 630ed1088..8def4c0da 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -31,6 +31,7 @@ + diff --git a/test/rendering.html b/test/rendering.html new file mode 100644 index 000000000..ebefeaa4a --- /dev/null +++ b/test/rendering.html @@ -0,0 +1,127 @@ + + + + + Rendering Tests + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    z16z17
    BaseHoverSelectedBaseHoverSelected
    + + + + diff --git a/test/spec/actions/add_relation_member.js b/test/spec/actions/add_relation_member.js new file mode 100644 index 000000000..aeae8e353 --- /dev/null +++ b/test/spec/actions/add_relation_member.js @@ -0,0 +1,37 @@ +describe("iD.actions.AddRelationMember", function () { + it("adds a member at the end of the relation", function () { + var relation = iD.Relation(), + graph = iD.Graph([relation]); + + graph = iD.actions.AddRelationMember(relation.id, {id: '1'})(graph); + + expect(graph.entity(relation.id).members).to.eql([{id: '1'}]); + }); + + it("adds a member at index 0", function () { + var relation = iD.Relation({members: [{id: '1'}]}), + graph = iD.Graph([relation]); + + graph = iD.actions.AddRelationMember(relation.id, {id: '2'}, 0)(graph); + + expect(graph.entity(relation.id).members).to.eql([{id: '2'}, {id: '1'}]); + }); + + it("adds a member at a positive index", function () { + var relation = iD.Relation({members: [{id: '1'}, {id: '3'}]}), + graph = iD.Graph([relation]); + + graph = iD.actions.AddRelationMember(relation.id, {id: '2'}, 1)(graph); + + expect(graph.entity(relation.id).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]); + }); + + it("adds a member at a negative index", function () { + var relation = iD.Relation({members: [{id: '1'}, {id: '3'}]}), + graph = iD.Graph([relation]); + + graph = iD.actions.AddRelationMember(relation.id, {id: '2'}, -1)(graph); + + expect(graph.entity(relation.id).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]); + }); +}); diff --git a/test/spec/actions/split_way.js b/test/spec/actions/split_way.js index 6a4edc187..e83d5ee1a 100644 --- a/test/spec/actions/split_way.js +++ b/test/spec/actions/split_way.js @@ -8,55 +8,206 @@ describe("iD.actions.SplitWay", function () { // Expected result: // a ---- b ==== c // - var a = iD.Node(), - b = iD.Node(), - c = iD.Node(), - way = iD.Way({nodes: [a.id, b.id, c.id]}), - graph = iD.Graph([a, b, c, way]); + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}) + }); - graph = iD.actions.SplitWay(b.id)(graph); + graph = iD.actions.SplitWay('b', '=')(graph); - var waysA = graph.parentWays(a), - waysB = graph.parentWays(b), - waysC = graph.parentWays(c); - - expect(waysA).to.have.length(1); - expect(waysB).to.have.length(2); - expect(waysC).to.have.length(1); - - expect(waysA[0]).to.equal(waysB[0]); - expect(waysB[1]).to.equal(waysC[0]); + expect(graph.entity('-').nodes).to.eql(['a', 'b']); + expect(graph.entity('=').nodes).to.eql(['b', 'c']); }); - it("moves restriction relations to the new way", function () { + it("copies tags to the new way", function () { + var tags = {highway: 'residential'}, + graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c'], tags: tags}) + }); + + graph = iD.actions.SplitWay('b', '=')(graph); + + // Immutable tags => should be shared by identity. + expect(graph.entity('-').tags).to.equal(tags); + expect(graph.entity('=').tags).to.equal(tags); + }); + + it("adds the new way to parent relations (no connections)", function () { // Situation: - // a ==== b ==== c ---- d - // A restriction from ==== to ---- via c. + // a ---- b ---- c + // Relation: [----] // // Split at b. // // Expected result: - // a ==== b ≠≠≠≠ c ---- d - // A restriction from ≠≠≠≠ to ---- via c. + // a ---- b ==== c + // Relation: [----, ====] // - var a = iD.Node(), - b = iD.Node(), - c = iD.Node(), - d = iD.Node(), - from = iD.Way({nodes: [a.id, b.id, c.id]}), - to = iD.Way({nodes: [c.id, d.id]}), - restriction = iD.Relation({tags: {type: 'restriction'}, members: [ - { role: 'from', id: from.id }, - { role: 'to', id: to.id }, - { role: 'via', id: c.id }]}), - graph = iD.Graph([a, b, c, d, from, to, restriction]); + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}), + 'r': iD.Relation({id: 'r', members: [{id: '-', type: 'way'}]}) + }); - graph = iD.actions.SplitWay(b.id)(graph); + graph = iD.actions.SplitWay('b', '=')(graph); - restriction = graph.entity(restriction.id); + expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['-', '=']); + }); - expect(restriction.members[0]).not.to.eql({ role: 'from', id: from.id }); - expect(restriction.members[1]).to.eql({ role: 'to', id: to.id }); - expect(restriction.members[2]).to.eql({ role: 'via', id: c.id }); + it("adds the new way to parent relations (forward order)", function () { + // Situation: + // a ---- b ---- c ~~~~ d + // Relation: [----, ~~~~] + // + // Split at b. + // + // Expected result: + // a ---- b ==== c ~~~~ d + // Relation: [----, ====, ~~~~] + // + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + 'd': iD.Node({id: 'd'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}), + '~': iD.Way({id: '~', nodes: ['c', 'd']}), + 'r': iD.Relation({id: 'r', members: [{id: '-', type: 'way'}, {id: '~', type: 'way'}]}) + }); + + graph = iD.actions.SplitWay('b', '=')(graph); + + expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['-', '=', '~']); + }); + + it("adds the new way to parent relations (reverse order)", function () { + // Situation: + // a ---- b ---- c ~~~~ d + // Relation: [~~~~, ----] + // + // Split at b. + // + // Expected result: + // a ---- b ==== c ~~~~ d + // Relation: [~~~~, ====, ----] + // + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + 'd': iD.Node({id: 'd'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}), + '~': iD.Way({id: '~', nodes: ['c', 'd']}), + 'r': iD.Relation({id: 'r', members: [{id: '~', type: 'way'}, {id: '-', type: 'way'}]}) + }); + + graph = iD.actions.SplitWay('b', '=')(graph); + + expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['~', '=', '-']); + }); + + ['restriction', 'restriction:bus'].forEach(function (type) { + it("updates a restriction's 'from' role", function () { + // Situation: + // a ----> b ----> c ~~~~ d + // A restriction from ---- to ~~~~ via c. + // + // Split at b. + // + // Expected result: + // a ----> b ====> c ~~~~ d + // A restriction from ==== to ~~~~ via c. + // + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + 'd': iD.Node({id: 'd'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}), + '~': iD.Way({id: '~', nodes: ['c', 'd']}), + 'r': iD.Relation({id: 'r', tags: {type: type}, members: [ + {id: '-', role: 'from'}, + {id: '~', role: 'to'}, + {id: 'c', role: 'via'}]}) + }); + + graph = iD.actions.SplitWay('b', '=')(graph); + + expect(graph.entity('r').members).to.eql([ + {id: '=', role: 'from'}, + {id: '~', role: 'to'}, + {id: 'c', role: 'via'}]); + }); + + it("updates a restriction's 'to' role", function () { + // Situation: + // a ----> b ----> c ~~~~ d + // A restriction from ~~~~ to ---- via c. + // + // Split at b. + // + // Expected result: + // a ----> b ====> c ~~~~ d + // A restriction from ~~~~ to ==== via c. + // + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + 'd': iD.Node({id: 'd'}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}), + '~': iD.Way({id: '~', nodes: ['c', 'd']}), + 'r': iD.Relation({id: 'r', tags: {type: type}, members: [ + {id: '~', role: 'from'}, + {id: '-', role: 'to'}, + {id: 'c', role: 'via'}]}) + }); + + graph = iD.actions.SplitWay('b', '=')(graph); + + expect(graph.entity('r').members).to.eql([ + {id: '~', role: 'from'}, + {id: '=', role: 'to'}, + {id: 'c', role: 'via'}]); + }); + + it("leaves unaffected restrictions unchanged", function () { + // Situation: + // a <---- b <---- c ~~~~ d + // A restriction from ---- to ~~~~ via c. + // + // Split at b. + // + // Expected result: + // a <==== b <---- c ~~~~ d + // A restriction from ---- to ~~~~ via c. + // + var graph = iD.Graph({ + 'a': iD.Node({id: 'a'}), + 'b': iD.Node({id: 'b'}), + 'c': iD.Node({id: 'c'}), + 'd': iD.Node({id: 'd'}), + '-': iD.Way({id: '-', nodes: ['c', 'b', 'a']}), + '~': iD.Way({id: '~', nodes: ['c', 'd']}), + 'r': iD.Relation({id: 'r', tags: {type: type}, members: [ + {id: '-', role: 'from'}, + {id: '~', role: 'to'}, + {id: 'c', role: 'via'}]}) + }); + + graph = iD.actions.SplitWay('b', '=')(graph); + + expect(graph.entity('r').members).to.eql([ + {id: '-', role: 'from'}, + {id: '~', role: 'to'}, + {id: 'c', role: 'via'}]); + }); }); }); diff --git a/test/spec/actions/update_relation_member.js b/test/spec/actions/update_relation_member.js index 1878ff923..c85d72b34 100644 --- a/test/spec/actions/update_relation_member.js +++ b/test/spec/actions/update_relation_member.js @@ -2,7 +2,7 @@ describe("iD.actions.UpdateRelationMember", function () { it("updates the properties of the relation member at the specified index", function () { var node = iD.Node(), relation = iD.Relation({members: [{id: node.id, role: 'forward'}]}), - graph = iD.actions.UpdateRelationMember(relation.id, 0, {role: 'backward'})(iD.Graph([node, relation])); + graph = iD.actions.UpdateRelationMember(relation.id, {role: 'backward'}, 0)(iD.Graph([node, relation])); expect(graph.entity(relation.id).members).to.eql([{id: node.id, role: 'backward'}]); }); }); diff --git a/test/spec/graph/entity.js b/test/spec/graph/entity.js index e5741104f..3f3a701fe 100644 --- a/test/spec/graph/entity.js +++ b/test/spec/graph/entity.js @@ -113,6 +113,22 @@ describe('iD.Entity', function () { }); }); + describe("#intersects", function () { + it("returns true for a way with a node within the given extent", function () { + var node = iD.Node({loc: [0, 0]}), + way = iD.Way({nodes: [node.id]}), + graph = iD.Graph([node, way]); + expect(way.intersects([[-5, -5], [5, 5]], graph)).to.equal(true); + }); + + it("returns false for way with no nodes within the given extent", function () { + var node = iD.Node({loc: [6, 6]}), + way = iD.Way({nodes: [node.id]}), + graph = iD.Graph([node, way]); + expect(way.intersects([[-5, -5], [5, 5]], graph)).to.equal(false); + }); + }); + describe("#hasInterestingTags", function () { it("returns false if the entity has no tags", function () { expect(iD.Entity().hasInterestingTags()).to.equal(false); diff --git a/test/spec/graph/history.js b/test/spec/graph/history.js index 15fe10964..554b22a82 100644 --- a/test/spec/graph/history.js +++ b/test/spec/graph/history.js @@ -126,6 +126,34 @@ describe("iD.History", function () { }); }); + describe("#hasChanges", function() { + it("is true when any of change's values are nonempty", function() { + var node = iD.Node(); + history.perform(function (graph) { return graph.replace(node); }); + expect(history.hasChanges()).to.eql(true); + }); + + it("is false when all of change's values are empty", function() { + expect(history.hasChanges()).to.eql(false); + }); + }); + + describe("#numChanges", function() { + it("is 0 when there are no changes", function() { + expect(history.numChanges()).to.eql(0); + }); + + it("is the sum of all types of changes", function() { + var node1 = iD.Node({id: "n1"}), + node2 = iD.Node(); + history.merge(iD.Graph([node1])); + history.perform(function (graph) { return graph.remove(node1); }); + expect(history.numChanges()).to.eql(1); + history.perform(function (graph) { return graph.replace(node2); }); + expect(history.numChanges()).to.eql(2); + }); + }); + describe("#reset", function () { it("clears the version stack", function () { history.perform(action, "annotation"); diff --git a/test/spec/graph/relation.js b/test/spec/graph/relation.js index 8880fc98c..f8b49a213 100644 --- a/test/spec/graph/relation.js +++ b/test/spec/graph/relation.js @@ -55,6 +55,55 @@ describe('iD.Relation', function () { }); }); + describe("#geometry", function () { + it("returns 'relation'", function () { + expect(iD.Relation().geometry()).to.equal('relation'); + }); + }); + + describe("#memberByRole", function () { + it("returns the first member with the given role", function () { + var r = iD.Relation({members: [ + {id: 'a', role: 'inner'}, + {id: 'b', role: 'outer'}, + {id: 'c', role: 'outer'}]}); + expect(r.memberByRole('outer')).to.eql({id: 'b', role: 'outer', index: 1}); + }); + + it("returns undefined if no members have the given role", function () { + expect(iD.Relation().memberByRole('outer')).to.be.undefined; + }); + }); + + describe("#memberById", function () { + it("returns the first member with the given id", function () { + var r = iD.Relation({members: [ + {id: 'a', role: 'outer'}, + {id: 'b', role: 'outer'}, + {id: 'b', role: 'inner'}]}); + expect(r.memberById('b')).to.eql({id: 'b', role: 'outer', index: 1}); + }); + + it("returns undefined if no members have the given role", function () { + expect(iD.Relation().memberById('b')).to.be.undefined; + }); + }); + + describe("#isRestriction", function () { + it("returns true for 'restriction' type", function () { + expect(iD.Relation({tags: {type: 'restriction'}}).isRestriction()).to.be.true; + }); + + it("returns true for 'restriction:type' types", function () { + expect(iD.Relation({tags: {type: 'restriction:bus'}}).isRestriction()).to.be.true; + }); + + it("returns false otherwise", function () { + expect(iD.Relation().isRestriction()).to.be.false; + expect(iD.Relation({tags: {type: 'multipolygon'}}).isRestriction()).to.be.false; + }); + }); + describe("#multipolygon", function () { specify("single polygon consisting of a single way", function () { var a = iD.Node(), diff --git a/test/spec/graph/way.js b/test/spec/graph/way.js index 19a4514db..37df6b648 100644 --- a/test/spec/graph/way.js +++ b/test/spec/graph/way.js @@ -35,6 +35,28 @@ describe('iD.Way', function() { expect(iD.Way({tags: {foo: 'bar'}}).tags).to.eql({foo: 'bar'}); }); + describe("#first", function () { + it("returns the first node", function () { + expect(iD.Way({nodes: ['a', 'b', 'c']}).first()).to.equal('a'); + }); + }); + + describe("#last", function () { + it("returns the last node", function () { + expect(iD.Way({nodes: ['a', 'b', 'c']}).last()).to.equal('c'); + }); + }); + + describe("#contains", function () { + it("returns true if the way contains the given node", function () { + expect(iD.Way({nodes: ['a', 'b', 'c']}).contains('b')).to.be.true; + }); + + it("returns false if the way does not contain the given node", function () { + expect(iD.Way({nodes: ['a', 'b', 'c']}).contains('d')).to.be.false; + }); + }); + describe("#extent", function () { it("returns the minimal extent containing all member nodes", function () { var node1 = iD.Node({loc: [0, 0]}), @@ -45,22 +67,6 @@ describe('iD.Way', function() { }); }); - describe("#intersects", function () { - it("returns true for a way with a node within the given extent", function () { - var node = iD.Node({loc: [0, 0]}), - way = iD.Way({nodes: [node.id]}), - graph = iD.Graph([node, way]); - expect(way.intersects([[-5, -5], [5, 5]], graph)).to.equal(true); - }); - - it("returns false for way with no nodes within the given extent", function () { - var node = iD.Node({loc: [6, 6]}), - way = iD.Way({nodes: [node.id]}), - graph = iD.Graph([node, way]); - expect(way.intersects([[-5, -5], [5, 5]], graph)).to.equal(false); - }); - }); - describe('#isClosed', function() { it('returns false when the way has no nodes', function() { expect(iD.Way().isClosed()).to.equal(false); diff --git a/test/spec/ui/flash.js b/test/spec/ui/flash.js index 0426d1734..2b43ef83e 100644 --- a/test/spec/ui/flash.js +++ b/test/spec/ui/flash.js @@ -1,13 +1,17 @@ describe("iD.ui.flash", function () { - it('can be instantiated', function () { - var flash = iD.ui.flash(); - expect(flash).to.be.ok; + var clock; + + beforeEach(function () { + clock = sinon.useFakeTimers(); }); - it('leaves after 1000 ms', function (done) { + + afterEach(function () { + clock.restore(); + }); + + it('leaves after 1000 ms', function () { var flash = iD.ui.flash(); - window.setTimeout(function() { - expect(flash.node().parentNode).to.be.null; - done(); - }, 1200); + clock.tick(1010); + expect(flash.node().parentNode).to.be.null; }); }); diff --git a/test/spec/ui/inspector.js b/test/spec/ui/inspector.js index baa830e76..1a4df1899 100644 --- a/test/spec/ui/inspector.js +++ b/test/spec/ui/inspector.js @@ -57,11 +57,11 @@ describe("iD.ui.inspector", function () { expect(inspector.tags()).to.eql({}); }); - it("emits a close event when the close button is clicked", function () { + it("emits a close event when the apply button is clicked", function () { var spy = sinon.spy(); inspector.on('close', spy); - element.select('.close').trigger('click'); + element.select('.apply').trigger('click'); expect(spy).to.have.been.calledWith(entity); });