make c3nav embeddable using iframes

This commit is contained in:
Laura Klünder 2017-12-07 00:52:13 +01:00
parent e768a6176c
commit 276eee5adf
5 changed files with 167 additions and 118 deletions

View file

@ -96,6 +96,9 @@ main.map {
} }
#search { #search {
width: 0;
}
#sidebar #search {
z-index: 4; z-index: 4;
min-height: 54px; min-height: 54px;
min-width: 54px; min-width: 54px;
@ -103,7 +106,7 @@ main.map {
transition: width 150ms; transition: width 150ms;
flex-shrink: 0; flex-shrink: 0;
} }
#search.loading { #sidebar #search.loading {
/*noinspection CssUnknownTarget*/ /*noinspection CssUnknownTarget*/
background: url('../../img/loader.gif') no-repeat 4px 3px; background: url('../../img/loader.gif') no-repeat 4px 3px;
width: 5%; width: 5%;
@ -116,6 +119,26 @@ main.map {
background: url('../../img/loader.gif'); background: url('../../img/loader.gif');
} }
#embed-logo {
font-size: 3rem;
line-height: 100%;
font-weight: 300;
z-index: 2;
position: absolute;
color: #606c76;
top: 10px;
left: 10px;
opacity: 0.4;
letter-spacing: -.1rem;
}
#embed-logo:hover {
opacity: 1;
text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.3),
1px -1px 1px rgba(255, 255, 255, 0.3),
-1px 1px 1px rgba(255, 255, 255, 0.3),
-1px -1px 1px rgba(255, 255, 255, 0.3);
}
main:not([data-view^=route]) #origin-input, main:not([data-view^=route]) #origin-input,
#search.loading #destination-input { #search.loading #destination-input {
margin-bottom: -55px; margin-bottom: -55px;
@ -475,13 +498,13 @@ main:not([data-view=route-result]) #route-summary {
} }
@media not all and (min-height: 700px) and (min-width: 1100px) { @media not all and (min-height: 700px) and (min-width: 1100px) {
main[data-view=route-result] #search:not(.focused) .locationinput { main[data-view=route-result] #sidebar #search:not(.focused) .locationinput {
margin-bottom: -21px; margin-bottom: -21px;
} }
main[data-view=route-result] #search:not(.focused) .locationinput input { main[data-view=route-result] #sidebar #search:not(.focused) .locationinput input {
padding-bottom: 28px; padding-bottom: 28px;
} }
main[data-view=route-result] #search:not(.focused) .locationinput small { main[data-view=route-result] #sidebar #search:not(.focused) .locationinput small {
opacity: 0; opacity: 0;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
@ -489,16 +512,16 @@ main:not([data-view=route-result]) #route-summary {
user-select: none; user-select: none;
pointer-events: none; pointer-events: none;
} }
main[data-view=route-result] #search:not(.focused) .locationinput .icon { main[data-view=route-result] #sidebar #search:not(.focused) .locationinput .icon {
transform: scale(0.6); transform: scale(0.6);
top: -1px; top: -1px;
} }
main[data-view=route-result] #search:not(.focused) .locationinput button { main[data-view=route-result] #sidebar #search:not(.focused) .locationinput button {
transform: scale(0.6); transform: scale(0.6);
top: -5px; top: -5px;
right: -5px; right: -5px;
} }
main[data-view=route-result] #search:not(.focused) #route-dots { main[data-view=route-result] #sidebar #search:not(.focused) #route-dots {
transform: scale(0.7); transform: scale(0.7);
top: 32px; top: 32px;
} }

View file

