easyfaa 0.20.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +131 -468
- data/README.md +49 -222
- data/app/assets/fonts/easy/outfit-latin-ext.woff2 +0 -0
- data/app/assets/fonts/easy/outfit-latin.woff2 +0 -0
- data/app/assets/images/easy/favicon.ico +0 -0
- data/app/assets/images/easy/favicon.svg +11 -0
- data/app/assets/images/easy/sprite.svg +52 -0
- data/app/assets/javascripts/easy/add_rows.js +38 -0
- data/app/assets/javascripts/easy/flyer.js +100 -0
- data/app/assets/javascripts/easy/highlight.js +54 -0
- data/app/assets/javascripts/easy/konami.js +73 -0
- data/app/assets/javascripts/easy/modal.js +49 -0
- data/app/assets/javascripts/easy/pin_code.js +43 -0
- data/app/assets/javascripts/easy/report_sort.js +57 -0
- data/app/assets/javascripts/easy/rotating_placeholder.js +35 -0
- data/app/assets/javascripts/easy/step_foot.js +17 -0
- data/app/assets/javascripts/easy/switches.js +96 -0
- data/app/assets/javascripts/easy/switches_address.js +39 -0
- data/app/assets/javascripts/easy/tabs.js +35 -0
- data/app/assets/javascripts/easy/theme.js +41 -0
- data/app/assets/stylesheets/easy/base.css +42 -0
- data/app/assets/stylesheets/easy/components.css +533 -0
- data/app/assets/stylesheets/easy/layout.css +47 -0
- data/app/assets/stylesheets/easy/tokens.css +11 -0
- data/app/controllers/easyfaa/errors_controller.rb +41 -0
- data/app/helpers/easyfaa/easy_helper.rb +58 -0
- data/app/models/aircraft_avatar.rb +61 -0
- data/app/views/easy/_ad_card.html.erb +16 -0
- data/app/views/easy/_ad_list.html.erb +11 -0
- data/app/views/easy/_flyer.html.erb +10 -0
- data/app/views/easy/_logo.html.erb +15 -0
- data/app/views/easy/_modal.html.erb +17 -0
- data/app/views/easy/_part_tabs.html.erb +24 -0
- data/app/views/easy/_pin_code.html.erb +10 -0
- data/app/views/easy/_report_head.html.erb +28 -0
- data/app/views/easy/mailer/_button.html.erb +10 -0
- data/app/views/easy/mailer/_card.html.erb +23 -0
- data/app/views/easy/mailer/_code.html.erb +28 -0
- data/app/views/easy/mailer/_code.text.erb +19 -0
- data/app/views/easy/mailer/_frame.html.erb +35 -0
- data/app/views/easy/mailer/_text.html.erb +7 -0
- data/app/views/easyfaa/errors/show.html.erb +17 -0
- data/app/views/layouts/easy/site.html.erb +55 -0
- data/app/views/layouts/easyfaa/errors.html.erb +30 -0
- data/config/aircraft_avatars.yml +26 -0
- data/lib/easyfaa/engine.rb +16 -43
- data/lib/easyfaa/version.rb +2 -2
- data/lib/easyfaa.rb +14 -35
- metadata +52 -181
- data/app/views/layouts/easyfaa.html.erb +0 -24
- data/app/views/recourses/_head.html.erb +0 -3
- data/art/autopilot.svg +0 -9
- data/art/cube.svg +0 -9
- data/art/design.svg +0 -8
- data/art/down.html.erb +0 -95
- data/art/easyfaa.svg +0 -7
- data/art/fountain.svg +0 -9
- data/art/goldrush.svg +0 -7
- data/art/houston.svg +0 -8
- data/art/icons.txt +0 -185
- data/art/index.html.erb +0 -157
- data/config/locales/easyfaa.en.yml +0 -7
- data/lib/easyfaa/copy.rb +0 -5
- data/lib/easyfaa/down.rb +0 -19
- data/lib/easyfaa/helpers/flows.rb +0 -53
- data/lib/easyfaa/helpers/heads.rb +0 -60
- data/lib/easyfaa/helpers/logos.rb +0 -43
- data/lib/easyfaa/helpers.rb +0 -11
- data/lib/easyfaa/marks.rb +0 -16
- data/public/404.html +0 -118
- data/public/_headers +0 -28
- data/public/bh/css/bh.css +0 -1
- data/public/bh/js/bh.js +0 -152
- data/public/bh/theme/bootstrap.css +0 -10
- data/public/bh/theme/dawn.css +0 -189
- data/public/bh/theme/dracula.css +0 -189
- data/public/bh/theme/gruvbox.css +0 -189
- data/public/bh/theme/monokai.css +0 -190
- data/public/bh/theme/nord.css +0 -189
- data/public/bh/theme/one_dark.css +0 -189
- data/public/bh/theme/solarized.css +0 -188
- data/public/bh/theme/tokyo_night.css +0 -189
- data/public/css/easyfaa.css +0 -1
- data/public/down/autopilot.html +0 -87
- data/public/down/cube.html +0 -87
- data/public/down/design.html +0 -87
- data/public/down/easyfaa.html +0 -84
- data/public/down/fountain.html +0 -87
- data/public/down/goldrush.html +0 -87
- data/public/down/houston.html +0 -87
- data/public/favicon/autopilot-180.png +0 -0
- data/public/favicon/autopilot-192.png +0 -0
- data/public/favicon/autopilot-512.png +0 -0
- data/public/favicon/autopilot-96.png +0 -0
- data/public/favicon/autopilot-dev.svg +0 -9
- data/public/favicon/autopilot.ico +0 -0
- data/public/favicon/autopilot.svg +0 -9
- data/public/favicon/autopilot.webmanifest +0 -12
- data/public/favicon/cube-180.png +0 -0
- data/public/favicon/cube-192.png +0 -0
- data/public/favicon/cube-512.png +0 -0
- data/public/favicon/cube-96.png +0 -0
- data/public/favicon/cube-dev.svg +0 -9
- data/public/favicon/cube.ico +0 -0
- data/public/favicon/cube.svg +0 -9
- data/public/favicon/cube.webmanifest +0 -12
- data/public/favicon/design-180.png +0 -0
- data/public/favicon/design-192.png +0 -0
- data/public/favicon/design-512.png +0 -0
- data/public/favicon/design-96.png +0 -0
- data/public/favicon/design-dev.svg +0 -8
- data/public/favicon/design.ico +0 -0
- data/public/favicon/design.svg +0 -8
- data/public/favicon/design.webmanifest +0 -12
- data/public/favicon/easyfaa-180.png +0 -0
- data/public/favicon/easyfaa-192.png +0 -0
- data/public/favicon/easyfaa-512.png +0 -0
- data/public/favicon/easyfaa-96.png +0 -0
- data/public/favicon/easyfaa-dev.svg +0 -7
- data/public/favicon/easyfaa.ico +0 -0
- data/public/favicon/easyfaa.svg +0 -7
- data/public/favicon/easyfaa.webmanifest +0 -12
- data/public/favicon/fountain-180.png +0 -0
- data/public/favicon/fountain-192.png +0 -0
- data/public/favicon/fountain-512.png +0 -0
- data/public/favicon/fountain-96.png +0 -0
- data/public/favicon/fountain-dev.svg +0 -9
- data/public/favicon/fountain.ico +0 -0
- data/public/favicon/fountain.svg +0 -9
- data/public/favicon/fountain.webmanifest +0 -12
- data/public/favicon/goldrush-180.png +0 -0
- data/public/favicon/goldrush-192.png +0 -0
- data/public/favicon/goldrush-512.png +0 -0
- data/public/favicon/goldrush-96.png +0 -0
- data/public/favicon/goldrush-dev.svg +0 -7
- data/public/favicon/goldrush.ico +0 -0
- data/public/favicon/goldrush.svg +0 -7
- data/public/favicon/goldrush.webmanifest +0 -12
- data/public/favicon/houston-180.png +0 -0
- data/public/favicon/houston-192.png +0 -0
- data/public/favicon/houston-512.png +0 -0
- data/public/favicon/houston-96.png +0 -0
- data/public/favicon/houston-dev.svg +0 -8
- data/public/favicon/houston.ico +0 -0
- data/public/favicon/houston.svg +0 -8
- data/public/favicon/houston.webmanifest +0 -12
- data/public/favicon.ico +0 -0
- data/public/font/bootstrap-icons-6LILWPE2.woff2 +0 -0
- data/public/font/bootstrap-icons-FRBPO3H7.woff2 +0 -0
- data/public/font/ibm-plex-sans-500-latin-LA7QXWWL.woff2 +0 -0
- data/public/font/ibm-plex-sans-500-latin-ext-X4QHZICY.woff2 +0 -0
- data/public/font/ibm-plex-sans-500-latin-ext.woff2 +0 -0
- data/public/font/ibm-plex-sans-500-latin.woff2 +0 -0
- data/public/font/ibm-plex-sans-500-vietnamese-24B6BSND.woff2 +0 -0
- data/public/font/ibm-plex-sans-500-vietnamese.woff2 +0 -0
- data/public/index.html +0 -165
- data/public/js/easyfaa.js +0 -1
- data/public/logo/autopilot.png +0 -0
- data/public/logo/autopilot.svg +0 -9
- data/public/logo/cube.png +0 -0
- data/public/logo/cube.svg +0 -9
- data/public/logo/design.png +0 -0
- data/public/logo/design.svg +0 -8
- data/public/logo/easyfaa.png +0 -0
- data/public/logo/easyfaa.svg +0 -7
- data/public/logo/fountain.png +0 -0
- data/public/logo/fountain.svg +0 -9
- data/public/logo/goldrush.png +0 -0
- data/public/logo/goldrush.svg +0 -7
- data/public/logo/houston.png +0 -0
- data/public/logo/houston.svg +0 -8
- data/public/page/400.html +0 -118
- data/public/page/404.html +0 -118
- data/public/page/406-unsupported-browser.html +0 -118
- data/public/page/422.html +0 -118
- data/public/page/500.html +0 -118
- data/public/site.webmanifest +0 -12
- data/public/theme/bootstrap.css +0 -10
- data/public/theme/dawn.css +0 -189
- data/public/theme/dracula.css +0 -189
- data/public/theme/gruvbox.css +0 -189
- data/public/theme/monokai.css +0 -190
- data/public/theme/nord.css +0 -189
- data/public/theme/one_dark.css +0 -189
- data/public/theme/solarized.css +0 -188
- data/public/theme/tokyo_night.css +0 -189
- data/public/v0.20.0/bh/css/bh.css +0 -1
- data/public/v0.20.0/bh/js/bh.js +0 -152
- data/public/v0.20.0/bh/theme/bootstrap.css +0 -10
- data/public/v0.20.0/bh/theme/dawn.css +0 -189
- data/public/v0.20.0/bh/theme/dracula.css +0 -189
- data/public/v0.20.0/bh/theme/gruvbox.css +0 -189
- data/public/v0.20.0/bh/theme/monokai.css +0 -190
- data/public/v0.20.0/bh/theme/nord.css +0 -189
- data/public/v0.20.0/bh/theme/one_dark.css +0 -189
- data/public/v0.20.0/bh/theme/solarized.css +0 -188
- data/public/v0.20.0/bh/theme/tokyo_night.css +0 -189
- data/public/v0.20.0/css/easyfaa.css +0 -1
- data/public/v0.20.0/font/bootstrap-icons-FRBPO3H7.woff2 +0 -0
- data/public/v0.20.0/font/ibm-plex-sans-500-latin-LA7QXWWL.woff2 +0 -0
- data/public/v0.20.0/font/ibm-plex-sans-500-latin-ext-X4QHZICY.woff2 +0 -0
- data/public/v0.20.0/font/ibm-plex-sans-500-latin-ext.woff2 +0 -0
- data/public/v0.20.0/font/ibm-plex-sans-500-latin.woff2 +0 -0
- data/public/v0.20.0/font/ibm-plex-sans-500-vietnamese-24B6BSND.woff2 +0 -0
- data/public/v0.20.0/font/ibm-plex-sans-500-vietnamese.woff2 +0 -0
- data/public/v0.20.0/js/easyfaa.js +0 -1
data/README.md
CHANGED
|
@@ -1,256 +1,83 @@
|
|
|
1
|
-
#
|
|
1
|
+
# easyFAA design
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
The Bootstrap 6 underneath is [`bh`](https://claudiob.github.io/bh/), which this gem depends on
|
|
8
|
-
and draws on top of rather than bundling: a page links bh's stylesheet and script first and this
|
|
9
|
-
one's after, so the house rules win and Bootstrap ships once. **The components are documented
|
|
10
|
-
there, not here** — the form builder, the combobox, the 6-digit code, the phone field, the
|
|
11
|
-
dialog, the toasts, the thread of messages and the twenty-two Stimulus controllers are Bootstrap
|
|
12
|
-
with the markup written once, and are worth the same to any Rails app. This gem is only what is
|
|
13
|
-
the house's.
|
|
14
|
-
|
|
15
|
-
Published three ways from this repo:
|
|
3
|
+
The front end every easyFAA app shares: the easy design system its pages are drawn with (the
|
|
4
|
+
layout, stylesheets, scripts, fonts, icons and partials) and the error pages every app shows.
|
|
5
|
+
Published two ways from `design/` in the brand's repo (`github.com/easyfaa/easyfaa`):
|
|
16
6
|
|
|
17
7
|
| Surface | Where | What it is for |
|
|
18
8
|
| --- | --- | --- |
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
| npm | `easyfaa` | The same built stylesheet and script, for any CDN that carries it |
|
|
9
|
+
| Gem | `easyfaa` on RubyGems | A Rails engine: lends an app its views, assets, helpers and error pages |
|
|
10
|
+
| Site | https://easyfaa.com/design | The style guide, every token and component with its markup, and the marks |
|
|
22
11
|
|
|
23
12
|
## How to install
|
|
24
13
|
|
|
25
|
-
### From RubyGems
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
gem install easyfaa
|
|
29
|
-
```
|
|
30
|
-
|
|
31
14
|
```ruby
|
|
32
15
|
# Gemfile
|
|
33
|
-
gem 'easyfaa', '~>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
bh comes with it; nothing else has to be named.
|
|
37
|
-
|
|
38
|
-
`~> 0.15.0` stops short of `0.16`, since below 1.0 a minor release may break whatever it likes.
|
|
39
|
-
Rails 8.1 and Ruby 3.2 are the minimum.
|
|
40
|
-
|
|
41
|
-
A page takes the gem's layout, and wears the same head:
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
class Unauthenticated::AgentsController < ApplicationController
|
|
45
|
-
layout 'easyfaa'
|
|
46
|
-
end
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
```erb
|
|
50
|
-
<% content_for :title, 'Authenticating as admin' %>
|
|
51
|
-
<% content_for :head, tag.meta(name: 'robots', content: 'noindex') %>
|
|
52
|
-
<h1>Authenticating as admin</h1>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
<p>
|
|
56
|
-
<img src='screenshot/layout-light.png' width='49%' alt='The page in light mode'>
|
|
57
|
-
<img src='screenshot/layout-dark.png' width='49%' alt='The page in dark mode'>
|
|
58
|
-
</p>
|
|
59
|
-
|
|
60
|
-
The layout is the charset, the title, the CSRF and CSP metas, `easyfaa_head_tags`,
|
|
61
|
-
`yield :head`, then `yield` inside a `main.container-fluid` in the `<body>`, and `notices`
|
|
62
|
-
after it, and nothing else. A host wanting more copies
|
|
63
|
-
it into its own `app/views/layouts/`, which wins on being earlier in the view paths.
|
|
64
|
-
|
|
65
|
-
Which of the family an app is, the gem reads off the Rails application's own name: the
|
|
66
|
-
`Goldrush` of `Goldrush::Application`. That is what its tab wears, what `easyfaa_head_tags`
|
|
67
|
-
links without an argument, and whose page the engine answers `/down.html` with — the app's
|
|
68
|
-
mark in the middle and the house's at the foot. An app the family has no mark for wears the
|
|
69
|
-
horizon. The same page stands at `https://design.easyfaa.com/down/fountain` for Heroku's
|
|
70
|
-
`MAINTENANCE_PAGE_URL`, which has to be somewhere the app is not — without the `.html`,
|
|
71
|
-
which Pages redirects away from.
|
|
72
|
-
|
|
73
|
-
With the gem in the bundle an app serves `/css/easyfaa.css`, `/js/easyfaa.js`,
|
|
74
|
-
`/theme/*`, `/font/*`, `/logo/*` and `/favicon/*` itself — and its own version's copy under
|
|
75
|
-
`/v0.15.0/`, which is what the head helper links, cached for a year — answers a 400, 404, 406,
|
|
76
|
-
422 or 500 with the house's own page, and answers `/down.html` with this app's.
|
|
77
|
-
|
|
78
|
-
## Which app this is
|
|
79
|
-
|
|
80
|
-
Read off the Rails application's own name, the `Goldrush` of `Goldrush::Application`. That is
|
|
81
|
-
what its tab wears, what `easyfaa_head_tags` links without an argument, and whose page the
|
|
82
|
-
engine answers `/down.html` with — the app's mark in the middle and the house's at the foot. An
|
|
83
|
-
app the family has no mark for wears the horizon.
|
|
84
|
-
|
|
85
|
-
## The helpers
|
|
86
|
-
|
|
87
|
-
```erb
|
|
88
|
-
<%# In the <head>: the viewport, color-scheme and Turbo refresh metas, the stylesheet, the
|
|
89
|
-
script, and the icons a tab, a phone and a manifest ask for — this app's, by its name. %>
|
|
90
|
-
<%= easyfaa_head_tags %>
|
|
91
|
-
|
|
92
|
-
<%# The icons alone, for a head of the host's own: this app's unless another of the family is
|
|
93
|
-
named, the way a page for customers wears the house's horizon rather than the app's. A host of
|
|
94
|
-
recourse needs no call at all — the gem answers recourse's `recourses/head` with them. %>
|
|
95
|
-
<%= easyfaa_icons :easyfaa %>
|
|
96
|
-
|
|
97
|
-
<%# A mark beside its name, as one word and one link. Each drawing fills its 512 grid
|
|
98
|
-
differently, so equal sizes never align: every mark is rendered at a box of its own and
|
|
99
|
-
pushed down by its own nudge, and its ink then stands on the baseline. %>
|
|
100
|
-
<%= easyfaa_logo %>
|
|
101
|
-
<%= easyfaa_logo app: :fountain, href: root_path, class: 'fs-3' %>
|
|
102
|
-
|
|
103
|
-
<%# An asset on the design site, for a page this app does not serve — a mail, say. %>
|
|
104
|
-
<%= image_tag easyfaa_url('logo/fountain.png') %>
|
|
16
|
+
gem 'easyfaa', '~> 1.0'
|
|
105
17
|
```
|
|
106
18
|
|
|
107
|
-
|
|
19
|
+
`~> 1.0` takes every 1.x and stops short of 2.0, the next release that may break an app.
|
|
20
|
+
Rails 8.1 and Ruby 3.4 are the minimum, and the app brings its own asset pipeline (Propshaft).
|
|
108
21
|
|
|
109
|
-
|
|
22
|
+
## The easy design system
|
|
110
23
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
layout 'easyfaa'
|
|
114
|
-
end
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
```erb
|
|
118
|
-
<% content_for :title, 'Authenticating as admin' %>
|
|
119
|
-
<% content_for :head, tag.meta(name: 'robots', content: 'noindex') %>
|
|
120
|
-
<h1>Authenticating as admin</h1>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
<p>
|
|
124
|
-
<img src='screenshot/layout-light.png' width='49%' alt='The page in light mode'>
|
|
125
|
-
<img src='screenshot/layout-dark.png' width='49%' alt='The page in dark mode'>
|
|
126
|
-
</p>
|
|
127
|
-
|
|
128
|
-
It is the charset, the title, the CSRF and CSP metas, `easyfaa_head_tags`, `yield :head`,
|
|
129
|
-
then `yield` inside a `main.container-fluid`, and `notices` after it — and nothing else. A host
|
|
130
|
-
wanting more copies it into its own `app/views/layouts/`, which wins on being earlier in the
|
|
131
|
-
view paths.
|
|
132
|
-
|
|
133
|
-
## The flow, and the wall it stands on
|
|
134
|
-
|
|
135
|
-
`flow` is the page a signup flow lives on: the lockup over the cyanotype, a title, a line, one
|
|
136
|
-
card, and the way out under it. A layout of the app's own calls it and nests on the gem's:
|
|
24
|
+
What every easyFAA app's pages are drawn with. The stylesheets, scripts, fonts and icons are
|
|
25
|
+
under `app/assets`, which the host's asset pipeline reads as its own:
|
|
137
26
|
|
|
138
27
|
```erb
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<% content_for :
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<% end %>
|
|
146
|
-
|
|
147
|
-
<%= render template: 'layouts/easyfaa' %>
|
|
28
|
+
<%# The app's own layout fills the slots, then renders the shared page. %>
|
|
29
|
+
<% content_for :easy_head do %><title>easyFAA</title><% end %>
|
|
30
|
+
<% content_for :site_brand, "easyFAA" %>
|
|
31
|
+
<% content_for :site_nav do %><%= link_to "Sign in", sign_in_path %><% end %>
|
|
32
|
+
<%= javascript_include_tag "easy/modal", "easy/pin_code", defer: true %>
|
|
33
|
+
<%= render template: "layouts/easy/site" %>
|
|
148
34
|
```
|
|
149
35
|
|
|
150
|
-
`
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
36
|
+
`layouts/easy/site` links `easy/tokens`, `easy/base`, `easy/layout` and `easy/components` and
|
|
37
|
+
yields `:easy_head`, `:easy_stylesheets`, `:body_class`, `:site_brand_url`, `:site_brand`,
|
|
38
|
+
`:site_nav`, `:sky_band`, `:page_class`, `:site_footer` and `:body_end`. The partials are
|
|
39
|
+
`easy/pin_code`, `easy/modal` (a layout), `easy/flyer`, `easy/report_head` and
|
|
40
|
+
`easy/mailer/code`; the scripts beside them are in `app/assets/javascripts/easy`.
|
|
155
41
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
the columns stand still.
|
|
42
|
+
`easy_icon "plane"` draws an icon from the sprite every page shares (`easy_icon_href` is its
|
|
43
|
+
address), and `easy_aircraft_avatar AircraftAvatar.for(...)` an aircraft's silhouette until it
|
|
44
|
+
has a picture.
|
|
160
45
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
The same words from every app, in each voice they are said in. `written` is the sentence a
|
|
164
|
-
person reads and, under it, the `@host #code` line that offers the code above the keyboard on
|
|
165
|
-
the phone it arrived on; `spoken` is the sentence alone, its digits one at a time so a listener
|
|
166
|
-
can write them down.
|
|
46
|
+
The error pages name easyFAA, or the site an app names for the request:
|
|
167
47
|
|
|
168
48
|
```ruby
|
|
169
|
-
|
|
170
|
-
# => "Enter this 6-digit code to proceed: 482913\n\n@easyfaa.com #482913"
|
|
171
|
-
Easyfaa.one_time_code(contact.pin).spoken
|
|
172
|
-
# => "Enter this 6-digit code to proceed: 4, 8, 2, 9, 1, 3"
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Off the gem
|
|
176
|
-
|
|
177
|
-
A page not on the gem links the two files from the site, and nothing else:
|
|
178
|
-
|
|
179
|
-
```html
|
|
180
|
-
<link rel='stylesheet' href='https://design.easyfaa.com/css/easyfaa.css'>
|
|
181
|
-
<script type='module' src='https://design.easyfaa.com/js/easyfaa.js'></script>
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Those follow the latest release. Every release is also kept whole under its own version,
|
|
185
|
-
forever and cached for a year, for a page that would rather move on its own terms:
|
|
186
|
-
|
|
187
|
-
```html
|
|
188
|
-
<link rel='stylesheet' href='https://design.easyfaa.com/v0.20.0/css/easyfaa.css'>
|
|
189
|
-
<script type='module' src='https://design.easyfaa.com/v0.20.0/js/easyfaa.js'></script>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
bh's layer goes under the house's, from the same place, since the house is drawn on top of it
|
|
193
|
-
and neither stands alone:
|
|
194
|
-
|
|
195
|
-
```html
|
|
196
|
-
<link rel='stylesheet' href='https://design.easyfaa.com/v0.20.0/bh/css/bh.css'>
|
|
197
|
-
<link rel='stylesheet' href='https://design.easyfaa.com/v0.20.0/css/easyfaa.css'>
|
|
49
|
+
EasyFAA.site = ->(request) { ['easyAnnual', 'https://easyannual.com/'] }
|
|
198
50
|
```
|
|
199
51
|
|
|
200
|
-
The
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
A host with a Content Security Policy allows whichever origin it names for `style-src`,
|
|
204
|
-
`script-src` and `font-src`.
|
|
52
|
+
The dummy app's `/guide` is the style guide: every token and component, drawn, with its markup.
|
|
53
|
+
## The site
|
|
205
54
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
`
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
wall. IBM Plex Sans, the lockup's face, is served from `/font` too; nothing on a page reaches
|
|
212
|
-
another origin. `js/easyfaa.js` is the house's script alone: `wall`, the one controller
|
|
213
|
-
that is its own, registered with the Stimulus application bh's script starts. bh's two files
|
|
214
|
-
are copied beside these at `bh/`, and a page links bh's first. Everything is drawn for light and dark alike: every color is a Bootstrap
|
|
215
|
-
token or a `light-dark()` pair, and `data-bs-theme` on `<html>` forces either. A phone is
|
|
216
|
-
formatted in the browser: the server hands over ten digits and
|
|
217
|
-
`data-controller='phone'`, and the controller decides `555-555-5555`.
|
|
55
|
+
`rake pages` renders the dummy app's `/showcase` (the style guide in the site's own frame, then
|
|
56
|
+
the marks), `/404` and `/down.html` into `public/design/`, every asset they link copied to
|
|
57
|
+
`public/design/assets` under its own name. The marks are the house's own, drawn under `art/` and
|
|
58
|
+
rendered by `build-icons.sh` into `public/design/logo` and `public/design/favicon`; no app shows
|
|
59
|
+
one in public.
|
|
218
60
|
|
|
219
61
|
## Development
|
|
220
62
|
|
|
221
63
|
```bash
|
|
222
|
-
bin/setup # bundle
|
|
223
|
-
bundle exec rake #
|
|
64
|
+
bin/setup # bundle install
|
|
65
|
+
bundle exec rake # render the site and check it is committed, test at 100% coverage,
|
|
66
|
+
# RuboCop, the file-length ceiling
|
|
224
67
|
./build-icons.sh # after editing a drawing under art/
|
|
225
|
-
|
|
226
|
-
cd test/dummy && bin/rails server
|
|
68
|
+
cd test/dummy && bin/rails server # /site, /guide and /showcase
|
|
227
69
|
```
|
|
228
70
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
The built `public/css`, `public/js`, `public/font` and `public/theme` are committed: the gem
|
|
234
|
-
serves them, and `spec.files` is `git ls-files`. `rake assets` rebuilds and fails until the
|
|
235
|
-
result is committed, so a source edit cannot ship without its build.
|
|
236
|
-
|
|
237
|
-
While the gem is read from a path, an app in development serves every file `no-cache`, so a
|
|
238
|
-
rebuild shows on the next reload; only a production app keeps the versioned copy for a year.
|
|
239
|
-
|
|
240
|
-
Every build also writes `public/v<version>/`, whole, with every URL inside pointing under
|
|
241
|
-
that prefix, until the tag `v<version>` exists; from then on the folder is never written
|
|
242
|
-
again, and a page that pinned it is owed the same bytes for good.
|
|
243
|
-
|
|
244
|
-
A release, in order: `CHANGELOG.md`, the version in `lib/easyfaa/version.rb` and
|
|
245
|
-
`package.json`, `bundle exec rake` so the versioned copy is the release's, then commit, tag
|
|
246
|
-
`v<version>`, `gem build` and `gem push`, and fast-forward `production` to `main` so the
|
|
247
|
-
site carries both the latest and the versioned copy.
|
|
71
|
+
A release, in order: `CHANGELOG.md`, the version in `lib/easyfaa/version.rb`, `bundle exec rake`,
|
|
72
|
+
then commit, tag `v<version>`, `gem build` and `gem push`. The site already carries it: it deploys
|
|
73
|
+
from `main`.
|
|
248
74
|
|
|
249
75
|
## Cloudflare
|
|
250
76
|
|
|
251
|
-
The site is a
|
|
252
|
-
`
|
|
253
|
-
|
|
254
|
-
`
|
|
255
|
-
|
|
256
|
-
|
|
77
|
+
The site is a Worker named `design` that serves `public/` as static assets on the routes
|
|
78
|
+
`easyfaa.com/design` and `easyfaa.com/design/*`, ahead of tower, as `wrangler.jsonc` says; the
|
|
79
|
+
zone's redirect rule sends its old address, `design.easyfaa.com`, there. Workers Builds deploys
|
|
80
|
+
`main` when it changes `design/`, with `npx wrangler deploy` there and no build command: what is
|
|
81
|
+
committed is what is served.
|
|
82
|
+
A path with no file is answered with `design/404.html` and a 404, and every file keeps Cloudflare's
|
|
83
|
+
default of revalidating on every load, so a URL never has to change between versions.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<style>.i{stroke:#15202e}@media (prefers-color-scheme:dark){.i{stroke:#f3f5f8}}</style>
|
|
3
|
+
<g class="i" stroke-width="2.5" stroke-linejoin="round">
|
|
4
|
+
<polygon points="60,8 4,30 25,37" fill="#ffffff"/>
|
|
5
|
+
<polygon points="60,8 25,37 31,57" fill="#cfe0f3"/>
|
|
6
|
+
</g>
|
|
7
|
+
<g stroke="#7a93b3" stroke-width="1.6" stroke-linecap="round">
|
|
8
|
+
<line x1="16" y1="28.5" x2="36" y2="21"/><line x1="21" y1="31.5" x2="44" y2="22.5"/><line x1="27" y1="33" x2="46" y2="25.5"/>
|
|
9
|
+
</g>
|
|
10
|
+
<path d="M10 44 C14 50 18 53 24 55" fill="none" class="i" stroke-width="2" stroke-dasharray="3 3" stroke-linecap="round"/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
2
|
+
<symbol id="i-check" viewBox="0 0 16 16"><circle cx="8" cy="8" r="8" fill="currentColor"/><path d="M4.5 8.2l2.3 2.3 4.7-4.9" fill="none" stroke="#fff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
3
|
+
<symbol id="i-applies" viewBox="0 0 16 16"><circle cx="8" cy="8" r="8" fill="currentColor"/><path d="M8 4.2v4.6" stroke="#fff" stroke-width="2" stroke-linecap="round"/><circle cx="8" cy="11.6" r="1.2" fill="#fff"/></symbol>
|
|
4
|
+
<symbol id="i-maybe" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="2"/><path d="M8 4.2v4.6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><circle cx="8" cy="11.6" r="1.2" fill="currentColor"/></symbol>
|
|
5
|
+
<symbol id="i-none" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="2"/><path d="M4.8 8h6.4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></symbol>
|
|
6
|
+
<symbol id="i-alert" viewBox="0 0 16 16"><path d="M8 1.6L14.8 13.8H1.2Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M8 6v3.6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><circle cx="8" cy="11.6" r="1" fill="currentColor"/></symbol>
|
|
7
|
+
<symbol id="i-superseded" viewBox="0 0 16 16"><path d="M3.5 1.75h5.75l3.25 3.25v9.25h-9z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M9.25 1.75v3.25h3.25" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M1.5 14.5L14.5 1.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol>
|
|
8
|
+
<symbol id="i-engine" viewBox="0 0 24 24"><path fill="currentColor" d="M7 4v2h3v2H7l-2 2v3H3v-3H1v8h2v-3h2v3h3l2 2h8v-4h2v3h3V9h-3v3h-2V8h-6V6h3V4z"/></symbol>
|
|
9
|
+
<symbol id="i-propeller" viewBox="0 0 16 16"><g fill="currentColor"><path d="M8 7.4C5.4 6 5.3 2.1 8 .5c2.7 1.6 2.6 5.5 0 6.9z"/><path d="M8 7.4C5.4 6 5.3 2.1 8 .5c2.7 1.6 2.6 5.5 0 6.9z" transform="rotate(120 8 8)"/><path d="M8 7.4C5.4 6 5.3 2.1 8 .5c2.7 1.6 2.6 5.5 0 6.9z" transform="rotate(240 8 8)"/><circle cx="8" cy="8" r="2.1"/></g></symbol>
|
|
10
|
+
<symbol id="i-appliance" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M13.07 6.45L15.31 6.84L15.31 9.16L13.07 9.55L12.68 10.49L13.99 12.35L12.35 13.99L10.49 12.68L9.55 13.07L9.16 15.31L6.84 15.31L6.45 13.07L5.51 12.68L3.65 13.99L2.01 12.35L3.32 10.49L2.93 9.55L0.69 9.16L0.69 6.84L2.93 6.45L3.32 5.51L2.01 3.65L3.65 2.01L5.51 3.32L6.45 2.93L6.84 0.69L9.16 0.69L9.55 2.93L10.49 3.32L12.35 2.01L13.99 3.65L12.68 5.51Z M8 5.6a2.4 2.4 0 1 0 0 4.8a2.4 2.4 0 1 0 0-4.8Z"/></symbol>
|
|
11
|
+
<symbol id="i-calendar" viewBox="0 0 16 16"><rect x="2" y="3" width="12" height="11" rx="2" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M2 6.5h12M5 1.5v3M11 1.5v3" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol>
|
|
12
|
+
<symbol id="i-newest" viewBox="0 0 16 16"><path d="M4 2.5v10M1.5 10l2.5 2.5L6.5 10M9 4h5.5M9 7.5h4M9 11h2.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
13
|
+
<symbol id="i-oldest" viewBox="0 0 16 16"><path d="M4 13.5v-10M1.5 6L4 3.5 6.5 6M9 4h2.5M9 7.5h4M9 11h5.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
14
|
+
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="10.5" cy="10.5" r="6.5" fill="none" stroke="currentColor" stroke-width="2.4"/><path d="M15.5 15.5l5 5" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></symbol>
|
|
15
|
+
<symbol id="i-bookmark" viewBox="0 0 16 16"><path d="M4 1.5h8a.5.5 0 0 1 .5.5v12.3L8 11.2l-4.5 3.1V2a.5.5 0 0 1 .5-.5z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></symbol>
|
|
16
|
+
<symbol id="i-share" viewBox="0 0 16 16"><circle cx="12" cy="3.5" r="2" fill="none" stroke="currentColor" stroke-width="1.5"/><circle cx="4" cy="8" r="2" fill="none" stroke="currentColor" stroke-width="1.5"/><circle cx="12" cy="12.5" r="2" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M5.8 7l4.4-2.5M5.8 9l4.4 2.5" stroke="currentColor" stroke-width="1.5"/></symbol>
|
|
17
|
+
<symbol id="i-link" viewBox="0 0 16 16"><path d="M6.6 9.4l2.8-2.8M7 4.6l1.2-1.2a2.8 2.8 0 0 1 4 4L11 8.6M9 11.4l-1.2 1.2a2.8 2.8 0 0 1-4-4L5 7.4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></symbol>
|
|
18
|
+
<symbol id="i-out" viewBox="0 0 16 16"><path d="M9 2.5h4.5V7M13.5 2.5L7.5 8.5M11.5 9.5v3a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1h3" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
19
|
+
<symbol id="i-sun" viewBox="0 0 16 16"><circle cx="8" cy="8" r="3.2" fill="currentColor"/><path d="M8 .8v2M8 13.2v2M.8 8h2M13.2 8h2M2.9 2.9l1.4 1.4M11.7 11.7l1.4 1.4M2.9 13.1l1.4-1.4M11.7 4.3l1.4-1.4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></symbol>
|
|
20
|
+
<symbol id="i-moon" viewBox="0 0 16 16"><path d="M13.5 10.2A6 6 0 0 1 5.8 2.5a6 6 0 1 0 7.7 7.7z" fill="currentColor"/></symbol>
|
|
21
|
+
<symbol id="i-monitor" viewBox="0 0 16 16"><rect x="1.2" y="2" width="13.6" height="9" rx="1.4" fill="none" stroke="currentColor" stroke-width="1.4"/><path d="M5.5 14.2h5M8 11v3.2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></symbol>
|
|
22
|
+
<symbol id="i-plane" viewBox="0 0 24 24"><path d="M21 15.5v-2l-8-5V3.2a1.5 1.5 0 0 0-3 0V8.5l-8 5v2l8-2.5v5.2l-2 1.5V21l3.5-1 3.5 1v-1.3l-2-1.5V13z" fill="currentColor"/></symbol>
|
|
23
|
+
<symbol id="i-small-plane" viewBox="0 0 24 24"><path fill="currentColor" d="M8.2 1.1h7.6v1.1H8.2zM12 2.2c1.1 0 1.7 1.1 1.7 2.7v3.2h8.1c.5 0 .9.4.9.9v1.7c0 .5-.4.9-.9.9h-8.1l-.8 7.1h2.9c.5 0 .8.3.8.8v1c0 .5-.3.8-.8.8H8.2c-.5 0-.8-.3-.8-.8v-1c0-.5.3-.8.8-.8h2.9l-.8-7.1H2.2c-.5 0-.9-.4-.9-.9V9c0-.5.4-.9.9-.9h8.1V4.9c0-1.6.6-2.7 1.7-2.7z"/></symbol>
|
|
24
|
+
<symbol id="i-twin-plane" viewBox="0 0 24 24"><path fill="currentColor" d="M12 1.8c.9 0 1.5 1 1.5 2.6v13.4l-.5 2.8h-2l-.5-2.8V4.4c0-1.6.6-2.6 1.5-2.6zM1.3 10.2h21.4c.5 0 .8.3.8.8v1.4c0 .5-.3.8-.8.8H1.3c-.5 0-.8-.3-.8-.8V11c0-.5.3-.8.8-.8zM7 6.2c.7 0 1.2.5 1.2 1.2v7H5.8v-7c0-.7.5-1.2 1.2-1.2zM17 6.2c.7 0 1.2.5 1.2 1.2v7h-2.4v-7c0-.7.5-1.2 1.2-1.2zM4.8 5h4.4v1H4.8zM14.8 5h4.4v1h-4.4zM8.2 19.2h7.6c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5H8.2c-.3 0-.5-.2-.5-.5v-1c0-.3.2-.5.5-.5z"/></symbol>
|
|
25
|
+
<symbol id="s-x" viewBox="0 0 24 24"><path fill="currentColor" d="M17.8 3h3.1l-6.8 7.8L22 21h-6.2l-4.9-6.4L5.3 21H2.2l7.3-8.3L1.9 3h6.3l4.4 5.9zm-1.1 16.2h1.7L7.4 4.7H5.6z"/></symbol>
|
|
26
|
+
<symbol id="s-fb" viewBox="0 0 24 24"><path fill="currentColor" d="M13.6 22v-8.2h2.8l.4-3.3h-3.2V8.4c0-.9.3-1.6 1.6-1.6h1.7V3.9c-.3 0-1.3-.1-2.5-.1-2.5 0-4.2 1.5-4.2 4.3v2.4H7.4v3.3h2.8V22z"/></symbol>
|
|
27
|
+
<symbol id="s-li" viewBox="0 0 24 24"><path fill="currentColor" d="M3.6 8.6h3.7V20.5H3.6zM5.45 2.8a2.15 2.15 0 110 4.3 2.15 2.15 0 010-4.3zM9.6 8.6h3.5v1.6h.1c.5-.9 1.7-1.9 3.5-1.9 3.8 0 4.5 2.5 4.5 5.7v6.5h-3.7v-5.8c0-1.4 0-3.2-1.9-3.2s-2.3 1.5-2.3 3.1v5.9H9.6z"/></symbol>
|
|
28
|
+
<symbol id="s-rd" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M19.7 5.3a1.6 1.6 0 11-1.5 2l-3.3-.7-1 4.4c2.2.1 4.2.8 5.6 1.9a2.2 2.2 0 113 3 4.3 4.3 0 010 .6c0 3.4-4 6.1-8.5 6.1S5.5 19.9 5.5 16.5v-.6a2.2 2.2 0 113-3c1.4-1 3.3-1.7 5.4-1.8l1.2-5.3 4 .9a1.6 1.6 0 01.6-1.4zM9.3 14.2a1.4 1.4 0 100 2.8 1.4 1.4 0 000-2.8zm5.4 0a1.4 1.4 0 100 2.8 1.4 1.4 0 000-2.8zm-5.2 4.3a.5.5 0 00-.3.9c.9.7 2 1 2.8 1s1.9-.3 2.8-1a.5.5 0 10-.6-.8c-.6.5-1.4.8-2.2.8s-1.6-.3-2.2-.8a.5.5 0 00-.3-.1z"/></symbol>
|
|
29
|
+
<symbol id="s-wa" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2a10 10 0 00-8.6 15.1L2 22l5-1.3A10 10 0 1012 2zm0 1.8a8.2 8.2 0 11-4.2 15.3l-.3-.2-3 .8.8-2.9-.2-.3A8.2 8.2 0 0112 3.8zM8.6 7.2c-.2 0-.5 0-.8.3-.3.3-1 1-1 2.5s1.1 2.9 1.2 3.1c.2.2 2.1 3.3 5.2 4.5 2.6 1 3.1.8 3.7.8.6-.1 1.8-.7 2-1.5.3-.7.3-1.3.2-1.5l-.6-.4-2-1c-.3-.1-.5-.1-.7.1l-.9 1.2c-.2.2-.3.2-.6.1-.3-.2-1.3-.5-2.4-1.5-.9-.8-1.5-1.8-1.7-2.1-.2-.3 0-.5.1-.6l.5-.6.3-.5v-.5l-.9-2.3c-.2-.6-.5-.5-.7-.5z"/></symbol>
|
|
30
|
+
<symbol id="s-em" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" d="M3 5.5h18v13H3zM3.5 6l8.5 7 8.5-7"/></symbol>
|
|
31
|
+
<!-- Aircraft avatars (a-): side views, nose right, drones from above; holes take the avatar background. -->
|
|
32
|
+
<symbol id="a-high-wing" viewBox="1 1 46 46" fill="currentColor"><path d="M43.5 24.2C43.5 22 41.5 20.8 39 20.6L35 18.6H24.5L9 21.4H5V24.2L24 27.2H38.5C41.5 27.2 43.5 26.4 43.5 24.2Z"/><path d="M34.2 19.3L37.6 21H33.8V19.3ZM25.2 19.3H32.8V21H25.2Z" style="fill:var(--avatar-bg,#fff)"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="21.5" y="16.6" width="13.0" height="2.0" rx="0.8"/><line x1="31" y1="18.4" x2="27" y2="26.4" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><line x1="38.5" y1="27" x2="38.5" y2="32.2" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="38.5" cy="33.6" r="2"/><line x1="28" y1="27" x2="29" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="29" cy="33.6" r="2.3"/></symbol>
|
|
33
|
+
<symbol id="a-high-wing-tailwheel" viewBox="1 1 46 46" fill="currentColor"><g transform="rotate(-8 24 28)"><path d="M43.5 24.2C43.5 22 41.5 20.8 39 20.6L35 18.6H24.5L9 21.4H5V24.2L24 27.2H38.5C41.5 27.2 43.5 26.4 43.5 24.2Z"/><path d="M34.2 19.3L37.6 21H33.8V19.3ZM25.2 19.3H32.8V21H25.2Z" style="fill:var(--avatar-bg,#fff)"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="21.5" y="16.6" width="13.0" height="2.0" rx="0.8"/><line x1="31" y1="18.4" x2="27" y2="26.4" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><line x1="32.5" y1="27" x2="34" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="34" cy="33.6" r="2.3"/><line x1="7.5" y1="23.8" x2="7" y2="25.6" stroke="currentColor" stroke-width="0.9" stroke-linecap="round"/><circle cx="7" cy="26.2" r="1.1"/></g></symbol>
|
|
34
|
+
<symbol id="a-low-wing" viewBox="1 1 46 46" fill="currentColor"><path d="M43.5 24.2C43.5 22 41.5 20.8 39 20.6L35 18.6H24.5L9 21.4H5V24.2L24 27.2H38.5C41.5 27.2 43.5 26.4 43.5 24.2Z"/><path d="M34.2 19.3L37.6 21H33.8V19.3ZM25.2 19.3H32.8V21H25.2Z" style="fill:var(--avatar-bg,#fff)"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="20" y="26.4" width="14.5" height="2.0" rx="0.8"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><line x1="38.5" y1="27" x2="38.5" y2="32.2" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="38.5" cy="33.6" r="2"/><line x1="28" y1="27" x2="29" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="29" cy="33.6" r="2.3"/></symbol>
|
|
35
|
+
<symbol id="a-low-wing-retractable" viewBox="1 1 46 46" fill="currentColor"><path d="M43.5 24.2C43.5 22 41.5 20.8 39 20.6L35 18.6H24.5L9 21.4H5V24.2L24 27.2H38.5C41.5 27.2 43.5 26.4 43.5 24.2Z"/><path d="M34.2 19.3L37.6 21H33.8V19.3ZM25.2 19.3H32.8V21H25.2Z" style="fill:var(--avatar-bg,#fff)"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="20" y="26.4" width="14.5" height="2.0" rx="0.8"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/></symbol>
|
|
36
|
+
<symbol id="a-low-wing-tailwheel" viewBox="1 1 46 46" fill="currentColor"><g transform="rotate(-8 24 28)"><path d="M43.5 24.2C43.5 22 41.5 21 39 21H31L28 18.4C26.5 17.6 23.5 17.6 22 18.6L19.5 21H9L5 21.4V24L24 27H38.5C41.5 27 43.5 26.4 43.5 24.2Z"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="20" y="26.4" width="14.5" height="2.0" rx="0.8"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><line x1="32.5" y1="27" x2="34" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="34" cy="33.6" r="2.3"/><line x1="7.5" y1="23.8" x2="7" y2="25.6" stroke="currentColor" stroke-width="0.9" stroke-linecap="round"/><circle cx="7" cy="26.2" r="1.1"/></g></symbol>
|
|
37
|
+
<symbol id="a-biplane" viewBox="1 1 46 46" fill="currentColor"><g transform="rotate(-8 24 28)"><path d="M43.5 24.2C43.5 22 41.5 20.8 39 20.6L31 20.4C30 18.4 26.5 18.4 25.5 20.4L9 21.4H5V24.2L24 27.2H38.5C41.5 27.2 43.5 26.4 43.5 24.2Z"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="20" y="13.6" width="14" height="2.0" rx="0.8"/><line x1="25" y1="15.4" x2="26.5" y2="19.5" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><line x1="29.5" y1="15.4" x2="30" y2="19" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><rect x="21" y="26.4" width="13" height="1.8" rx="0.8"/><line x1="22" y1="15.4" x2="22" y2="26.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><line x1="32.5" y1="15.4" x2="32.5" y2="26.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><line x1="32.5" y1="27" x2="34" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="34" cy="33.6" r="2.3"/><line x1="7.5" y1="23.8" x2="7" y2="25.6" stroke="currentColor" stroke-width="0.9" stroke-linecap="round"/><circle cx="7" cy="26.2" r="1.1"/></g></symbol>
|
|
38
|
+
<symbol id="a-ag-plane" viewBox="1 1 46 46" fill="currentColor"><g transform="rotate(-8 24 28)"><path d="M43.5 24.4C43.5 22.2 42 21.4 40 21.2L37 18.8H31.5L28.5 15.6C27.5 14.8 24.5 14.8 23.5 15.8L21.5 19.6L9 21.4H5V24.2L24 27.6H38.5C41.5 27.6 43.5 26.6 43.5 24.4Z"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="20" y="26.4" width="14.5" height="2.0" rx="0.8"/><rect x="19.5" y="29.4" width="15.5" height="0.8" rx="0.4"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><line x1="32.5" y1="27" x2="34" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="34" cy="33.6" r="2.3"/><line x1="7.5" y1="23.8" x2="7" y2="25.6" stroke="currentColor" stroke-width="0.9" stroke-linecap="round"/><circle cx="7" cy="26.2" r="1.1"/></g></symbol>
|
|
39
|
+
<symbol id="a-floatplane" viewBox="1 1 46 46" fill="currentColor"><path d="M43.5 24.2C43.5 22 41.5 20.8 39 20.6L35 18.6H24.5L9 21.4H5V24.2L24 27.2H38.5C41.5 27.2 43.5 26.4 43.5 24.2Z"/><path d="M34.2 19.3L37.6 21H33.8V19.3ZM25.2 19.3H32.8V21H25.2Z" style="fill:var(--avatar-bg,#fff)"/><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="21.5" y="16.6" width="13.0" height="2.0" rx="0.8"/><line x1="31" y1="18.4" x2="27" y2="26.4" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><rect x="44.4" y="15.5" width="1.2" height="17.4" rx="0.6"/><circle cx="44.2" cy="24.2" r="1.4"/><path d="M13 32.4H38C41 32.4 42.6 31.4 43.4 29.8L44 30.6C43.4 33 41.4 34.8 38 34.8H15L13 33.8Z"/><line x1="22" y1="27" x2="21" y2="32.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><line x1="33.5" y1="27" x2="34.5" y2="32.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/></symbol>
|
|
40
|
+
<symbol id="a-single-turboprop" viewBox="1 1 46 46" fill="currentColor"><path d="M45 24.4C45 22.6 43.6 21.6 41.6 21.4L33 20.8L30 18.4H21L7 21.2H3.6V24L22 27.4H41.6C43.8 27.4 45 26.4 45 24.4Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M29.2 19.1L31 20.6H28.4V19.1Z"/><rect x="21.6" y="19.1" width="5.8" height="1.5" rx="0.3"/></g><rect x="35.5" y="19.6" width="2.5" height="2.0" rx="0.5"/><path d="M3.6 21.4L2.6 11.6H6.6L13 21Z"/><rect x="1.2" y="21" width="10.4" height="1.4" rx="0.7"/><rect x="18" y="16.4" width="14" height="2.0" rx="0.8"/><line x1="28" y1="18.2" x2="24.6" y2="26.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><path d="M17 27.4H34C35.4 27.4 36 28 36 28.8C36 29.6 35.4 30.2 34 30.2H19Z"/><rect x="45.6" y="15.4" width="1.2" height="18.0" rx="0.6"/><line x1="40" y1="27.4" x2="40" y2="31.6" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="40" cy="33.2" r="1.8"/><line x1="26" y1="30" x2="27" y2="32" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="27" cy="33.4" r="2.1"/></symbol>
|
|
41
|
+
<symbol id="a-twin" viewBox="1 1 46 46" fill="currentColor"><path d="M45 24.6C45 22.8 43 21.6 40 21.2L35.4 18.6H24.5L9 21.4H5V24.2L24 27.2H40C43 27.2 45 26.4 45 24.6Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M34.6 19.3L37.8 21H34.2V19.3Z"/><rect x="25.2" y="19.3" width="7.8" height="1.7" rx="0.3"/></g><path d="M5 21.6L4 12.6H7.6L14 21Z"/><rect x="2.5" y="21.2" width="10.0" height="1.4" rx="0.7"/><rect x="19" y="26.4" width="14" height="2.0" rx="0.8"/><path d="M20 24.2H34.6C37.2 24.2 38.4 25.8 38.4 27.6C38.4 29.4 37.2 31 34.6 31H20Z"/><ellipse cx="38.4" cy="27.6" rx="0.8" ry="7"/></symbol>
|
|
42
|
+
<symbol id="a-business-jet" viewBox="1 1 46 46" fill="currentColor"><path d="M46.6 24.8C45 22.6 42.6 21.2 39 20.8L36 19.2H20L7 21.2H4V24.4L20 27H38C42.6 27 45.6 26.2 46.6 24.8Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M37.8 20.2L39.8 21.2H37.2Z"/><circle cx="32" cy="21.2" r="0.8"/><circle cx="29" cy="21.2" r="0.8"/><circle cx="26" cy="21.2" r="0.8"/><circle cx="23" cy="21.4" r="0.8"/></g><path d="M4.4 21.4L5.4 10.6H8.4L13.6 21Z"/><rect x="2.6" y="9.8" width="8.8" height="1.4" rx="0.7"/><path d="M9 17.4H17C19 17.4 20 18.4 20 19.4C20 20.4 19 21.4 17 21.4H9Z"/><path d="M18 26.4L30 26.2L34 27.8L22 28.4Z"/></symbol>
|
|
43
|
+
<symbol id="a-airliner" viewBox="1 1 46 46" fill="currentColor"><path d="M47 24.6C46 22.4 44 21.2 41 21.2H8L3.4 18.6H1V24.4L6 27.4H41C44 27.4 46.4 26.2 47 24.6Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M42 22.2L44.4 23.2H41.6Z"/><circle cx="10" cy="23" r="0.55"/><circle cx="13" cy="23" r="0.55"/><circle cx="16" cy="23" r="0.55"/><circle cx="19" cy="23" r="0.55"/><circle cx="22" cy="23" r="0.55"/><circle cx="25" cy="23" r="0.55"/><circle cx="28" cy="23" r="0.55"/><circle cx="31" cy="23" r="0.55"/><circle cx="34" cy="23" r="0.55"/><circle cx="37" cy="23" r="0.55"/></g><path d="M1.4 19L1 8.8H4.4L11.2 21.2Z"/><rect x="0" y="20.4" width="8.6" height="1.4" rx="0.7"/><path d="M18 26.6L30 26.4L34 28.4L22 29Z"/><path d="M24 28.2H31C32.6 28.2 33.4 29 33.4 30C33.4 31 32.6 31.8 31 31.8H24Z"/></symbol>
|
|
44
|
+
<symbol id="a-helicopter" viewBox="1 1 46 46" fill="currentColor"><path d="M39 26.4C39 21.4 36 18.4 31 18.4H27L24.6 16.4H18.6L16 19L4.6 21.6V23.4L19 25.4C19.6 28.4 21.6 30 24.6 30H35.4C37.6 30 39 28.6 39 26.4Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M31.4 19.8C35 19.8 37.4 22.4 37.6 25.8H32Z"/><rect x="25.4" y="20.6" width="4.0" height="4.4" rx="0.6"/></g><rect x="22.6" y="12.6" width="1.8" height="4" rx="0.5"/><rect x="4" y="11.4" width="40" height="1.4" rx="0.7"/><path d="M4.8 22L3 13.6H5.6L8.8 21.6Z"/><rect x="9" y="21.4" width="6" height="1.2" rx="0.5"/><circle cx="4.4" cy="17.4" r="2.6" fill="none" stroke="currentColor" stroke-width="0.8"/><line x1="25" y1="30" x2="24.4" y2="33" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><line x1="35" y1="30" x2="35.6" y2="33" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><rect x="20" y="32.8" width="20" height="1.2" rx="0.6"/></symbol>
|
|
45
|
+
<symbol id="a-heavy-helicopter" viewBox="1 1 46 46" fill="currentColor"><path d="M43 26C43 22 40.4 19.6 36.4 19.4L33 17.4H17.4L15 19.6L3.6 20.4V22.8L15 24.4V28.4C15 29.4 15.8 30 17 30H39.4C41.6 30 43 28.6 43 26Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M37 20.8C39.6 21 41.2 22.6 41.6 25H37.4Z"/><rect x="30" y="21.4" width="4.4" height="3.0" rx="0.5"/><rect x="23.4" y="21.4" width="4.4" height="3.0" rx="0.5"/><rect x="17.6" y="21.4" width="4.0" height="3.0" rx="0.5"/></g><path d="M14.6 17.4H34.6L33 15.6H17Z"/><rect x="24.4" y="11.4" width="2.0" height="4.4" rx="0.5"/><rect x="2.4" y="10.4" width="44.6" height="1.4" rx="0.7"/><path d="M4 21L2.4 12.6H5.4L9 20.6Z"/><rect x="5" y="19.4" width="7" height="1.2" rx="0.5"/><line x1="37" y1="30" x2="37" y2="32.6" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="37" cy="34" r="1.8"/><line x1="21" y1="30" x2="21" y2="32.6" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><circle cx="21" cy="34" r="1.9"/><circle cx="4.6" cy="23.6" r="1"/></symbol>
|
|
46
|
+
<symbol id="a-multirotor-drone" viewBox="1 1 46 46" fill="currentColor"><g transform="translate(24 24) scale(.84) translate(-24 -24)"><line x1="11" y1="11" x2="37" y2="37" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/><line x1="37" y1="11" x2="11" y2="37" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/><circle cx="11" cy="11" r="7.4" fill="none" stroke="currentColor" stroke-width="1.3"/><circle cx="37" cy="11" r="7.4" fill="none" stroke="currentColor" stroke-width="1.3"/><circle cx="11" cy="37" r="7.4" fill="none" stroke="currentColor" stroke-width="1.3"/><circle cx="37" cy="37" r="7.4" fill="none" stroke="currentColor" stroke-width="1.3"/><circle cx="11" cy="11" r="1.6"/><circle cx="37" cy="11" r="1.6"/><circle cx="11" cy="37" r="1.6"/><circle cx="37" cy="37" r="1.6"/><rect x="18.4" y="17" width="11.2" height="14" rx="3"/><g style="fill:var(--avatar-bg,#fff)"><rect x="20.6" y="19.6" width="6.8" height="4.4" rx="1.4"/></g><rect x="9" y="40.6" width="30" height="1.4" rx="0.7"/></g></symbol>
|
|
47
|
+
<symbol id="a-fixed-wing-drone" viewBox="1 1 46 46" fill="currentColor"><g transform="translate(24 24) scale(.84) translate(-24 -24)"><path d="M24 6C25.6 6 26.6 7.6 26.6 10V36H21.4V10C21.4 7.6 22.4 6 24 6Z"/><rect x="2" y="17" width="44" height="4.4" rx="1.4"/><rect x="11" y="9" width="2" height="26" rx="1"/><rect x="35" y="9" width="2" height="26" rx="1"/><circle cx="12" cy="9" r="4.4" fill="none" stroke="currentColor" stroke-width="1.1"/><circle cx="12" cy="35" r="4.4" fill="none" stroke="currentColor" stroke-width="1.1"/><circle cx="36" cy="9" r="4.4" fill="none" stroke="currentColor" stroke-width="1.1"/><circle cx="36" cy="35" r="4.4" fill="none" stroke="currentColor" stroke-width="1.1"/><rect x="17" y="36" width="14" height="2" rx="0.8"/><ellipse cx="24" cy="40" rx="5.4" ry="1"/></g></symbol>
|
|
48
|
+
<symbol id="a-glider" viewBox="1 1 46 46" fill="currentColor"><path d="M46 24.4C46 23 44 22.2 41 21.8L36 20.2C34.4 19.4 31 19.4 29.6 20.4L28 21.4L8 22.6H4.4V24.6L8 25.2L28 27H40C44 27 46 25.8 46 24.4Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M35.8 21.4C34.6 20.4 31.6 20.4 30.4 21.4Z"/></g><path d="M4.6 22.8L5.6 12H8.4L12 22.6Z"/><rect x="3.4" y="11.2" width="8.2" height="1.2" rx="0.6"/><rect x="22" y="22.2" width="11" height="1.4" rx="0.6"/><circle cx="30.4" cy="28" r="1.6"/></symbol>
|
|
49
|
+
<symbol id="a-balloon" viewBox="1 1 46 46" fill="currentColor"><path d="M24 2.6C32.6 2.6 38.6 8.4 38.6 16.4C38.6 23.6 31.6 29 27.2 32.6H20.8C16.4 29 9.4 23.6 9.4 16.4C9.4 8.4 15.4 2.6 24 2.6Z"/><g style="fill:var(--avatar-bg,#fff)"><path d="M19.6 4.4C17.4 9 17 20 21.4 31.6H22.6C19.4 20 19.8 9 21.6 3.8Z"/><path d="M28.4 4.4C30.6 9 31 20 26.6 31.6H25.4C28.6 20 28.2 9 26.4 3.8Z"/></g><line x1="21" y1="32.6" x2="21.6" y2="38.6" stroke="currentColor" stroke-width="0.7" stroke-linecap="round"/><line x1="27" y1="32.6" x2="26.4" y2="38.6" stroke="currentColor" stroke-width="0.7" stroke-linecap="round"/><rect x="20.4" y="38.2" width="7.2" height="6.0" rx="0.8"/></symbol>
|
|
50
|
+
<symbol id="a-trike" viewBox="1 1 46 46" fill="currentColor"><path d="M2 17.6L42 8.4C43.6 8.2 44.6 9 44.2 10.2L43.6 11.4L4 19.8Z"/><line x1="26" y1="13.6" x2="24" y2="25" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><line x1="26" y1="13.6" x2="34.6" y2="22.6" stroke="currentColor" stroke-width="0.9" stroke-linecap="round"/><line x1="26" y1="13.6" x2="30.6" y2="23.4" stroke="currentColor" stroke-width="0.9" stroke-linecap="round"/><line x1="30.6" y1="23.4" x2="34.6" y2="22.6" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><path d="M38 27.4C38 25.4 36 24 33 24H24L20 25V29.4H34C36.6 29.4 38 28.8 38 27.4Z"/><ellipse cx="18.4" cy="27.2" rx="1" ry="6.2"/><line x1="34" y1="29.4" x2="35" y2="32.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><circle cx="35" cy="34.2" r="1.8"/><line x1="23" y1="29.4" x2="22" y2="32.6" stroke="currentColor" stroke-width="1" stroke-linecap="round"/><circle cx="22" cy="34.2" r="1.9"/></symbol>
|
|
51
|
+
<symbol id="a-powered-parachute" viewBox="1 1 46 46" fill="currentColor"><path d="M4 13C10 4 38 4 44 13L41.6 14.6C35.6 8 12.4 8 6.4 14.6Z"/><line x1="7" y1="14" x2="22" y2="30" stroke="currentColor" stroke-width="0.6" stroke-linecap="round"/><line x1="17" y1="8.4" x2="23" y2="30" stroke="currentColor" stroke-width="0.6" stroke-linecap="round"/><line x1="31" y1="8.4" x2="25" y2="30" stroke="currentColor" stroke-width="0.6" stroke-linecap="round"/><line x1="41" y1="14" x2="26" y2="30" stroke="currentColor" stroke-width="0.6" stroke-linecap="round"/><path d="M34 32C34 30.2 32 29 29 29H20L16 30V34H30C32.6 34 34 33.4 34 32Z"/><circle cx="14.4" cy="31" r="5.2" fill="none" stroke="currentColor" stroke-width="1"/><circle cx="29" cy="36.4" r="1.7"/><circle cx="20" cy="36.4" r="1.8"/></symbol>
|
|
52
|
+
</svg>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Things added together (add-rows): its "+ Add …" (add-rows-add) draws a row from its template at the list's end, the
|
|
2
|
+
// cursor in its first field, and a row's Remove (add-row-drop) takes it off, telling the form it changed. Its button
|
|
3
|
+
// (add-rows-save), where it has one, shows while a row is there and counts them: data-one with one, data-many with
|
|
4
|
+
// %{count} for more. Without the script, the rows the page drew are all there are. ES5 alone.
|
|
5
|
+
(function () {
|
|
6
|
+
// A page with two boxes may link the script twice: its clicks are heard once.
|
|
7
|
+
if (window.easyAddRows || !document.querySelectorAll || !document.addEventListener) { return; }
|
|
8
|
+
window.easyAddRows = true;
|
|
9
|
+
|
|
10
|
+
function each(list, callback) { for (var i = 0; i < list.length; i++) { callback(list[i]); } }
|
|
11
|
+
|
|
12
|
+
function count(box) {
|
|
13
|
+
var save = box.querySelector('.add-rows-save');
|
|
14
|
+
if (!save) { return; }
|
|
15
|
+
var n = box.querySelectorAll('.add-rows-list .add-row').length;
|
|
16
|
+
save.hidden = n === 0;
|
|
17
|
+
save.textContent = n === 1 ? save.getAttribute('data-one') : save.getAttribute('data-many').replace('%{count}', n);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
document.addEventListener('click', function (event) {
|
|
21
|
+
var button = event.target.closest && event.target.closest('.add-rows-add, .add-row-drop');
|
|
22
|
+
var box = button && button.closest('.add-rows');
|
|
23
|
+
if (!box) { return; }
|
|
24
|
+
if (button.classList.contains('add-row-drop')) {
|
|
25
|
+
var form = box.closest('form');
|
|
26
|
+
var row = button.closest('.add-row');
|
|
27
|
+
row.parentNode.removeChild(row);
|
|
28
|
+
if (form) { form.dispatchEvent(new Event('change', { bubbles: true })); }
|
|
29
|
+
} else {
|
|
30
|
+
var added = box.querySelector('template').content.firstElementChild.cloneNode(true);
|
|
31
|
+
box.querySelector('.add-rows-list').appendChild(added);
|
|
32
|
+
var first = added.querySelector('input, select, textarea');
|
|
33
|
+
if (first) { first.focus(); }
|
|
34
|
+
}
|
|
35
|
+
count(box);
|
|
36
|
+
});
|
|
37
|
+
each(document.querySelectorAll('.add-rows'), count);
|
|
38
|
+
})();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// The plane of render "easy/flyer": across the sky in 14 seconds on a gentle wave, nose along it, its trail behind it.
|
|
2
|
+
// On a holiday, by the visitor's own calendar, it flies in the day's colors with a line of smoke per color: every day
|
|
3
|
+
// is in this file, so whatever cached it, it's right at midnight. No plane where motion is off. ES5, for old phones.
|
|
4
|
+
(function () {
|
|
5
|
+
if (!document.querySelectorAll || !document.createElementNS || !window.requestAnimationFrame) { return; }
|
|
6
|
+
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { return; }
|
|
7
|
+
|
|
8
|
+
var SKY = { trail: ['#5aa9e6'], opacity: 0.45 }, HANUKKAH = { plane: '#2f6fd0', trail: ['#2f6fd0', '#cfd8e3'] };
|
|
9
|
+
var GOLD = { plane: '#d4a514', trail: ['#d4a514', '#a8b0b8'] };
|
|
10
|
+
var USA = { plane: '#2f5fc4', trail: ['#d63a3a', '#ffffff', '#2f5fc4'] };
|
|
11
|
+
// Hanukkah's first day (25 Kislev) as a day of December, 2025 to 2045 (Nov 28 is -2); its days yield to the others.
|
|
12
|
+
var KISLEV_25 = [15, 5, 25, 13, 2, 21, 10, -2, 17, 7, 26, 14, 3, 22, 12, 0, 18, 8, 27, 15, 5];
|
|
13
|
+
|
|
14
|
+
// The holidays of a year, by "month-day": the plane's color, then its trail's, top to bottom. National Aviation Day
|
|
15
|
+
// is in pilot's-wings gold and blue, Wright Brothers Day in the Wright Flyer's muslin and spruce.
|
|
16
|
+
function holidays(year) {
|
|
17
|
+
var easter = easterOf(year), days = {};
|
|
18
|
+
for (var k = 0; k < 16; k++) { // last year's eight days of Hanukkah, reaching into January, then this year's
|
|
19
|
+
var y = year - 1 + (k >> 3), day = new Date(y, 11, KISLEV_25[y - 2025] + k % 8);
|
|
20
|
+
if (day.getFullYear() === year) { days[(day.getMonth() + 1) + '-' + day.getDate()] = HANUKKAH; }
|
|
21
|
+
}
|
|
22
|
+
days['1-1'] = days['12-31'] = GOLD;
|
|
23
|
+
days['2-14'] = { plane: '#e0457b', trail: ['#d62f4b', '#f07aa0'] };
|
|
24
|
+
days['3-17'] = { plane: '#1f9d55', trail: ['#1f9d55', '#7cc576'] };
|
|
25
|
+
days[easter] = { plane: '#a77bd6', trail: ['#f2c94c', '#a77bd6', '#6fcf97'] };
|
|
26
|
+
days['5-' + weekday(year, 5, 1, -1)] = days['7-4'] = days['11-11'] = USA;
|
|
27
|
+
days['8-19'] = { plane: '#1a70d0', trail: ['#e0b43a', '#5aa9e6'] };
|
|
28
|
+
days['10-31'] = { plane: '#f28c28', trail: ['#f28c28', '#7b3fa0'] };
|
|
29
|
+
days['11-' + weekday(year, 11, 4, 4)] = { plane: '#c0662b', trail: ['#e0a526', '#c0662b', '#8a4b2a'] };
|
|
30
|
+
days['12-17'] = { plane: '#8a6a43', trail: ['#d9c9a3', '#8a6a43'] };
|
|
31
|
+
days['12-25'] = { plane: '#d42f2f', trail: ['#1f8a4c', '#d42f2f'] };
|
|
32
|
+
return days;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The day of the month of its nth weekday (0 Sunday), the last for n -1: Memorial Day, Thanksgiving.
|
|
36
|
+
function weekday(year, month, day, n) {
|
|
37
|
+
if (n < 0) { var last = new Date(year, month, 0); return last.getDate() - (last.getDay() - day + 7) % 7; }
|
|
38
|
+
return 1 + (day - new Date(year, month - 1, 1).getDay() + 7) % 7 + (n - 1) * 7;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Easter Sunday's "month-day": the anonymous Gregorian algorithm.
|
|
42
|
+
function easterOf(y) {
|
|
43
|
+
var a = y % 19, b = Math.floor(y / 100), c = y % 100, f = Math.floor((b + 8) / 25);
|
|
44
|
+
var h = (19 * a + b - Math.floor(b / 4) - Math.floor((b - f + 1) / 3) + 15) % 30;
|
|
45
|
+
var l = (32 + 2 * (b % 4) + 2 * Math.floor(c / 4) - h - c % 4) % 7, m = Math.floor((a + 11 * h + 22 * l) / 451);
|
|
46
|
+
return Math.floor((h + l - 7 * m + 114) / 31) + '-' + ((h + l - 7 * m + 114) % 31 + 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The sky's colors: its data-date's ("2026-12-25"), to see a holiday on any day, or today's.
|
|
50
|
+
function colorsOf(sky) {
|
|
51
|
+
var date = /^(\d{4})-(\d\d)-(\d\d)$/.exec(sky.getAttribute('data-date') || '');
|
|
52
|
+
var day = date ? new Date(+date[1], date[2] - 1, +date[3]) : new Date();
|
|
53
|
+
return holidays(day.getFullYear())[(day.getMonth() + 1) + '-' + day.getDate()] || SKY;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function svg(name, attributes, parent) {
|
|
57
|
+
var node = document.createElementNS('http://www.w3.org/2000/svg', name);
|
|
58
|
+
for (var key in attributes) { node.setAttribute(key, attributes[key]); }
|
|
59
|
+
return parent.appendChild(node);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function fly(sky, n) {
|
|
63
|
+
var flyer = sky.querySelector('.js-flyer'), trail = sky.querySelector('.js-trail'), colors = colorsOf(sky);
|
|
64
|
+
var defs = svg('defs', {}, trail), lines = [], fades = [];
|
|
65
|
+
if (colors.plane) { flyer.style.color = colors.plane; }
|
|
66
|
+
// One line per color, 3px apart, each fading in from the sky's left edge to the tail.
|
|
67
|
+
for (var i = 0; i < colors.trail.length; i++) {
|
|
68
|
+
var id = 'flyer-' + n + '-' + i, color = colors.trail[i];
|
|
69
|
+
var fade = svg('linearGradient', { id: id, gradientUnits: 'userSpaceOnUse', x1: 0, y1: 0, x2: 100, y2: 0 }, defs);
|
|
70
|
+
svg('stop', { offset: 0, 'stop-color': color, 'stop-opacity': 0 }, fade);
|
|
71
|
+
svg('stop', { offset: 1, 'stop-color': color, 'stop-opacity': colors.opacity || 0.7 }, fade);
|
|
72
|
+
fades.push(fade);
|
|
73
|
+
lines.push(svg('polyline', { fill: 'none', stroke: 'url(#' + id + ')', 'stroke-width': 2, 'stroke-linecap': 'round' }, trail));
|
|
74
|
+
}
|
|
75
|
+
var span = 14000, size = 34, began = null;
|
|
76
|
+
function wave(x, width) { return 14 + 9 * Math.sin((x / width) * Math.PI * 3); }
|
|
77
|
+
function frame(now) {
|
|
78
|
+
if (began === null) { began = now; }
|
|
79
|
+
var width = sky.offsetWidth, t = ((now - began) % span) / span;
|
|
80
|
+
var x = -size - 40 + (width + size + 80) * t, y = wave(x + size / 2, width);
|
|
81
|
+
var angle = Math.atan(wave(x + size / 2 + 1, width) - y) * 180 / Math.PI;
|
|
82
|
+
var move = 'translate(' + x.toFixed(1) + 'px,' + (y - size / 2).toFixed(1) + 'px) rotate(' + angle.toFixed(1) + 'deg)';
|
|
83
|
+
flyer.style.webkitTransform = move; flyer.style.transform = move;
|
|
84
|
+
// The tail is the plane's left edge, at its middle, turned with it.
|
|
85
|
+
var tailX = x + size / 2 - (size / 2 - 3) * Math.cos(angle * Math.PI / 180);
|
|
86
|
+
for (var i = 0; i < lines.length; i++) {
|
|
87
|
+
var shift = (i - (lines.length - 1) / 2) * 3, points = [];
|
|
88
|
+
for (var px = 0; px < tailX; px += 6) { points.push(px + ',' + (wave(px, width) + shift).toFixed(1)); }
|
|
89
|
+
if (tailX > 0) { points.push(tailX.toFixed(1) + ',' + (wave(tailX, width) + shift).toFixed(1)); }
|
|
90
|
+
lines[i].setAttribute('points', points.join(' '));
|
|
91
|
+
fades[i].setAttribute('x2', Math.max(tailX, 1).toFixed(1));
|
|
92
|
+
}
|
|
93
|
+
if (sky.className.indexOf('is-gone') < 0) { window.requestAnimationFrame(frame); } // freed (easy/konami)
|
|
94
|
+
}
|
|
95
|
+
window.requestAnimationFrame(frame);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var skies = document.querySelectorAll('.js-sky');
|
|
99
|
+
for (var i = 0; i < skies.length; i++) { fly(skies[i], i); }
|
|
100
|
+
})();
|