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 {
width: 0;
}
#sidebar #search {
z-index: 4;
min-height: 54px;
min-width: 54px;
@ -103,7 +106,7 @@ main.map {
transition: width 150ms;
flex-shrink: 0;
}
#search.loading {
#sidebar #search.loading {
/*noinspection CssUnknownTarget*/
background: url('../../img/loader.gif') no-repeat 4px 3px;
width: 5%;
@ -116,6 +119,26 @@ main.map {
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,
#search.loading #destination-input {
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) {
main[data-view=route-result] #search:not(.focused) .locationinput {
main[data-view=route-result] #sidebar #search:not(.focused) .locationinput {
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;
}
main[data-view=route-result] #search:not(.focused) .locationinput small {
main[data-view=route-result] #sidebar #search:not(.focused) .locationinput small {
opacity: 0;
-webkit-user-select: none;
-moz-user-select: none;
@ -489,16 +512,16 @@ main:not([data-view=route-result]) #route-summary {
user-select: 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);
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);
top: -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);
top: 32px;
}

View file

@ -47,7 +47,10 @@ c3nav = {
$('.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);
c3nav.load_state(state, true);
c3nav.update_map_locations();
@ -343,16 +346,16 @@ c3nav = {
if (a.center[0] !== b.center[0] || a.center[1] !== b.center[1]) return false;
return true;
},
_build_state_url: function (state) {
var url;
_build_state_url: function (state, embed) {
var url = embed ? '/embed' : '';
if (state.routing) {
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 {
url = (state.destination) ? '/d/'+state.destination.slug+'/' : '/r/';
url += (state.destination) ? '/d/'+state.destination.slug+'/' : '/r/';
}
} else {
url = state.destination?('/l/'+state.destination.slug+'/'):'/';
url += state.destination?('/l/'+state.destination.slug+'/'):'/';
}
if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) {
url += 'details/'
@ -368,7 +371,12 @@ c3nav = {
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;
if (replace || (!state.sidebar && !old_state.sidebar)) {
@ -859,8 +867,8 @@ c3nav = {
var $search = $('#search'),
$main = $('main'),
padBesideSidebar = ($main.width() > 1000 && ($main.height() < 250 || c3nav.state.details)),
left = padBesideSidebar ? $search.width()+10 : 0,
top = padBesideSidebar ? 10 : $search.height()+10;
left = padBesideSidebar ? ($search.width() || 0)+10 : 0,
top = padBesideSidebar ? 10 : ($search.height() || 0)+10;
options[topleft || 'paddingTopLeft'] = L.point(left+13, top+41);
options[bottomright || 'paddingBottomRight'] = L.point(50, 20);
return options;

View file

@ -3,7 +3,7 @@
{% load i18n %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE }}">
<head>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>c3nav</title>
@ -15,110 +15,120 @@
<link href="{% static 'material-icons/material-icons.css' %}" rel="stylesheet">
<link href="{% static 'site/css/c3nav.css' %}" rel="stylesheet">
{% endcompress %}
</head>
<body>
<header>
<h1>c3nav</h1>
<a href="#" id="user">
<span>{{ user_data.title }}</span>
{% if user_data.subtitle %}<small>{{ user_data.subtitle }}</small>{% endif %}
</a>
</header>
<main class="map" data-state="{{ state }}">
<section id="popup-buttons">
<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-origin">{% trans 'Route from here' %}</button>
</section>
<section class="share-ui">
<h3>Share</h3>
<img src="">
<input type="text" readonly>
</section>
</head>
<body>
{% if not embed %}
<header>
<h1>c3nav</h1>
<a href="#" id="user">
<span>{{ user_data.title }}</span>
{% if user_data.subtitle %}<small>{{ user_data.subtitle }}</small>{% endif %}
</a>
</header>
{% 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>
<section id="sidebar">
<section id="search" class="loading">
<div class="location locationinput empty" id="origin-input">
<i class="icon material-icons">place</i>
<input type="text" autocomplete="off" spellcheck="false" placeholder="{% trans 'Search any location…' %}">
<small></small>
<button class="button-clear locate material-icons">my_location</button>
<button class="button-clear clear material-icons">clear</button>
</div>
<div class="location locationinput empty" id="destination-input">
<i class="icon material-icons">place</i>
<input type="text" autocomplete="off" spellcheck="false" placeholder="{% trans 'Search any location…' %}">
<small></small>
<button class="button-clear locate material-icons">my_location</button>
<button class="button-clear clear material-icons">clear</button>
</div>
<i class="material-icons" id="route-dots">more_vert</i>
<div class="buttons" id="location-buttons">
<button class="button-clear details">
<i class="material-icons">expand_more</i>
{% trans 'Details' %}
</button>
<button class="button-clear share">
<i class="material-icons">share</i>
{% trans 'Share' %}
</button>
<button class="button-clear route">
<i class="material-icons">directions</i>
{% trans 'Route' %}
</button>
</div>
<div id="route-summary">
<i class="icon material-icons">directions</i>
<span>10min (100m)… sorry no routing yet</span>
<small><em>default options</em></small>
</div>
<div class="buttons" id="route-search-buttons">
<button class="button-clear swap">
<i class="material-icons">swap_vert</i>
{% trans 'Swap' %}
</button>
<button class="button-clear close">
<i class="material-icons">close</i>
{% trans 'Close' %}
</button>
</div>
<div class="buttons" id="route-result-buttons">
<button class="button-clear swap">
<i class="material-icons">swap_vert</i>
{% trans 'Swap' %}
</button>
<button class="button-clear details">
<i class="material-icons">arrow_downward</i>
{% trans 'Details' %}
</button>
{% if not embed %}
<section id="popup-buttons">
<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-origin">{% trans 'Route from here' %}</button>
</section>
<section class="share-ui">
<h3>Share</h3>
<img src="">
<input type="text" readonly>
</section>
<section id="sidebar">
<section id="search" class="loading">
<div class="location locationinput empty" id="origin-input">
<i class="icon material-icons">place</i>
<input type="text" autocomplete="off" spellcheck="false" placeholder="{% trans 'Search any location…' %}">
<small></small>
<button class="button-clear locate material-icons">my_location</button>
<button class="button-clear clear material-icons">clear</button>
</div>
<div class="location locationinput empty" id="destination-input">
<i class="icon material-icons">place</i>
<input type="text" autocomplete="off" spellcheck="false" placeholder="{% trans 'Search any location…' %}">
<small></small>
<button class="button-clear locate material-icons">my_location</button>
<button class="button-clear clear material-icons">clear</button>
</div>
<i class="material-icons" id="route-dots">more_vert</i>
<div class="buttons" id="location-buttons">
<button class="button-clear details">
<i class="material-icons">expand_more</i>
{% trans 'Details' %}
</button>
<button class="button-clear share">
<i class="material-icons">share</i>
{% trans 'Share' %}
</button>
<button class="button-clear route">
<i class="material-icons">directions</i>
{% trans 'Route' %}
</button>
</div>
<div id="route-summary">
<i class="icon material-icons">directions</i>
<span>10min (100m)… sorry no routing yet</span>
<small><em>default options</em></small>
</div>
<div class="buttons" id="route-search-buttons">
<button class="button-clear swap">
<i class="material-icons">swap_vert</i>
{% trans 'Swap' %}
</button>
<button class="button-clear close">
<i class="material-icons">close</i>
{% trans 'Close' %}
</button>
</div>
<div class="buttons" id="route-result-buttons">
<button class="button-clear swap">
<i class="material-icons">swap_vert</i>
{% trans 'Swap' %}
</button>
<button class="button-clear details">
<i class="material-icons">arrow_downward</i>
{% trans 'Details' %}
</button>
</div>
</section>
<div id="resultswrapper">
<section id="autocomplete"></section>
<section id="location-details" class="details">
<div class="details-head">
<a class="button button-clear editor float-right" target="_blank">
<i class="material-icons">edit</i>
{% trans 'Open in Editor' %}
</a>
<h2>{% trans 'Details' %}</h2>
</div>
<div class="details-body"></div>
</section>
<section id="route-details" class="details"></section>
</div>
</section>
<div id="resultswrapper">
<section id="autocomplete"></section>
<section id="location-details" class="details">
<div class="details-head">
<a class="button button-clear editor float-right" target="_blank">
<i class="material-icons">edit</i>
{% trans 'Open in Editor' %}
</a>
<h2>{% trans 'Details' %}</h2>
</div>
<div class="details-body"></div>
</section>
<section id="route-details" class="details"></section>
</div>
</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>
<div id="modal" class="loading">
<div id="modal-content">
{% if not embed %}
<div id="modal" class="loading">
<div id="modal-content"></div>
</div>
</div>
{% endif %}
{% compress js %}
<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 'site/js/c3nav.js' %}"></script>
{% endcompress %}
</body>
</body>
</html>

View file

@ -2,14 +2,17 @@ from django.conf.urls import url
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/)?'
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 = [
url(r'^(?P<mode>[l])/(?P<slug>[a-z0-9-_.:]+)/%s%s$' % (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'^r/(?P<slug>[a-z0-9-_.:]+)/(?P<slug2>[a-z0-9-_.:]+)/%s%s$' % (details, pos), map_index, name='site.index'),
url(r'^(?P<mode>r)/%s$' % 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'^%s(?P<mode>[od])/%s/%s$' % (embed, slug, 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'^%s(?P<mode>r)/%s$' % (embed, pos), map_index, name='site.index'),
url(r'^qr/(?P<path>.*)$', qr_code, name='site.qr'),
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.shortcuts import render
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 c3nav.mapdata.models import Location, Source
@ -35,7 +36,8 @@ def check_location(location: Optional[str], request) -> Optional[SpecificLocatio
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
destination = None
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),
'tile_cache_server': settings.TILE_CACHE_SERVER,
'user_data': get_user_data(request),
'embed': bool(embed),
}
response = render(request, 'site/map.html', ctx)
set_tile_access_cookie(request, response)
if embed:
xframe_options_exempt(lambda: response)()
return response