@ -47,7 +47,10 @@ c3nav = {
$('.locationinput').data('location', null); $('.locationinput').data('location', null);
var state = JSON.parse($('main').attr('data-state')); var $main = $('main'),
state = JSON.parse($main.attr('data-state'));
c3nav.embed = $main.is('[data-embed]');
history.replaceState(state, window.location.path); history.replaceState(state, window.location.path);
c3nav.load_state(state, true); c3nav.load_state(state, true);
c3nav.update_map_locations(); c3nav.update_map_locations();
@ -343,16 +346,16 @@ c3nav = {
if (a.center[0] !== b.center[0] || a.center[1] !== b.center[1]) return false; if (a.center[0] !== b.center[0] || a.center[1] !== b.center[1]) return false;
return true; return true;
}, },
_build_state_url: function (state) { _build_state_url: function (state, embed) {
var url; var url = embed ? '/embed' : '';
if (state.routing) { if (state.routing) {
if (state.origin) { if (state.origin) {
url = (state.destination) ? '/r/'+state.origin.slug+'/'+state.destination.slug+'/' : '/o/'+state.origin.slug+'/'; url += (state.destination) ? '/r/'+state.origin.slug+'/'+state.destination.slug+'/' : '/o/'+state.origin.slug+'/';
} else { } else {
url = (state.destination) ? '/d/'+state.destination.slug+'/' : '/r/'; url += (state.destination) ? '/d/'+state.destination.slug+'/' : '/r/';
} }
} else { } else {
url = state.destination?('/l/'+state.destination.slug+'/'):'/'; url += state.destination?('/l/'+state.destination.slug+'/'):'/';
} }
if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) { if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) {
url += 'details/' url += 'details/'
@ -368,7 +371,12 @@ c3nav = {
if (!replace && c3nav._equal_states(old_state, state)) return; if (!replace && c3nav._equal_states(old_state, state)) return;
var url = c3nav._build_state_url(state); var url = c3nav._build_state_url(state, c3nav.embed),
embed_logo = $('#embed-logo');
if (embed_logo.length) {
embed_logo.attr('href', c3nav._build_state_url(state));
}
c3nav.state = state; c3nav.state = state;
if (replace || (!state.sidebar && !old_state.sidebar)) { if (replace || (!state.sidebar && !old_state.sidebar)) {
@ -859,8 +867,8 @@ c3nav = {
var $search = $('#search'), var $search = $('#search'),
$main = $('main'), $main = $('main'),
padBesideSidebar = ($main.width() > 1000 && ($main.height() < 250 || c3nav.state.details)), padBesideSidebar = ($main.width() > 1000 && ($main.height() < 250 || c3nav.state.details)),
left = padBesideSidebar ? $search.width()+10 : 0, left = padBesideSidebar ? ($search.width() || 0)+10 : 0,
top = padBesideSidebar ? 10 : $search.height()+10; top = padBesideSidebar ? 10 : ($search.height() || 0)+10;
options[topleft || 'paddingTopLeft'] = L.point(left+13, top+41); options[topleft || 'paddingTopLeft'] = L.point(left+13, top+41);
options[bottomright || 'paddingBottomRight'] = L.point(50, 20); options[bottomright || 'paddingBottomRight'] = L.point(50, 20);
return options; return options;

View file

@ -16,8 +16,8 @@
<link href="{% static 'site/css/c3nav.css' %}" rel="stylesheet"> <link href="{% static 'site/css/c3nav.css' %}" rel="stylesheet">
{% endcompress %} {% endcompress %}
</head> </head>
<body> <body>
{% if not embed %}
<header> <header>
<h1>c3nav</h1> <h1>c3nav</h1>
<a href="#" id="user"> <a href="#" id="user">
@ -25,7 +25,10 @@
{% if user_data.subtitle %}<small>{{ user_data.subtitle }}</small>{% endif %} {% if user_data.subtitle %}<small>{{ user_data.subtitle }}</small>{% endif %}
</a> </a>
</header> </header>
<main class="map" data-state="{{ state }}"> {% endif %}
<main class="map" data-state="{{ state }}"{% if embed %} data-embed{% endif %}>
<section id="map" data-bounds="{{ bounds }}" data-levels="{{ levels }}"{% if tile_cache_server %} data-tile-server="{{ tile_cache_server }}"{% endif %}></section>
{% if not embed %}
<section id="popup-buttons"> <section id="popup-buttons">
<button class="button-clear as-location">{% trans 'Show only this location' %}</button> <button class="button-clear as-location">{% trans 'Show only this location' %}</button>
<button class="button-clear as-destination">{% trans 'Route to here' %}</button> <button class="button-clear as-destination">{% trans 'Route to here' %}</button>
@ -36,7 +39,6 @@
<img src=""> <img src="">
<input type="text" readonly> <input type="text" readonly>
</section> </section>
<section id="map" data-bounds="{{ bounds }}" data-levels="{{ levels }}"{% if tile_cache_server %} data-tile-server="{{ tile_cache_server }}"{% endif %}></section>
<section id="sidebar"> <section id="sidebar">
<section id="search" class="loading"> <section id="search" class="loading">
<div class="location locationinput empty" id="origin-input"> <div class="location locationinput empty" id="origin-input">
@ -109,12 +111,20 @@
<section id="route-details" class="details"></section> <section id="route-details" class="details"></section>
</div> </div>
</section> </section>
{% else %}
<a id="embed-logo">c3nav</a>
<section id="popup-buttons"></section>
<section id="search">
<input type="hidden" id="origin-input">
<input type="hidden" id="destination-input">
</section>
{% endif %}
</main> </main>
{% if not embed %}
<div id="modal" class="loading"> <div id="modal" class="loading">
<div id="modal-content"> <div id="modal-content"></div>
</div>
</div> </div>
{% endif %}
{% compress js %} {% compress js %}
<script type="text/javascript" src="{% static 'jquery/jquery.js' %}"></script> <script type="text/javascript" src="{% static 'jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'leaflet/leaflet.js' %}"></script> <script type="text/javascript" src="{% static 'leaflet/leaflet.js' %}"></script>

View file

@ -2,14 +2,17 @@ from django.conf.urls import url
from c3nav.site.views import map_index, qr_code from c3nav.site.views import map_index, qr_code
slug = r'(?P<slug>[a-z0-9-_.:]+)'
slug2 = r'(?P<slug2>[a-z0-9-_.:]+)'
details = r'(?P<details>details/)?' details = r'(?P<details>details/)?'
pos = r'(@(?P<level>[a-z0-9-_:]+),(?P<x>-?\d+(\.\d+)?),(?P<y>-?\d+(\.\d+)?),(?P<zoom>-?\d+(\.\d+)?))?' pos = r'(@(?P<level>[a-z0-9-_:]+),(?P<x>-?\d+(\.\d+)?),(?P<y>-?\d+(\.\d+)?),(?P<zoom>-?\d+(\.\d+)?))?'
embed = r'(?P<embed>embed/)?'
urlpatterns = [ urlpatterns = [
url(r'^(?P<mode>[l])/(?P<slug>[a-z0-9-_.:]+)/%s%s$' % (details, pos), map_index, name='site.index'), url(r'^%s(?P<mode>[l])/%s/%s%s$' % (embed, slug, details, pos), map_index, name='site.index'),
url(r'^(?P<mode>[od])/(?P<slug>[a-z0-9-_.:]+)/%s$' % pos, map_index, name='site.index'), url(r'^%s(?P<mode>[od])/%s/%s$' % (embed, slug, pos), map_index, name='site.index'),
url(r'^r/(?P<slug>[a-z0-9-_.:]+)/(?P<slug2>[a-z0-9-_.:]+)/%s%s$' % (details, pos), map_index, name='site.index'), url(r'^%sr/%s/%s/%s%s$' % (embed, slug, slug2, details, pos), map_index, name='site.index'),
url(r'^(?P<mode>r)/%s$' % pos, map_index, name='site.index'), url(r'^%s(?P<mode>r)/%s$' % (embed, pos), map_index, name='site.index'),
url(r'^qr/(?P<path>.*)$', qr_code, name='site.qr'), url(r'^qr/(?P<path>.*)$', qr_code, name='site.qr'),
url(r'^%s$' % pos, map_index, name='site.index') url(r'^%s$' % pos, map_index, name='site.index')
] ]

View file

@ -7,6 +7,7 @@ from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse, HttpResponseBadRequest from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render from django.shortcuts import render
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.http import etag from django.views.decorators.http import etag
from c3nav.mapdata.models import Location, Source from c3nav.mapdata.models import Location, Source
@ -35,7 +36,8 @@ def check_location(location: Optional[str], request) -> Optional[SpecificLocatio
return location return location
def map_index(request, mode=None, slug=None, slug2=None, details=None, level=None, x=None, y=None, zoom=None): def map_index(request, mode=None, slug=None, slug2=None, details=None,
level=None, x=None, y=None, zoom=None, embed=None):
origin = None origin = None
destination = None destination = None
routing = False routing = False
@ -76,9 +78,12 @@ def map_index(request, mode=None, slug=None, slug2=None, details=None, level=Non
'state': json.dumps(state, separators=(',', ':'), cls=DjangoJSONEncoder), 'state': json.dumps(state, separators=(',', ':'), cls=DjangoJSONEncoder),
'tile_cache_server': settings.TILE_CACHE_SERVER, 'tile_cache_server': settings.TILE_CACHE_SERVER,
'user_data': get_user_data(request), 'user_data': get_user_data(request),
'embed': bool(embed),
} }
response = render(request, 'site/map.html', ctx) response = render(request, 'site/map.html', ctx)
set_tile_access_cookie(request, response) set_tile_access_cookie(request, response)
if embed:
xframe_options_exempt(lambda: response)()
return response return response