diff --git a/Gemfile b/Gemfile index e399d121f..5d011e54e 100644 --- a/Gemfile +++ b/Gemfile @@ -53,7 +53,7 @@ gem "sitemap_generator", "~> 6.0.2" gem "social-share-button", "~> 1.1" gem "sprockets", "~> 3.7.2" gem "translator-text", "~> 0.1.0" -gem "turbolinks", "~> 2.5.3" +gem "turbolinks", "~> 5.2.1" gem "turnout", "~> 2.4.0" gem "uglifier", "~> 4.1.2" gem "whenever", "~> 0.10.0", require: false diff --git a/Gemfile.lock b/Gemfile.lock index c4e2158a0..c97ee8422 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -571,8 +571,9 @@ GEM translator-text (0.1.0) dry-struct (~> 0.5.0) httparty (~> 0.15) - turbolinks (2.5.4) - coffee-rails + turbolinks (5.2.1) + turbolinks-source (~> 5.2) + turbolinks-source (5.2.0) turnout (2.4.1) i18n (~> 0.7) rack (>= 1.3, < 3) @@ -698,7 +699,7 @@ DEPENDENCIES spring-commands-rspec (~> 1.0.4) sprockets (~> 3.7.2) translator-text (~> 0.1.0) - turbolinks (~> 2.5.3) + turbolinks (~> 5.2.1) turnout (~> 2.4.0) uglifier (~> 4.1.2) web-console (~> 3.3.0) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index d3c84a255..5e7842945 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -42,6 +42,7 @@ //= require jquery-fileupload/basic //= require foundation //= require turbolinks +//= require turbolinks_anchors //= require ckeditor/loader //= require_directory ./ckeditor //= require social-share-button @@ -86,7 +87,7 @@ //= require legislation //= require legislation_allegations //= require legislation_annotatable -//= require watch_form_changes +//= require legislation_draft_versions //= require followable //= require flaggable //= require documentable @@ -110,6 +111,7 @@ //= require cookies //= require columns_selector //= require budget_edit_associations +//= require datepicker var initialize_modules = function() { "use strict"; @@ -144,7 +146,6 @@ var initialize_modules = function() { if ($(".legislation-annotatable").length) { App.LegislationAnnotatable.initialize(); } - App.WatchFormChanges.initialize(); App.TreeNavigator.initialize(); App.Documentable.initialize(); App.Imageable.initialize(); @@ -166,10 +167,16 @@ var initialize_modules = function() { App.BudgetEditAssociations.initialize(); }; -$(function() { +var destroy_non_idempotent_modules = function() { "use strict"; - Turbolinks.enableProgressBar(); -}); -$(document).ready(initialize_modules); -$(document).on("page:load", initialize_modules); + App.ColumnsSelector.destroy(); + App.Datepicker.destroy(); + App.HTMLEditor.destroy(); + App.LegislationAnnotatable.destroy(); + App.Map.destroy(); + App.SocialShare.destroy(); +}; + +$(document).on("turbolinks:load", initialize_modules); +$(document).on("turbolinks:before-cache", destroy_non_idempotent_modules); diff --git a/app/assets/javascripts/check_all_none.js b/app/assets/javascripts/check_all_none.js index 8684a7cf7..ac52cdd79 100644 --- a/app/assets/javascripts/check_all_none.js +++ b/app/assets/javascripts/check_all_none.js @@ -2,13 +2,17 @@ "use strict"; App.CheckAllNone = { initialize: function() { - $("[data-check-all]").on("click", function() { + $("[data-check-all]").on("click", function(e) { var target_name; + e.preventDefault(); + e.stopPropagation(); target_name = $(this).data("check-all"); $("[name='" + target_name + "']").prop("checked", true); }); - $("[data-check-none]").on("click", function() { + $("[data-check-none]").on("click", function(e) { var target_name; + e.preventDefault(); + e.stopPropagation(); target_name = $(this).data("check-none"); $("[name='" + target_name + "']").prop("checked", false); }); diff --git a/app/assets/javascripts/columns_selector.js b/app/assets/javascripts/columns_selector.js index 77dd5f4c1..2f680b7c6 100644 --- a/app/assets/javascripts/columns_selector.js +++ b/app/assets/javascripts/columns_selector.js @@ -85,6 +85,9 @@ $(".column-selectable").on("inserted", function() { App.ColumnsSelector.initColumns(); }); + }, + destroy: function() { + $("#js-columns-selector-wrapper").children(":not(#column_selector_item_template)").remove(); } }; }).call(this); diff --git a/app/assets/javascripts/datepicker.js b/app/assets/javascripts/datepicker.js new file mode 100644 index 000000000..b44fa8e58 --- /dev/null +++ b/app/assets/javascripts/datepicker.js @@ -0,0 +1,31 @@ +// Based on code by Javan Makhmali +// https://github.com/turbolinks/turbolinks/issues/253#issuecomment-289101048 +// The jQuery UI date picker widget appends a shared element to the +// body which it expects will never leave the page, but Turbolinks +// removes that shared element when it rerenders. We satisfy that +// expectation by removing the shared element from the page before +// Turbolinks caches the page, and appending it again before +// Turbolinks swaps the new body in during rendering. +// +// Additionally, returning to the cached version of a page that +// previously had date picker elements would result in those date +// pickers not being initialized again. We fix this issue by finding +// all initialized date picker inputs on the page and calling the +// date picker's destroy method before Turbolinks caches the page. +(function() { + "use strict"; + App.Datepicker = { + destroy: function() { + $.datepicker.dpDiv.remove(); + + document.querySelectorAll("input.hasDatepicker").forEach(function(input) { + $(input).datepicker("hide"); + $(input).datepicker("destroy"); + }); + } + }; + + document.addEventListener("turbolinks:before-render", function(event) { + $.datepicker.dpDiv.appendTo(event.data.newBody); + }); +}).call(this); diff --git a/app/assets/javascripts/html_editor.js b/app/assets/javascripts/html_editor.js index 590a473ae..e1602509d 100644 --- a/app/assets/javascripts/html_editor.js +++ b/app/assets/javascripts/html_editor.js @@ -1,11 +1,6 @@ (function() { "use strict"; App.HTMLEditor = { - destroy: function() { - for (var name in CKEDITOR.instances) { - CKEDITOR.instances[name].destroy(); - } - }, initialize: function() { $("textarea.html-area").each(function() { if ($(this).hasClass("admin")) { @@ -14,9 +9,11 @@ CKEDITOR.replace(this.name, { language: $("html").attr("lang") }); } }); + }, + destroy: function() { + for (var name in CKEDITOR.instances) { + CKEDITOR.instances[name].destroy(); + } } }; - - $(document).on("page:before-unload", App.HTMLEditor.destroy); - $(document).on("page:restore", App.HTMLEditor.initialize); }).call(this); diff --git a/app/assets/javascripts/legislation_annotatable.js b/app/assets/javascripts/legislation_annotatable.js index 9b1125722..1b0c91ca7 100644 --- a/app/assets/javascripts/legislation_annotatable.js +++ b/app/assets/javascripts/legislation_annotatable.js @@ -186,6 +186,15 @@ } }; }, + initCommentFormToggler: function() { + $("body").on("click", ".comment-box a.publish-comment", function(e) { + e.preventDefault(); + var annotation_id = $(this).closest(".comment-box").data("id"); + $("a.publish-comment").hide(); + $("#js-comment-form-annotation-" + annotation_id).toggle(); + $("#js-comment-form-annotation-" + annotation_id + " textarea").trigger("focus"); + }); + }, initialize: function() { var current_user_id; $("body").on("renderLegislationAnnotation", App.LegislationAnnotatable.renderAnnotationComments); @@ -226,6 +235,13 @@ }); }); }); + + App.LegislationAnnotatable.initCommentFormToggler(); + }, + destroy: function() { + if ($(".legislation-annotatable").length > 0) { + App.LegislationAnnotatable.app.destroy(); + } } }; }).call(this); diff --git a/app/assets/javascripts/legislation_draft_versions.js b/app/assets/javascripts/legislation_draft_versions.js new file mode 100644 index 000000000..742f3d492 --- /dev/null +++ b/app/assets/javascripts/legislation_draft_versions.js @@ -0,0 +1,22 @@ +(function() { + "use strict"; + App.LegislationDraftVersions = { + msg: function() { + return $("[data-markdown-changes-message]").data("markdown-changes-message"); + }, + hasChanged: function() { + return $(".markdown-editor textarea").is(function() { + return this.value !== this.defaultValue; + }); + }, + checkChanges: function() { + if (App.LegislationDraftVersions.hasChanged()) { + return confirm(App.LegislationDraftVersions.msg()); + } else { + return true; + } + }, + }; + + $(document).on("turbolinks:before-visit", App.LegislationDraftVersions.checkChanges); +}).call(this); diff --git a/app/assets/javascripts/managers.js b/app/assets/javascripts/managers.js index 4a87fa7e4..b66914f2a 100644 --- a/app/assets/javascripts/managers.js +++ b/app/assets/javascripts/managers.js @@ -17,10 +17,14 @@ $("#user_password").prop("type", type); }, initialize: function() { - $(".generate-random-value").on("click", function() { + $(".generate-random-value").on("click", function(e) { + e.preventDefault(); + e.stopPropagation(); $("#user_password").val(App.Managers.generatePassword()); }); - $(".show-password").on("click", function() { + $(".show-password").on("click", function(e) { + e.preventDefault(); + e.stopPropagation(); if ($("#user_password").is("input[type='password']")) { App.Managers.togglePassword("text"); } else { diff --git a/app/assets/javascripts/map.js b/app/assets/javascripts/map.js index d160d3a92..dc9c92ef1 100644 --- a/app/assets/javascripts/map.js +++ b/app/assets/javascripts/map.js @@ -1,6 +1,7 @@ (function() { "use strict"; App.Map = { + maps: [], initialize: function() { $("*[data-map]:visible").each(function() { App.Map.initializeMap(this); @@ -11,19 +12,53 @@ } }); }, + destroy: function() { + App.Map.maps.forEach(function(map) { + map.off(); + map.remove(); + }); + App.Map.maps = []; + }, initializeMap: function(element) { - var addMarkerInvestments, clearFormfields, createMarker, editable, getPopupContent, latitudeInputSelector, longitudeInputSelector, map, mapAttribution, mapCenterLatLng, mapCenterLatitude, mapCenterLongitude, mapTilesProvider, marker, markerIcon, markerLatitude, markerLongitude, moveOrPlaceMarker, openMarkerPopup, removeMarker, removeMarkerSelector, updateFormfields, zoom, zoomInputSelector; + var addMarkerInvestments, clearFormfields, createMarker, dataCoordinates, editable, formCoordinates, + getPopupContent, latitudeInputSelector, longitudeInputSelector, map, mapAttribution, mapCenterLatLng, + mapCenterLatitude, mapCenterLongitude, mapTilesProvider, marker, markerIcon, markerLatitude, + markerLongitude, moveOrPlaceMarker, openMarkerPopup, removeMarker, removeMarkerSelector, + updateFormfields, zoom, zoomInputSelector; App.Map.cleanInvestmentCoordinates(element); - mapCenterLatitude = $(element).data("map-center-latitude"); - mapCenterLongitude = $(element).data("map-center-longitude"); - markerLatitude = $(element).data("marker-latitude"); - markerLongitude = $(element).data("marker-longitude"); - zoom = $(element).data("map-zoom"); mapTilesProvider = $(element).data("map-tiles-provider"); mapAttribution = $(element).data("map-tiles-provider-attribution"); latitudeInputSelector = $(element).data("latitude-input-selector"); longitudeInputSelector = $(element).data("longitude-input-selector"); zoomInputSelector = $(element).data("zoom-input-selector"); + formCoordinates = { + lat: $(latitudeInputSelector).val(), + long: $(longitudeInputSelector).val(), + zoom: $(zoomInputSelector).val() + }; + dataCoordinates = { + lat: $(element).data("marker-latitude"), + long: $(element).data("marker-longitude") + }; + if (App.Map.validCoordinates(formCoordinates)) { + markerLatitude = formCoordinates.lat; + markerLongitude = formCoordinates.long; + mapCenterLatitude = formCoordinates.lat; + mapCenterLongitude = formCoordinates.long; + } else if (App.Map.validCoordinates(dataCoordinates)) { + markerLatitude = dataCoordinates.lat; + markerLongitude = dataCoordinates.long; + mapCenterLatitude = dataCoordinates.lat; + mapCenterLongitude = dataCoordinates.lat; + } else { + mapCenterLatitude = $(element).data("map-center-latitude"); + mapCenterLongitude = $(element).data("map-center-longitude"); + } + if (App.Map.validZoom(formCoordinates.zoom)) { + zoom = formCoordinates.zoom; + } else { + zoom = $(element).data("map-zoom"); + } removeMarkerSelector = $(element).data("marker-remove-selector"); addMarkerInvestments = $(element).data("marker-investments-coordinates"); editable = $(element).data("marker-editable"); @@ -88,6 +123,7 @@ }; mapCenterLatLng = new L.LatLng(mapCenterLatitude, mapCenterLongitude); map = L.map(element.id).setView(mapCenterLatLng, zoom); + App.Map.maps.push(map); L.tileLayer(mapTilesProvider, { attribution: mapAttribution }).addTo(map); @@ -125,6 +161,9 @@ $(element).attr("data-marker-investments-coordinates", clean_markers); } }, + validZoom: function(zoom) { + return App.Map.isNumeric(zoom); + }, validCoordinates: function(coordinates) { return App.Map.isNumeric(coordinates.lat) && App.Map.isNumeric(coordinates.long); }, diff --git a/app/assets/javascripts/markdown_editor.js b/app/assets/javascripts/markdown_editor.js index 7369c356f..d8e34719f 100644 --- a/app/assets/javascripts/markdown_editor.js +++ b/app/assets/javascripts/markdown_editor.js @@ -22,14 +22,26 @@ }); editor = $(this); editor.on("input", function() { + var textarea, warning; + + textarea = editor.find("textarea")[0]; + warning = $(this).closest(".translatable-fields").find(".warning"); + App.MarkdownEditor.refresh_preview($(this), md); - $(".legislation-draft-versions-edit .warning").show(); + + if (textarea.value === textarea.defaultValue) { + warning.hide(); + } else { + warning.show(); + } }); editor.find("textarea").on("scroll", function() { editor.find(".markdown-preview").scrollTop($(this).scrollTop()); }); - editor.find(".fullscreen-toggle").on("click", function() { + editor.find(".fullscreen-toggle").on("click", function(e) { var span; + e.preventDefault(); + e.stopPropagation(); editor.toggleClass("fullscreen"); $(".fullscreen-container").toggleClass("medium-8", "medium-12"); span = $(this).find("span"); diff --git a/app/assets/javascripts/settings.js b/app/assets/javascripts/settings.js index cea8fcda7..57be249ae 100644 --- a/app/assets/javascripts/settings.js +++ b/app/assets/javascripts/settings.js @@ -3,12 +3,8 @@ App.Settings = { initialize: function() { $("#settings-tabs").on("change.zf.tabs", function() { - var map_container; if ($("#tab-map-configuration:visible").length) { - map_container = L.DomUtil.get("admin-map"); - if (map_container !== null) { - map_container._leaflet_id = null; - } + App.Map.destroy(); App.Map.initialize(); } }); diff --git a/app/assets/javascripts/social_share.js b/app/assets/javascripts/social_share.js index 34ae229ac..eb75fe479 100644 --- a/app/assets/javascripts/social_share.js +++ b/app/assets/javascripts/social_share.js @@ -5,6 +5,9 @@ $(".social-share-button a").each(function() { $(this).append("" + ($(this).data("site")) + ""); }); + }, + destroy: function() { + $(".social-share-button a .show-for-sr").remove(); } }; }).call(this); diff --git a/app/assets/javascripts/stat_graphs.js b/app/assets/javascripts/stat_graphs.js index 97d743355..b5aa71898 100644 --- a/app/assets/javascripts/stat_graphs.js +++ b/app/assets/javascripts/stat_graphs.js @@ -9,6 +9,5 @@ var initialize_stats_modules = function() { App.Stats.initialize(); }; -$(document).ready(initialize_stats_modules); -$(document).on("page:load", initialize_stats_modules); +$(document).on("turbolinks:load", initialize_stats_modules); $(document).on("ajax:complete", initialize_stats_modules); diff --git a/app/assets/javascripts/turbolinks_anchors.js b/app/assets/javascripts/turbolinks_anchors.js new file mode 100644 index 000000000..65923c0fb --- /dev/null +++ b/app/assets/javascripts/turbolinks_anchors.js @@ -0,0 +1,15 @@ +(function() { + "use strict"; + + // Code by Dom Christie: + // https://github.com/turbolinks/turbolinks/issues/75#issuecomment-443256173 + document.addEventListener("turbolinks:click", function(event) { + if (event.target.getAttribute("href").charAt(0) === "#") { + Turbolinks.controller.pushHistoryWithLocationAndRestorationIdentifier( + event.data.url, + Turbolinks.uuid() + ); + event.preventDefault(); + } + }); +}).call(this); diff --git a/app/assets/javascripts/watch_form_changes.js b/app/assets/javascripts/watch_form_changes.js deleted file mode 100644 index 104d8d4d0..000000000 --- a/app/assets/javascripts/watch_form_changes.js +++ /dev/null @@ -1,32 +0,0 @@ -(function() { - "use strict"; - App.WatchFormChanges = { - forms: function() { - return $("form[data-watch-changes]"); - }, - msg: function() { - return $("[data-watch-form-message]").data("watch-form-message"); - }, - hasChanged: function() { - return App.WatchFormChanges.forms().is(function() { - return $(this).serialize() !== $(this).data("watchChanges"); - }); - }, - checkChanges: function() { - if (App.WatchFormChanges.hasChanged()) { - return confirm(App.WatchFormChanges.msg()); - } else { - return true; - } - }, - initialize: function() { - if (App.WatchFormChanges.forms().length === 0 || App.WatchFormChanges.msg() === undefined) { - return; - } - $(document).off("page:before-change").on("page:before-change", App.WatchFormChanges.checkChanges); - App.WatchFormChanges.forms().each(function() { - $(this).data("watchChanges", $(this).serialize()); - }); - } - }; -}).call(this); diff --git a/app/views/admin/budget_investments/show.html.erb b/app/views/admin/budget_investments/show.html.erb index 375c5e978..3727c5f4b 100644 --- a/app/views/admin/budget_investments/show.html.erb +++ b/app/views/admin/budget_investments/show.html.erb @@ -1,5 +1,5 @@ <%= link_to admin_budget_budget_investments_path(Budget::Investment.filter_params(params).to_h), - class: "back", data: { no_turbolink: true } do %> + class: "back", data: { turbolinks: false } do %> <%= t("shared.back") %> <% end %> diff --git a/app/views/admin/legislation/draft_versions/_form.html.erb b/app/views/admin/legislation/draft_versions/_form.html.erb index 3d500214c..9298990cd 100644 --- a/app/views/admin/legislation/draft_versions/_form.html.erb +++ b/app/views/admin/legislation/draft_versions/_form.html.erb @@ -1,11 +1,18 @@ <%= render "shared/globalize_locales", resource: @draft_version %> -<%= translatable_form_for [:admin, @process, @draft_version], url: url, html: { data: { watch_changes: true }} do |f| %> +<%= translatable_form_for [:admin, @process, @draft_version], url: url, + html: { data: { markdown_changes_message: I18n.t("admin.legislation.draft_versions.edit.markdown_changes_message") }} do |f| %> <%= render "shared/errors", resource: @draft_version %>