From 71e2b58a25b5499f83884236b4f94494c958b5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Sun, 14 May 2017 20:21:33 +0200 Subject: [PATCH] start new editor --- src/c3nav/editor/static/editor/css/editor.css | 31 +- src/c3nav/editor/static/editor/js/editor.js | 374 +++++++++--------- .../templates/editor/fragment_sections.html | 13 + .../editor/templates/editor/sections.html | 14 + src/c3nav/editor/urls.py | 7 +- src/c3nav/editor/views.py | 40 +- 6 files changed, 285 insertions(+), 194 deletions(-) create mode 100644 src/c3nav/editor/templates/editor/fragment_sections.html create mode 100644 src/c3nav/editor/templates/editor/sections.html diff --git a/src/c3nav/editor/static/editor/css/editor.css b/src/c3nav/editor/static/editor/css/editor.css index 47f4fba9..91f2b3dc 100644 --- a/src/c3nav/editor/static/editor/css/editor.css +++ b/src/c3nav/editor/static/editor/css/editor.css @@ -29,15 +29,6 @@ form button.invisiblesubmit { display: block; } -.leaflet-container .leaflet-control-layers-toggle { - color:#000000 !important; - font-size:14px; - text-align:center; - width:75px; - line-height:36px; - background-image:none; - padding:0 8px; -} .leaflet-container .leaflet-control-layers-expanded { min-width:75px; } @@ -46,6 +37,28 @@ form button.invisiblesubmit { line-height:18px; } +.leaflet-control-sections a, .leaflet-control-sections a:hover { + width: auto; + font-size: 14px; + padding: 0 6px; +} +.leaflet-control-sections a, .leaflet-control-sections a:hover { + display:none; +} +.leaflet-control-sections a.current, .leaflet-control-sections-expanded a, .leaflet-control-sections-expanded a:hover { + display:block; +} +.leaflet-control-sections:not(.leaflet-control-sections-expanded) a.current { + border-radius:4px; +} +.leaflet-control-sections a.current { + font-weight:bold; +} + +[data-sections] { + display:none; +} + #mapeditcontrols { position: absolute; diff --git a/src/c3nav/editor/static/editor/js/editor.js b/src/c3nav/editor/static/editor/js/editor.js index b976dcaf..af453bf3 100644 --- a/src/c3nav/editor/static/editor/js/editor.js +++ b/src/c3nav/editor/static/editor/js/editor.js @@ -5,7 +5,12 @@ } }()); + editor = { + options: { + position: 'bottomright' + }, + init: function () { // Init Map editor.map = L.map('map', { @@ -29,10 +34,11 @@ editor = { $('body').addClass('controls'); }); + editor._section_control = new SectionControl().addTo(editor.map); + editor.init_geometries(); editor.init_sidebar(); editor.get_sources(); - editor.get_levels(); bounds = [[0.0, 0.0], [240.0, 400.0]]; editor.map.setMaxBounds(bounds); @@ -44,7 +50,6 @@ editor = { get_sources: function () { // load sources editor._sources_control = L.control.layers().addTo(editor.map); - $(editor._sources_control._layersLink).text('Sources'); $.getJSON('/api/sources/', function (sources) { var source; @@ -57,60 +62,101 @@ editor = { }); }, - // levels - levels: {}, - _level: null, - _loading_geometry: true, - _level_fake_layers: {}, - get_levels: function () { - // load levels and set the lowest one afterwards - $.getJSON('/api/levels/?ordering=-altitude', function (levels) { - var control = L.control.layers([], [], { - position: 'bottomright' - }).addTo(editor.map); - $(control._layersLink).text('Levels').parent().addClass('leaflet-levels'); + // sidebar + get_location_path: function () { + return window.location.pathname + window.location.search; + }, + init_sidebar: function() { + // init the sidebar. sed listeners for form submits and link clicks + $('#mapeditcontrols').on('click', 'a[href]', editor._sidebar_link_click) + .on('click', 'button[type=submit]', editor._sidebar_submit_btn_click) + .on('submit', 'form', editor._sidebar_submit); + var location_path = editor.get_location_path(); + editor.sidebar_get(location_path); + history.replaceState({}, '', location_path); + window.onpopstate = function() { + editor.sidebar_get(editor.get_location_path()); + }; + }, + sidebar_get: function(location) { + // load a new page into the sidebar using a GET request + var location_path = editor.get_location_path(); + if ($('#mapeditcontrols').html() !== '') { + history.pushState({}, '', location); + } + editor._sidebar_unload(); + $.get(location, editor._sidebar_loaded).fail(editor._sidebar_error); + }, + _sidebar_unload: function() { + // unload the sidebar. called on sidebar_get and form submit. + editor._section_control.disable(); + $('#mapeditcontrols').html('').addClass('loading'); + editor._unhighlight_geometry(); + editor._cancel_editing(); + }, + _sidebar_loaded: function(data) { + // sidebar was loaded. load the content. check if there are any redirects. call _check_start_editing. + var content = $(data); + var mapeditcontrols = $('#mapeditcontrols'); + mapeditcontrols.html(content).removeClass('loading'); - var level, layer; - for (var i = levels.length -1; i >= 0; i--) { - level = levels[i]; - layer = L.circle([-200, -200], 0.1); - layer._c3nav_level = level.name; - layer.on('add', editor._click_level); - editor._level_fake_layers[level.name] = layer; - control.addBaseLayer(layer, level.name); + redirect = $('span[data-redirect]'); + if (redirect.length) { + editor.sidebar_get(redirect.attr('data-redirect')); + return; + } + + sections = $('[data-sections]'); + if (sections.length) { + var sections = sections.find('a'); + editor._section_control.clearSections(); + for(var i=0;i 1) { + editor._section_control.enable(); + } else { + editor._section_control.disable(); + } + editor._section_control.show() + } else { + editor._section_control.hide(); + } - editor._loading_geometry = false; - editor.set_current_level(levels[0].name); + editor._check_start_editing(); + }, + _sidebar_error: function(data) { + $('#mapeditcontrols').html('

Error '+data.status+'

'+data.statusText).removeClass('loading'); + editor._section_control.hide(); + }, + _sidebar_link_click: function(e) { + // listener for link-clicks in the sidebar. + e.preventDefault(); + editor.sidebar_get($(this).attr('href')); + }, + _sidebar_submit_btn_click: function() { + // listener for submit-button-clicks in the sidebar, so the submit event will know which button submitted. + $(this).closest('form').data('btn', $(this)).clearQueue().delay(300).queue(function() { + $(this).data('btn', null); }); }, - _click_level: function(e) { - if (editor._level === null) return; - var level = e.target._c3nav_level; - var success = editor.set_current_level(level); - if (!success) { - editor._level_fake_layers[level].remove(); - editor._level_fake_layers[editor._level].addTo(editor.map); + _sidebar_submit: function(e) { + // listener for form submits in the sidebar. + e.preventDefault(); + var data = $(this).serialize(); + var btn = $(this).data('btn'); + if (btn !== undefined && btn !== null) { + if ($(btn).is('[name]')) { + data += '&' + $('').attr('name', $(btn).attr('name')).val($(btn).val()).serialize(); + } + if ($(btn).is('[data-reload-geometries]')) { + editor._get_geometries_next_time = true; + } } - }, - set_current_level: function(level_name) { - // sets the current level if the sidebar allows it - if (editor._loading_geometry) return false; - var level_switch = $('#mapeditcontrols').find('[data-level-switch]'); - if (level_switch.length === 0) return; - editor._loading_geometry = true; - if (editor._level !== null) { - editor._level_fake_layers[editor._level].remove(); - } - editor._level_fake_layers[level_name].addTo(editor.map); - editor._level = level_name; - editor.get_geometries(); - - var level_switch_href = level_switch.attr('data-level-switch'); - if (level_switch_href) { - editor.sidebar_get(level_switch_href.replace('LEVEL', level_name)); - } - return true; + var action = $(this).attr('action'); + editor._sidebar_unload(); + $.post(action, data, editor._sidebar_loaded); }, // geometries @@ -122,9 +168,6 @@ editor = { _geometries_shadows: {}, _creating: false, _editing: null, - _geometry_types: [], - _shown_geometry_types: {}, - _geometry_types_control: null, init_geometries: function () { // init geometries and edit listeners editor._highlight_layer = L.layerGroup().addTo(editor.map); @@ -142,50 +185,6 @@ editor = { editor.map.on('editable:vertex:ctrlclick editable:vertex:metakeyclick', function (e) { e.vertex.continue(); }); - - editor._get_geometry_types(); - }, - _get_geometry_types: function() { - editor._geometry_types_control = L.control.layers().addTo(editor.map); - $(editor._geometry_types_control._layersLink).text('Types'); - $.getJSON('/api/geometrytypes/', function(geometrytypes) { - var geometrytype, layer; - for (var i = 0; i < geometrytypes.length; i++) { - geometrytype = geometrytypes[i]; - layer = L.circle([-200, -200], 0.1); - layer._c3nav_geometry_type = geometrytype.name; - layer.on('add', editor._add_geometrytype_layer); - layer.on('remove', editor._remove_geometrytype_layer); - layer.addTo(editor.map); - editor._geometry_types_control.addOverlay(layer, geometrytype.title_plural); - editor._geometry_types.push(geometrytype.name) - editor._shown_geometry_types[geometrytype.name] = true; - } - }); - }, - _add_geometrytype_layer: function(e) { - var type = e.target._c3nav_geometry_type; - if (!editor._shown_geometry_types[type]) { - if (editor._loading_geometry) { - e.target.remove(); - return; - } - editor._loading_geometry = true; - editor._shown_geometry_types[type] = true; - editor.get_geometries(); - } - }, - _remove_geometrytype_layer: function(e) { - var type = e.target._c3nav_geometry_type; - if (editor._shown_geometry_types[type]) { - if (editor._loading_geometry) { - e.target.addTo(map); - return; - } - editor._loading_geometry = true; - editor._shown_geometry_types[type] = false; - editor.get_geometries(); - } }, get_geometries: function () { // reload geometries of current level @@ -194,13 +193,7 @@ editor = { if (editor._geometries_layer !== null) { editor.map.removeLayer(editor._geometries_layer); } - geometrytypes = ''; - for (var i = 0; i < editor._geometry_types.length; i++) { - if (editor._shown_geometry_types[editor._geometry_types[i]]) { - geometrytypes += '&type=' + editor._geometry_types[i]; - } - } - $.getJSON('/api/geometries/?level='+String(editor._level)+geometrytypes, function(geometries) { + $.getJSON('/api/geometries/?level='+String(editor._level), function(geometries) { editor._geometries_layer = L.geoJSON(geometries, { style: editor._get_geometry_style, onEachFeature: editor._register_geojson_feature @@ -426,87 +419,106 @@ editor = { if (editor._editing !== null) { $('#id_geometry').val(JSON.stringify(editor._editing.toGeoJSON().geometry)); } - }, - - // sidebar - sidebar_location: null, - init_sidebar: function() { - // init the sidebar. sed listeners for form submits and link clicks - $('#mapeditcontrols').on('click', 'a[href]', editor._sidebar_link_click) - .on('click', 'button[type=submit]', editor._sidebar_submit_btn_click) - .on('submit', 'form', editor._sidebar_submit); - }, - sidebar_get: function(location) { - // load a new page into the sidebar using a GET request - editor._sidebar_unload(); - $.get(location, editor._sidebar_loaded); - }, - _sidebar_unload: function() { - // unload the sidebar. called on sidebar_get and form submit. - $('#mapeditcontrols').html('').addClass('loading'); - editor._unhighlight_geometry(); - editor._cancel_editing(); - }, - _sidebar_loaded: function(data) { - // sidebar was loaded. load the content. check if there are any redirects. call _check_start_editing. - var content = $(data); - var mapeditcontrols = $('#mapeditcontrols'); - mapeditcontrols.html(content).removeClass('loading'); - - var redirect = mapeditcontrols.find('form[name=redirect]'); - if (redirect.length) { - redirect.submit(); - return; - } - - redirect = $('span[data-redirect]'); - if (redirect.length) { - editor.sidebar_get(redirect.attr('data-redirect').replace('LEVEL', editor._level)); - return; - } - - editor._check_start_editing(); - }, - _sidebar_link_click: function(e) { - // listener for link-clicks in the sidebar. - e.preventDefault(); - if ($(this).is('[data-level-link]')) { - editor.set_current_level($(this).attr('data-level-link')); - return; - } - var href = $(this).attr('href'); - if ($(this).is('[data-insert-level]')) { - href = href.replace('LEVEL', editor._level); - } - editor.sidebar_get(href); - }, - _sidebar_submit_btn_click: function() { - // listener for submit-button-clicks in the sidebar, so the submit event will know which button submitted. - $(this).closest('form').data('btn', $(this)).clearQueue().delay(300).queue(function() { - $(this).data('btn', null); - }); - }, - _sidebar_submit: function(e) { - // listener for form submits in the sidebar. - if ($(this).attr('name') == 'redirect') return; - e.preventDefault(); - var data = $(this).serialize(); - var btn = $(this).data('btn'); - if (btn !== undefined && btn !== null) { - if ($(btn).is('[name]')) { - data += '&' + $('').attr('name', $(btn).attr('name')).val($(btn).val()).serialize(); - } - if ($(btn).is('[data-reload-geometries]')) { - editor._get_geometries_next_time = true; - } - } - var action = $(this).attr('action'); - editor._sidebar_unload(); - $.post(action, data, editor._sidebar_loaded); } }; +SectionControl = L.Control.extend({ + options: { + position: 'bottomright' + }, + + onAdd: function (map) { + this._container = L.DomUtil.create('div', 'leaflet-control-sections leaflet-bar'); + this._sectionButtons = []; + this._disabled = true; + this.hide(); + + if (!L.Browser.android) { + L.DomEvent.on(this._container, { + mouseenter: this.expand, + mouseleave: this.collapse + }, this); + } + + if (L.Browser.touch) { + L.DomEvent + .on(this._container, 'click', L.DomEvent.stop) + .on(this._container, 'click', this.collapse, this); + } else { + L.DomEvent.on(this._container, 'focus', this.expand, this); + } + + this._map.on('click', this.collapse, this); + + return this._container; + }, + + addSection: function (title, href, current) { + var link = L.DomUtil.create('a', (current ? 'current' : ''), this._container); + link.innerHTML = title; + link.href = href; + + L.DomEvent + .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation) + .on(link, 'click', this._sectionClick, this) + + this._sectionButtons.push(link); + return link; + }, + + clearSections: function() { + for (var i = 0; i < this._sectionButtons.length; i++) { + L.DomUtil.remove(this._sectionButtons[i]); + } + this._sectionButtons = []; + }, + + disable: function () { + for (var i = 0; i < this._sectionButtons.length; i++) { + L.DomUtil.addClass(this._sectionButtons[i], 'leaflet-disabled'); + } + this.collapse(); + this._disabled = true; + }, + + enable: function () { + for (var i = 0; i < this._sectionButtons.length; i++) { + L.DomUtil.removeClass(this._sectionButtons[i], 'leaflet-disabled'); + } + this._disabled = false; + }, + + hide: function () { + this._container.style.display = 'none'; + }, + + show: function () { + this._container.style.display = ''; + }, + + _sectionClick: function (e) { + console.log('click'); + e.preventDefault(); + if (!this._disabled) { + editor.sidebar_get(e.target.href); + this.collapse(); + } + }, + + expand: function () { + if (this._disabled) return; + L.DomUtil.addClass(this._container, 'leaflet-control-sections-expanded'); + return this; + }, + + collapse: function () { + L.DomUtil.removeClass(this._container, 'leaflet-control-sections-expanded'); + return this; + } +}); + + if ($('#mapeditcontrols').length) { editor.init(); } diff --git a/src/c3nav/editor/templates/editor/fragment_sections.html b/src/c3nav/editor/templates/editor/fragment_sections.html new file mode 100644 index 00000000..6cd5f36c --- /dev/null +++ b/src/c3nav/editor/templates/editor/fragment_sections.html @@ -0,0 +1,13 @@ +{% if sections %} + +{% elif section %} + +{% endif %} diff --git a/src/c3nav/editor/templates/editor/sections.html b/src/c3nav/editor/templates/editor/sections.html new file mode 100644 index 00000000..1ca979cc --- /dev/null +++ b/src/c3nav/editor/templates/editor/sections.html @@ -0,0 +1,14 @@ +{% load bootstrap3 %} +{% load i18n %} +{% include 'editor/fragment_sections.html' %} + +{% trans 'Add section' %} +

{% trans 'Sections' %}

+
+ {% for s in sections %} + + {{ s.title }} + + {% endfor %} +
+ diff --git a/src/c3nav/editor/urls.py b/src/c3nav/editor/urls.py index 998cecd7..8b7bfc40 100644 --- a/src/c3nav/editor/urls.py +++ b/src/c3nav/editor/urls.py @@ -1,10 +1,11 @@ from django.conf.urls import url -from django.views.generic import TemplateView -from c3nav.editor.views import edit_mapitem, list_mapitems, list_mapitemtypes +from c3nav.editor.views import edit_mapitem, list_mapitems, list_mapitemtypes, main_index, sections_list urlpatterns = [ - url(r'^$', TemplateView.as_view(template_name='editor/map.html'), name='editor.index'), + url(r'^$', main_index, name='editor.index'), + url(r'^sections/$', sections_list, name='editor.sections'), + url(r'^sections/(?P
[0-9]+)/$', main_index, name='editor.index'), url(r'^mapitemtypes/(?P[^/]+)/$', list_mapitemtypes, name='editor.mapitemtypes'), url(r'^mapitems/(?P[^/]+)/list/$', list_mapitems, name='editor.mapitems'), url(r'^mapitems/(?P[^/]+)/list/(?P[0-9]+)/$', list_mapitems, name='editor.mapitems.level'), diff --git a/src/c3nav/editor/views.py b/src/c3nav/editor/views.py index fb41fe9a..31862381 100644 --- a/src/c3nav/editor/views.py +++ b/src/c3nav/editor/views.py @@ -1,19 +1,57 @@ +from functools import wraps + from django.conf import settings from django.core.exceptions import PermissionDenied from django.http.response import Http404 from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse from c3nav.mapdata.models import Section from c3nav.mapdata.models.base import EDITOR_FORM_MODELS +def sidebar_view(func): + @wraps(func) + def with_ajax_check(request, *args, **kwargs): + if not request.is_ajax(): + return render(request, 'editor/map.html', {}) + return func(request, *args, **kwargs) + return with_ajax_check + + +@sidebar_view +def main_index(request, section=None): + if section is None: + first_section = Section.objects.first() + if first_section: + return render(request, 'editor/redirect.html', { + 'target': reverse('editor.index', kwargs={'section': first_section.id}) + }) + else: + return render(request, 'editor/redirect.html', { + 'target': reverse('editor.sections') + }) + else: + section = get_object_or_404(Section, pk=section) + + return render(request, 'editor/index.html', { + 'sections': Section.objects.all(), + 'section': section, + 'section_url': 'editor.index', + }) + + +@sidebar_view +def sections_list(request): + return render(request, 'editor/sections.html', {}) + + def list_mapitemtypes(request, section): section = get_object_or_404(Section, pk=section) def get_item_count(mapitemtype): if hasattr(mapitemtype, 'section'): return filter_queryset_by_access(request, mapitemtype.objects.filter(section=section)).count() - return 0 return render(request, 'editor/mapitemtypes.html', {