diff --git a/Gemfile b/Gemfile
index dc28acd5f..4f54b0034 100755
--- a/Gemfile
+++ b/Gemfile
@@ -89,3 +89,5 @@ group :test do
gem 'capybara'
gem 'selenium-webdriver'
end
+
+gem 'importmap-rails', '~> 2.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 02eb9d4ff..69de8ac34 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -182,6 +182,10 @@ GEM
thor
i18n (1.14.1)
concurrent-ruby (~> 1.0)
+ importmap-rails (2.0.1)
+ actionpack (>= 6.0.0)
+ activesupport (>= 6.0.0)
+ railties (>= 6.0.0)
intercom-rails (0.4.2)
activesupport (> 3.0)
io-console (0.7.2)
@@ -505,6 +509,7 @@ DEPENDENCIES
devise_invitable (~> 2.0)
font-awesome-sass
gabba
+ importmap-rails (~> 2.0)
intercom-rails
jbuilder (~> 2.7)
jquery-rails
diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js
index 4268d2c77..642ce2235 100644
--- a/app/assets/config/manifest.js
+++ b/app/assets/config/manifest.js
@@ -1,3 +1,7 @@
//= link_tree ../images
//= link application.js
//= link 'application.css'
+//= link_tree ../../javascript .js
+//= link_tree ../../../vendor/javascript .js
+
+//= link local-time/app/assets/javascripts/local-time.es2017-umd.js
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 5cc4ea896..c9af3cf77 100755
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -11,6 +11,9 @@
// about supported directives.
//
// The long list of requires for the main Angular 1 application has been moved to core.js.
+//
+// We have introduced importmaps, which live in application2.js. This should probably be migrated
+// over to that file assuming import maps works the way we are hoping!
//= require bootstrap/dist/js/bootstrap.bundle
//= require jquery
@@ -23,5 +26,3 @@
//= require codemirror/lib/codemirror
//= require codemirror/mode/javascript/javascript
-
-//= require local-time/app/assets/javascripts/local-time
diff --git a/app/javascript/application2.js b/app/javascript/application2.js
new file mode 100644
index 000000000..8e441626e
--- /dev/null
+++ b/app/javascript/application2.js
@@ -0,0 +1,10 @@
+// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
+// This is a importmap version of the classic application.js so that we can have
+// both sprockets and importmaps at the same time.
+
+
+// Note: the file in vendor/javascript/vendored-local-time.js is the one that was downloaded via
+// importmap pin. See https://github.com/basecamp/local_time/issues/113 for others who suggested
+// remaining it
+import LocalTime from "local-time"
+LocalTime.start()
diff --git a/app/views/admin/announcements/edit.html.erb b/app/views/admin/announcements/edit.html.erb
index eaad14b5d..0d881f75e 100644
--- a/app/views/admin/announcements/edit.html.erb
+++ b/app/views/admin/announcements/edit.html.erb
@@ -7,7 +7,7 @@
<%= form.label :text, class: 'form-label' %>
- <%= form.text_field :text, required: true, class: 'form-control' %>
+ <%= form.text_area :text, required: true, rows: 5, class: 'form-control' %>
The announcement. You can use HTML and emojis.
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 4c477acd8..d8bbf4c53 100755
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -1,9 +1,9 @@
-
+
-<%= favicon_link_tag asset_path "favicon.ico" %>
+ <%= favicon_link_tag asset_path "favicon.ico" %>
<% if flash[:unfurl] %>
@@ -54,9 +54,11 @@
-<%= stylesheet_link_tag 'application', media: 'all' %>
-<%= csrf_meta_tags %>
-<%= javascript_include_tag 'application' %>
+ <%= stylesheet_link_tag 'application', media: 'all' %>
+ <%= csrf_meta_tags %>
+ <%= javascript_importmap_tags 'application2' %>
+ <%= javascript_include_tag 'application' %>
+
diff --git a/bin/importmap b/bin/importmap
new file mode 100755
index 000000000..36502ab16
--- /dev/null
+++ b/bin/importmap
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+
+require_relative "../config/application"
+require "importmap/commands"
diff --git a/config/importmap.rb b/config/importmap.rb
new file mode 100644
index 000000000..f303acace
--- /dev/null
+++ b/config/importmap.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+# Pin npm packages by running ./bin/importmap
+
+pin 'application2', preload: true
+pin "local-time", to: "vendored-local-time.js"# @3.0.2
diff --git a/package.json b/package.json
index 26e631062..abf198dbb 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"d3-tip": "~0.9.1",
"file-saver": "^2.0.5",
"jquery": "~3.7.0",
- "local-time": "^2.1.0",
+ "local-time": "^3.0.0",
"ng-json-explorer": "http://github.com/o19s/ng-json-explorer",
"ng-tags-input": "3.2.0",
"ngclipboard": "^2.0.0",
diff --git a/spec/karma/config/unit.js b/spec/karma/config/unit.js
index 34f34184f..be9ffa1cf 100644
--- a/spec/karma/config/unit.js
+++ b/spec/karma/config/unit.js
@@ -19,7 +19,7 @@ module.exports = function(config) {
// to run a single one
files: [
'tmp/assets/core*.js',
- 'tmp/assets/application*.js',
+ 'tmp/assets/application.js',
'tmp/assets/application_spec*.js',
'spec/javascripts/mock/*.js',
'spec/javascripts/**/*_spec.js',
@@ -28,7 +28,7 @@ module.exports = function(config) {
// list of files to exclude
exclude: [
-
+ '**/application2.js', // ignore this importmap related file when running core JS app tests
],
diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/javascript/vendored-local-time.js b/vendor/javascript/vendored-local-time.js
new file mode 100644
index 000000000..3776cc68e
--- /dev/null
+++ b/vendor/javascript/vendored-local-time.js
@@ -0,0 +1,2 @@
+var t;t={config:{},run:function(){return this.getController().processElements()},process:function(...t){var e,r,a;for(r=0,a=t.length;r11?"pm":"am")).toUpperCase();case"P":return M("time."+(n>11?"pm":"am"));case"S":return p(o,l);case"w":return a;case"y":return p(u%100,l);case"Y":return u;case"Z":return S(t)}}))},p=function(t,e){return"-"===e?t:`0${t}`.slice(-2)},S=function(t){var e,r,a;return(r=h(t))?g[r]:(a=y(t,{allowGMT:!1}))||(a=v(t))?a:(e=y(t,{allowGMT:!0}))?e:""},h=function(t){return Object.keys(g).find((function(e){return b?new Date(t).toLocaleString("en-US",{timeZoneName:"long"}).includes(e):t.toString().includes(e)}))},y=function(t,{allowGMT:e}){var r;if(b&&(r=new Date(t).toLocaleString("en-US",{timeZoneName:"short"}).split(" ").pop(),e||!r.includes("GMT")))return r},v=function(t){var e,r,a,n,s;return(e=null!=(r=(s=t.toString()).match(/\(([\w\s]+)\)$/))?r[1]:void 0)?/\s/.test(e)?e.match(/\b(\w)/g).join(""):e:(e=null!=(a=s.match(/(\w{3,4})\s\d{4}$/))?a[1]:void 0)||(e=null!=(n=s.match(/(UTC[\+\-]\d+)/))?n[1]:void 0)?e:void 0},L.CalendarDate=class{static fromDate(t){return new this(t.getFullYear(),t.getMonth()+1,t.getDate())}static today(){return this.fromDate(new Date)}constructor(t,e,r){this.date=new Date(Date.UTC(t,e-1)),this.date.setUTCDate(r),this.year=this.date.getUTCFullYear(),this.month=this.date.getUTCMonth()+1,this.day=this.date.getUTCDate(),this.value=this.date.getTime()}equals(t){return(null!=t?t.value:void 0)===this.value}is(t){return this.equals(t)}isToday(){return this.is(this.constructor.today())}occursOnSameYearAs(t){return this.year===(null!=t?t.year:void 0)}occursThisYear(){return this.occursOnSameYearAs(this.constructor.today())}daysSince(t){if(t)return(this.date-t.date)/864e5}daysPassed(){return this.constructor.today().daysSince(this)}},({strftime:E,translate:I,getI18nValue:w,config:D}=L),L.RelativeTime=class{constructor(t){this.date=t,this.calendarDate=L.CalendarDate.fromDate(this.date)}toString(){var t,e;return(e=this.toTimeElapsedString())?I("time.elapsed",{time:e}):(t=this.toWeekdayString())?(e=this.toTimeString(),I("datetime.at",{date:t,time:e})):I("date.on",{date:this.toDateString()})}toTimeOrDateString(){return this.calendarDate.isToday()?this.toTimeString():this.toDateString()}toTimeElapsedString(){var t,e,r,a,n;return r=(new Date).getTime()-this.date.getTime(),a=Math.round(r/1e3),e=Math.round(a/60),t=Math.round(e/60),r<0?null:a<10?(n=I("time.second"),I("time.singular",{time:n})):a<45?`${a} ${I("time.seconds")}`:a<90?(n=I("time.minute"),I("time.singular",{time:n})):e<45?`${e} ${I("time.minutes")}`:e<90?(n=I("time.hour"),I("time.singularAn",{time:n})):t<24?`${t} ${I("time.hours")}`:""}toWeekdayString(){switch(this.calendarDate.daysPassed()){case 0:return I("date.today");case 1:return I("date.yesterday");case-1:return I("date.tomorrow");case 2:case 3:case 4:case 5:case 6:return E(this.date,"%A");default:return""}}toDateString(){var t;return t=this.calendarDate.occursThisYear()?w("date.formats.thisYear"):w("date.formats.default"),E(this.date,t)}toTimeString(){var t;return t=D.useFormat24?"default_24h":"default",E(this.date,w(`time.formats.${t}`))}},({elementMatchesSelector:C}=L),L.PageObserver=class{constructor(t,e){this.processMutations=this.processMutations.bind(this),this.processInsertion=this.processInsertion.bind(this),this.selector=t,this.callback=e}start(){if(!this.started)return this.observeWithMutationObserver()||this.observeWithMutationEvent(),this.started=!0}observeWithMutationObserver(){if("undefined"!=typeof MutationObserver&&null!==MutationObserver)return new MutationObserver(this.processMutations).observe(document.documentElement,{childList:!0,subtree:!0}),!0}observeWithMutationEvent(){return addEventListener("DOMNodeInserted",this.processInsertion,!1),!0}findSignificantElements(t){var e;return e=[],(null!=t?t.nodeType:void 0)===Node.ELEMENT_NODE&&(C(t,this.selector)&&e.push(t),e.push(...t.querySelectorAll(this.selector))),e}processMutations(t){var e,r,a,n,s,i,o,u;for(e=[],r=0,n=t.length;r