hanami-assets 1.3.5 → 2.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +92 -314
  4. data/hanami-assets.gemspec +26 -33
  5. data/lib/hanami/assets/asset.rb +83 -0
  6. data/lib/hanami/assets/base_url.rb +64 -0
  7. data/lib/hanami/assets/config.rb +98 -0
  8. data/lib/hanami/assets/errors.rb +46 -0
  9. data/lib/hanami/assets/version.rb +2 -2
  10. data/lib/hanami/assets.rb +61 -143
  11. data/lib/hanami-assets.rb +3 -0
  12. metadata +33 -115
  13. data/lib/hanami/assets/bundler/asset.rb +0 -100
  14. data/lib/hanami/assets/bundler/compressor.rb +0 -63
  15. data/lib/hanami/assets/bundler/manifest_entry.rb +0 -64
  16. data/lib/hanami/assets/bundler.rb +0 -154
  17. data/lib/hanami/assets/cache.rb +0 -102
  18. data/lib/hanami/assets/compiler.rb +0 -287
  19. data/lib/hanami/assets/compilers/less.rb +0 -31
  20. data/lib/hanami/assets/compilers/sass.rb +0 -61
  21. data/lib/hanami/assets/compressors/abstract.rb +0 -119
  22. data/lib/hanami/assets/compressors/builtin_javascript.rb +0 -36
  23. data/lib/hanami/assets/compressors/builtin_stylesheet.rb +0 -57
  24. data/lib/hanami/assets/compressors/closure_javascript.rb +0 -25
  25. data/lib/hanami/assets/compressors/javascript.rb +0 -77
  26. data/lib/hanami/assets/compressors/jsmin.rb +0 -284
  27. data/lib/hanami/assets/compressors/null_compressor.rb +0 -19
  28. data/lib/hanami/assets/compressors/sass_stylesheet.rb +0 -36
  29. data/lib/hanami/assets/compressors/stylesheet.rb +0 -77
  30. data/lib/hanami/assets/compressors/uglifier_javascript.rb +0 -25
  31. data/lib/hanami/assets/compressors/yui_javascript.rb +0 -25
  32. data/lib/hanami/assets/compressors/yui_stylesheet.rb +0 -25
  33. data/lib/hanami/assets/config/global_sources.rb +0 -52
  34. data/lib/hanami/assets/config/manifest.rb +0 -142
  35. data/lib/hanami/assets/config/sources.rb +0 -80
  36. data/lib/hanami/assets/configuration.rb +0 -657
  37. data/lib/hanami/assets/helpers.rb +0 -945
  38. data/lib/hanami/assets/precompiler.rb +0 -97
@@ -1,945 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "uri"
4
- require "hanami/helpers/html_helper"
5
- require "hanami/utils/escape"
6
-
7
- module Hanami
8
- module Assets
9
- # HTML assets helpers
10
- #
11
- # Include this helper in a view
12
- #
13
- # @since 0.1.0
14
- #
15
- # @see http://www.rubydoc.info/gems/hanami-helpers/Hanami/Helpers/HtmlHelper
16
- #
17
- # rubocop:disable Metrics/ModuleLength
18
- module Helpers
19
- # @since 0.1.0
20
- # @api private
21
- NEW_LINE_SEPARATOR = "\n"
22
-
23
- # @since 0.1.0
24
- # @api private
25
- WILDCARD_EXT = ".*"
26
-
27
- # @since 0.1.0
28
- # @api private
29
- JAVASCRIPT_EXT = ".js"
30
-
31
- # @since 0.1.0
32
- # @api private
33
- STYLESHEET_EXT = ".css"
34
-
35
- # @since 0.1.0
36
- # @api private
37
- JAVASCRIPT_MIME_TYPE = "text/javascript"
38
-
39
- # @since 0.1.0
40
- # @api private
41
- STYLESHEET_MIME_TYPE = "text/css"
42
-
43
- # @since 0.1.0
44
- # @api private
45
- FAVICON_MIME_TYPE = "image/x-icon"
46
-
47
- # @since 0.1.0
48
- # @api private
49
- STYLESHEET_REL = "stylesheet"
50
-
51
- # @since 0.1.0
52
- # @api private
53
- FAVICON_REL = "shortcut icon"
54
-
55
- # @since 0.1.0
56
- # @api private
57
- DEFAULT_FAVICON = "favicon.ico"
58
-
59
- # @since 0.3.0
60
- # @api private
61
- CROSSORIGIN_ANONYMOUS = "anonymous"
62
-
63
- # @since 0.3.0
64
- # @api private
65
- ABSOLUTE_URL_MATCHER = URI::DEFAULT_PARSER.make_regexp
66
-
67
- # @since 1.1.0
68
- # @api private
69
- QUERY_STRING_MATCHER = /\?/.freeze
70
-
71
- include Hanami::Helpers::HtmlHelper
72
-
73
- # Inject helpers into the given class
74
- #
75
- # @since 0.1.0
76
- # @api private
77
- def self.included(base)
78
- conf = ::Hanami::Assets::Configuration.for(base)
79
- base.class_eval do
80
- include Utils::ClassAttribute
81
-
82
- class_attribute :assets_configuration
83
- self.assets_configuration = conf
84
- end
85
- end
86
-
87
- # Generate <tt>script</tt> tag for given source(s)
88
- #
89
- # It accepts one or more strings representing the name of the asset, if it
90
- # comes from the application or third party gems. It also accepts strings
91
- # representing absolute URLs in case of public CDN (eg. jQuery CDN).
92
- #
93
- # If the "fingerprint mode" is on, <tt>src</tt> is the fingerprinted
94
- # version of the relative URL.
95
- #
96
- # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
97
- # application CDN.
98
- #
99
- # If the "subresource integrity mode" is on, <tt>integriy</tt> is the
100
- # name of the algorithm, then a hyphen, then the hash value of the file.
101
- # If more than one algorithm is used, they'll be separated by a space.
102
- #
103
- # It makes the script(s) eligible for HTTP/2 Push Promise/Early Hints.
104
- # You can opt-out with inline option: `push: false`.
105
- #
106
- # @param sources [Array<String>] one or more assets by name or absolute URL
107
- # @param push [TrueClass, FalseClass] HTTP/2 Push Promise/Early Hints flag
108
- #
109
- # @return [Hanami::Utils::Escape::SafeString] the markup
110
- #
111
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
112
- # `subresource_integrity` modes are on and the javascript file is missing
113
- # from the manifest
114
- #
115
- # @since 0.1.0
116
- #
117
- # @see Hanami::Assets::Configuration#fingerprint
118
- # @see Hanami::Assets::Configuration#cdn
119
- # @see Hanami::Assets::Helpers#asset_path
120
- #
121
- # @example Single Asset
122
- #
123
- # <%= javascript 'application' %>
124
- #
125
- # # <script src="/assets/application.js" type="text/javascript"></script>
126
- #
127
- # @example Multiple Assets
128
- #
129
- # <%= javascript 'application', 'dashboard' %>
130
- #
131
- # # <script src="/assets/application.js" type="text/javascript"></script>
132
- # # <script src="/assets/dashboard.js" type="text/javascript"></script>
133
- #
134
- # @example Asynchronous Execution
135
- #
136
- # <%= javascript 'application', async: true %>
137
- #
138
- # # <script src="/assets/application.js" type="text/javascript" async="async"></script>
139
- #
140
- # @example Subresource Integrity
141
- #
142
- # <%= javascript 'application' %>
143
- #
144
- # # <script src="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
145
- # # type="text/javascript" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
146
- #
147
- # @example Subresource Integrity for 3rd Party Scripts
148
- #
149
- # <%= javascript 'https://example.com/assets/example.js', integrity: 'sha384-oqVu...Y8wC' %>
150
- #
151
- # # <script src="https://example.com/assets/example.js" type="text/javascript"
152
- # # integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
153
- #
154
- # @example Deferred Execution
155
- #
156
- # <%= javascript 'application', defer: true %>
157
- #
158
- # # <script src="/assets/application.js" type="text/javascript" defer="defer"></script>
159
- #
160
- # @example Absolute URL
161
- #
162
- # <%= javascript 'https://code.jquery.com/jquery-2.1.4.min.js' %>
163
- #
164
- # # <script src="https://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript"></script>
165
- #
166
- # @example Fingerprint Mode
167
- #
168
- # <%= javascript 'application' %>
169
- #
170
- # # <script src="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript"></script>
171
- #
172
- # @example CDN Mode
173
- #
174
- # <%= javascript 'application' %>
175
- #
176
- # # <script src="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
177
- # # type="text/javascript"></script>
178
- #
179
- # @example Disable Push Promise/Early Hints
180
- #
181
- # <%= javascript 'application', push: false %>
182
- # <%= javascript 'http://cdn.example.test/jquery.js', 'dashboard', push: false %>
183
- def javascript(*sources, push: true, **options)
184
- options = options.reject { |k, _| k.to_sym == :src }
185
-
186
- _safe_tags(*sources) do |source|
187
- attributes = {
188
- src: _typed_asset_path(source, JAVASCRIPT_EXT, push: push, as: :script),
189
- type: JAVASCRIPT_MIME_TYPE
190
- }
191
- attributes.merge!(options)
192
-
193
- if _subresource_integrity? || attributes.include?(:integrity)
194
- attributes[:integrity] ||= _subresource_integrity_value(source, JAVASCRIPT_EXT)
195
- attributes[:crossorigin] ||= CROSSORIGIN_ANONYMOUS
196
- end
197
-
198
- html.script(**attributes).to_s
199
- end
200
- end
201
-
202
- # Generate <tt>link</tt> tag for given source(s)
203
- #
204
- # It accepts one or more strings representing the name of the asset, if it
205
- # comes from the application or third party gems. It also accepts strings
206
- # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
207
- #
208
- # If the "fingerprint mode" is on, <tt>href</tt> is the fingerprinted
209
- # version of the relative URL.
210
- #
211
- # If the "CDN mode" is on, the <tt>href</tt> is an absolute URL of the
212
- # application CDN.
213
- #
214
- # If the "subresource integrity mode" is on, <tt>integriy</tt> is the
215
- # name of the algorithm, then a hyphen, then the hashed value of the file.
216
- # If more than one algorithm is used, they'll be separated by a space.
217
- #
218
- # It makes the script(s) eligible for HTTP/2 Push Promise/Early Hints.
219
- # You can opt-out with inline option: `push: false`.
220
- #
221
- # @param sources [Array<String>] one or more assets by name or absolute URL
222
- # @param push [TrueClass, FalseClass] HTTP/2 Push Promise/Early Hints flag
223
- #
224
- # @return [Hanami::Utils::Escape::SafeString] the markup
225
- #
226
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
227
- # `subresource_integrity` modes are on and the stylesheet file is missing
228
- # from the manifest
229
- #
230
- # @since 0.1.0
231
- #
232
- # @see Hanami::Assets::Configuration#fingerprint
233
- # @see Hanami::Assets::Configuration#cdn
234
- # @see Hanami::Assets::Configuration#subresource_integrity
235
- # @see Hanami::Assets::Helpers#asset_path
236
- #
237
- # @example Single Asset
238
- #
239
- # <%= stylesheet 'application' %>
240
- #
241
- # # <link href="/assets/application.css" type="text/css" rel="stylesheet">
242
- #
243
- # @example Multiple Assets
244
- #
245
- # <%= stylesheet 'application', 'dashboard' %>
246
- #
247
- # # <link href="/assets/application.css" type="text/css" rel="stylesheet">
248
- # # <link href="/assets/dashboard.css" type="text/css" rel="stylesheet">
249
- #
250
- # @example Subresource Integrity
251
- #
252
- # <%= stylesheet 'application' %>
253
- #
254
- # # <link href="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css"
255
- # # type="text/css" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
256
- #
257
- # @example Subresource Integrity for 3rd Party Assets
258
- #
259
- # <%= stylesheet 'https://example.com/assets/example.css', integrity: 'sha384-oqVu...Y8wC' %>
260
- #
261
- # # <link href="https://example.com/assets/example.css"
262
- # # type="text/css" rel="stylesheet" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
263
- #
264
- # @example Absolute URL
265
- #
266
- # <%= stylesheet 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' %>
267
- #
268
- # # <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
269
- # # type="text/css" rel="stylesheet">
270
- #
271
- # @example Fingerprint Mode
272
- #
273
- # <%= stylesheet 'application' %>
274
- #
275
- # # <link href="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" rel="stylesheet">
276
- #
277
- # @example CDN Mode
278
- #
279
- # <%= stylesheet 'application' %>
280
- #
281
- # # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css"
282
- # # type="text/css" rel="stylesheet">
283
- #
284
- # @example Disable Push Promise/Early Hints
285
- #
286
- # <%= stylesheet 'application', push: false %>
287
- # <%= stylesheet 'http://cdn.example.test/bootstrap.css', 'dashboard', push: false %>
288
- def stylesheet(*sources, push: true, **options)
289
- options = options.reject { |k, _| k.to_sym == :href }
290
-
291
- _safe_tags(*sources) do |source|
292
- attributes = {
293
- href: _typed_asset_path(source, STYLESHEET_EXT, push: push, as: :style),
294
- type: STYLESHEET_MIME_TYPE,
295
- rel: STYLESHEET_REL
296
- }
297
- attributes.merge!(options)
298
-
299
- if _subresource_integrity? || attributes.include?(:integrity)
300
- attributes[:integrity] ||= _subresource_integrity_value(source, STYLESHEET_EXT)
301
- attributes[:crossorigin] ||= CROSSORIGIN_ANONYMOUS
302
- end
303
-
304
- html.link(**attributes).to_s
305
- end
306
- end
307
-
308
- # Generate <tt>img</tt> tag for given source
309
- #
310
- # It accepts one string representing the name of the asset, if it comes
311
- # from the application or third party gems. It also accepts string
312
- # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
313
- #
314
- # <tt>alt</tt> Attribute is auto generated from <tt>src</tt>.
315
- # You can specify a different value, by passing the <tt>:src</tt> option.
316
- #
317
- # If the "fingerprint mode" is on, <tt>src</tt> is the fingerprinted
318
- # version of the relative URL.
319
- #
320
- # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
321
- # application CDN.
322
- #
323
- # @param source [String] asset name or absolute URL
324
- # @param options [Hash] HTML 5 attributes
325
- # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
326
- #
327
- # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
328
- #
329
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
330
- # `subresource_integrity` modes are on and the image file is missing
331
- # from the manifest
332
- #
333
- # @since 0.1.0
334
- #
335
- # @see Hanami::Assets::Configuration#fingerprint
336
- # @see Hanami::Assets::Configuration#cdn
337
- # @see Hanami::Assets::Configuration#subresource_integrity
338
- # @see Hanami::Assets::Helpers#asset_path
339
- #
340
- # @example Basic Usage
341
- #
342
- # <%= image 'logo.png' %>
343
- #
344
- # # <img src="/assets/logo.png" alt="Logo">
345
- #
346
- # @example Custom alt Attribute
347
- #
348
- # <%= image 'logo.png', alt: 'Application Logo' %>
349
- #
350
- # # <img src="/assets/logo.png" alt="Application Logo">
351
- #
352
- # @example Custom HTML Attributes
353
- #
354
- # <%= image 'logo.png', id: 'logo', class: 'image' %>
355
- #
356
- # # <img src="/assets/logo.png" alt="Logo" id="logo" class="image">
357
- #
358
- # @example Absolute URL
359
- #
360
- # <%= image 'https://example-cdn.com/images/logo.png' %>
361
- #
362
- # # <img src="https://example-cdn.com/images/logo.png" alt="Logo">
363
- #
364
- # @example Fingerprint Mode
365
- #
366
- # <%= image 'logo.png' %>
367
- #
368
- # # <img src="/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
369
- #
370
- # @example CDN Mode
371
- #
372
- # <%= image 'logo.png' %>
373
- #
374
- # # <img src="https://assets.bookshelf.org/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
375
- #
376
- # @example Enable Push Promise/Early Hints
377
- #
378
- # <%= image 'logo.png', push: true %>
379
- def image(source, options = {})
380
- options = options.reject { |k, _| k.to_sym == :src }
381
- attributes = {
382
- src: asset_path(source, push: options.delete(:push) || false, as: :image),
383
- alt: Utils::String.titleize(::File.basename(source, WILDCARD_EXT))
384
- }
385
- attributes.merge!(options)
386
-
387
- html.img(attributes)
388
- end
389
-
390
- # Generate <tt>link</tt> tag application favicon.
391
- #
392
- # If no argument is given, it assumes <tt>favico.ico</tt> from the application.
393
- #
394
- # It accepts one string representing the name of the asset.
395
- #
396
- # If the "fingerprint mode" is on, <tt>href</tt> is the fingerprinted version
397
- # of the relative URL.
398
- #
399
- # If the "CDN mode" is on, the <tt>href</tt> is an absolute URL of the
400
- # application CDN.
401
- #
402
- # @param source [String] asset name
403
- # @param options [Hash] HTML 5 attributes
404
- # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
405
- #
406
- # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
407
- #
408
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
409
- # `subresource_integrity` modes are on and the favicon is file missing
410
- # from the manifest
411
- #
412
- # @since 0.1.0
413
- #
414
- # @see Hanami::Assets::Configuration#fingerprint
415
- # @see Hanami::Assets::Configuration#cdn
416
- # @see Hanami::Assets::Helpers#asset_path
417
- #
418
- # @example Basic Usage
419
- #
420
- # <%= favicon %>
421
- #
422
- # # <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
423
- #
424
- # @example Custom Path
425
- #
426
- # <%= favicon 'fav.ico' %>
427
- #
428
- # # <link href="/assets/fav.ico" rel="shortcut icon" type="image/x-icon">
429
- #
430
- # @example Custom HTML Attributes
431
- #
432
- # <%= favicon "favicon.ico", id: "fav" %>
433
- #
434
- # # <link id: "fav" href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
435
- #
436
- # @example Fingerprint Mode
437
- #
438
- # <%= favicon %>
439
- #
440
- # # <link href="/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico" rel="shortcut icon" type="image/x-icon">
441
- #
442
- # @example CDN Mode
443
- #
444
- # <%= favicon %>
445
- #
446
- # # <link href="https://assets.bookshelf.org/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico"
447
- # rel="shortcut icon" type="image/x-icon">
448
- #
449
- # @example Enable Push Promise/Early Hints
450
- #
451
- # <%= favicon 'favicon.ico', push: true %>
452
- def favicon(source = DEFAULT_FAVICON, options = {})
453
- options = options.reject { |k, _| k.to_sym == :href }
454
-
455
- attributes = {
456
- href: asset_path(source, push: options.delete(:push) || false, as: :image),
457
- rel: FAVICON_REL,
458
- type: FAVICON_MIME_TYPE
459
- }
460
- attributes.merge!(options)
461
-
462
- html.link(attributes)
463
- end
464
-
465
- # Generate <tt>video</tt> tag for given source
466
- #
467
- # It accepts one string representing the name of the asset, if it comes
468
- # from the application or third party gems. It also accepts string
469
- # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
470
- #
471
- # Alternatively, it accepts a block that allows to specify one or more
472
- # sources via the <tt>source</tt> tag.
473
- #
474
- # If the "fingerprint mode" is on, <tt>src</tt> is the fingerprinted
475
- # version of the relative URL.
476
- #
477
- # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
478
- # application CDN.
479
- #
480
- # @param source [String] asset name or absolute URL
481
- # @param options [Hash] HTML 5 attributes
482
- # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
483
- #
484
- # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
485
- #
486
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
487
- # `subresource_integrity` modes are on and the video file is missing
488
- # from the manifest
489
- #
490
- # @raise [ArgumentError] if source isn't specified both as argument or
491
- # tag inside the given block
492
- #
493
- # @since 0.1.0
494
- #
495
- # @see Hanami::Assets::Configuration#fingerprint
496
- # @see Hanami::Assets::Configuration#cdn
497
- # @see Hanami::Assets::Helpers#asset_path
498
- #
499
- # @example Basic Usage
500
- #
501
- # <%= video 'movie.mp4' %>
502
- #
503
- # # <video src="/assets/movie.mp4"></video>
504
- #
505
- # @example Absolute URL
506
- #
507
- # <%= video 'https://example-cdn.com/assets/movie.mp4' %>
508
- #
509
- # # <video src="https://example-cdn.com/assets/movie.mp4"></video>
510
- #
511
- # @example Custom HTML Attributes
512
- #
513
- # <%= video('movie.mp4', autoplay: true, controls: true) %>
514
- #
515
- # # <video src="/assets/movie.mp4" autoplay="autoplay" controls="controls"></video>
516
- #
517
- # @example Fallback Content
518
- #
519
- # <%=
520
- # video('movie.mp4') do
521
- # "Your browser does not support the video tag"
522
- # end
523
- # %>
524
- #
525
- # # <video src="/assets/movie.mp4">
526
- # # Your browser does not support the video tag
527
- # # </video>
528
- #
529
- # @example Tracks
530
- #
531
- # <%=
532
- # video('movie.mp4') do
533
- # track(kind: 'captions', src: asset_path('movie.en.vtt'),
534
- # srclang: 'en', label: 'English')
535
- # end
536
- # %>
537
- #
538
- # # <video src="/assets/movie.mp4">
539
- # # <track kind="captions" src="/assets/movie.en.vtt" srclang="en" label="English">
540
- # # </video>
541
- #
542
- # @example Sources
543
- #
544
- # <%=
545
- # video do
546
- # text "Your browser does not support the video tag"
547
- # source(src: asset_path('movie.mp4'), type: 'video/mp4')
548
- # source(src: asset_path('movie.ogg'), type: 'video/ogg')
549
- # end
550
- # %>
551
- #
552
- # # <video>
553
- # # Your browser does not support the video tag
554
- # # <source src="/assets/movie.mp4" type="video/mp4">
555
- # # <source src="/assets/movie.ogg" type="video/ogg">
556
- # # </video>
557
- #
558
- # @example Without Any Argument
559
- #
560
- # <%= video %>
561
- #
562
- # # ArgumentError
563
- #
564
- # @example Without src And Without Block
565
- #
566
- # <%= video(content: true) %>
567
- #
568
- # # ArgumentError
569
- #
570
- # @example Fingerprint Mode
571
- #
572
- # <%= video 'movie.mp4' %>
573
- #
574
- # # <video src="/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
575
- #
576
- # @example CDN Mode
577
- #
578
- # <%= video 'movie.mp4' %>
579
- #
580
- # # <video src="https://assets.bookshelf.org/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
581
- #
582
- # @example Enable Push Promise/Early Hints
583
- #
584
- # <%= video 'movie.mp4', push: true %>
585
- #
586
- # <%=
587
- # video do
588
- # text "Your browser does not support the video tag"
589
- # source src: asset_path("movie.mp4", push: :video), type: "video/mp4"
590
- # source src: asset_path("movie.ogg"), type: "video/ogg"
591
- # end
592
- # %>
593
- def video(source = nil, options = {}, &blk)
594
- options = _source_options(source, options, as: :video, &blk)
595
- html.video(blk, options)
596
- end
597
-
598
- # Generate <tt>audio</tt> tag for given source
599
- #
600
- # It accepts one string representing the name of the asset, if it comes
601
- # from the application or third party gems. It also accepts string
602
- # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
603
- #
604
- # Alternatively, it accepts a block that allows to specify one or more
605
- # sources via the <tt>source</tt> tag.
606
- #
607
- # If the "fingerprint mode" is on, <tt>src</tt> is the fingerprinted
608
- # version of the relative URL.
609
- #
610
- # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
611
- # application CDN.
612
- #
613
- # @param source [String] asset name or absolute URL
614
- # @param options [Hash] HTML 5 attributes
615
- # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
616
- #
617
- # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
618
- #
619
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
620
- # `subresource_integrity` modes are on and the audio file is missing
621
- # from the manifest
622
- #
623
- # @raise [ArgumentError] if source isn't specified both as argument or
624
- # tag inside the given block
625
- #
626
- # @since 0.1.0
627
- #
628
- # @see Hanami::Assets::Configuration#fingerprint
629
- # @see Hanami::Assets::Configuration#cdn
630
- # @see Hanami::Assets::Helpers#asset_path
631
- #
632
- # @example Basic Usage
633
- #
634
- # <%= audio 'song.ogg' %>
635
- #
636
- # # <audio src="/assets/song.ogg"></audio>
637
- #
638
- # @example Absolute URL
639
- #
640
- # <%= audio 'https://example-cdn.com/assets/song.ogg' %>
641
- #
642
- # # <audio src="https://example-cdn.com/assets/song.ogg"></audio>
643
- #
644
- # @example Custom HTML Attributes
645
- #
646
- # <%= audio('song.ogg', autoplay: true, controls: true) %>
647
- #
648
- # # <audio src="/assets/song.ogg" autoplay="autoplay" controls="controls"></audio>
649
- #
650
- # @example Fallback Content
651
- #
652
- # <%=
653
- # audio('song.ogg') do
654
- # "Your browser does not support the audio tag"
655
- # end
656
- # %>
657
- #
658
- # # <audio src="/assets/song.ogg">
659
- # # Your browser does not support the audio tag
660
- # # </audio>
661
- #
662
- # @example Tracks
663
- #
664
- # <%=
665
- # audio('song.ogg') do
666
- # track(kind: 'captions', src: asset_path('song.pt-BR.vtt'),
667
- # srclang: 'pt-BR', label: 'Portuguese')
668
- # end
669
- # %>
670
- #
671
- # # <audio src="/assets/song.ogg">
672
- # # <track kind="captions" src="/assets/song.pt-BR.vtt" srclang="pt-BR" label="Portuguese">
673
- # # </audio>
674
- #
675
- # @example Sources
676
- #
677
- # <%=
678
- # audio do
679
- # text "Your browser does not support the audio tag"
680
- # source(src: asset_path('song.ogg'), type: 'audio/ogg')
681
- # source(src: asset_path('song.wav'), type: 'auido/wav')
682
- # end
683
- # %>
684
- #
685
- # # <audio>
686
- # # Your browser does not support the audio tag
687
- # # <source src="/assets/song.ogg" type="audio/ogg">
688
- # # <source src="/assets/song.wav" type="auido/wav">
689
- # # </audio>
690
- #
691
- # @example Without Any Argument
692
- #
693
- # <%= audio %>
694
- #
695
- # # ArgumentError
696
- #
697
- # @example Without src And Without Block
698
- #
699
- # <%= audio(controls: true) %>
700
- #
701
- # # ArgumentError
702
- #
703
- # @example Fingerprint Mode
704
- #
705
- # <%= audio 'song.ogg' %>
706
- #
707
- # # <audio src="/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
708
- #
709
- # @example CDN Mode
710
- #
711
- # <%= audio 'song.ogg' %>
712
- #
713
- # # <audio src="https://assets.bookshelf.org/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
714
- #
715
- # @example Enable Push Promise/Early Hints
716
- #
717
- # <%= audio 'movie.mp4', push: true %>
718
- #
719
- # <%=
720
- # audio do
721
- # text "Your browser does not support the audio tag"
722
- # source src: asset_path("song.ogg", push: :audio), type: "audio/ogg"
723
- # source src: asset_path("song.wav"), type: "audio/wav"
724
- # end
725
- # %>
726
- def audio(source = nil, options = {}, &blk)
727
- options = _source_options(source, options, as: :audio, &blk)
728
- html.audio(blk, options)
729
- end
730
-
731
- # It generates the relative URL for the given source.
732
- #
733
- # It can be the name of the asset, coming from the sources or third party
734
- # gems.
735
- #
736
- # Absolute URLs are returned as they are.
737
- #
738
- # If Fingerprint mode is on, it returns the fingerprinted path of the source
739
- #
740
- # If CDN mode is on, it returns the absolute URL of the asset.
741
- #
742
- # @param source [String] the asset name
743
- # @param push [TrueClass, FalseClass, Symbol] HTTP/2 Push Promise/Early Hints flag, or type
744
- # @param as [Symbol] HTTP/2 Push Promise / Early Hints flag type
745
- #
746
- # @return [String] the asset path
747
- #
748
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
749
- # `subresource_integrity` modes are on and the asset is missing
750
- # from the manifest
751
- #
752
- # @since 0.1.0
753
- #
754
- # @example Basic Usage
755
- #
756
- # <%= asset_path 'application.js' %>
757
- #
758
- # # "/assets/application.js"
759
- #
760
- # @example Absolute URL
761
- #
762
- # <%= asset_path 'https://code.jquery.com/jquery-2.1.4.min.js' %>
763
- #
764
- # # "https://code.jquery.com/jquery-2.1.4.min.js"
765
- #
766
- # @example Fingerprint Mode
767
- #
768
- # <%= asset_path 'application.js' %>
769
- #
770
- # # "/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
771
- #
772
- # @example CDN Mode
773
- #
774
- # <%= asset_path 'application.js' %>
775
- #
776
- # # "https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
777
- #
778
- # @example Enable Push Promise/Early Hints
779
- #
780
- # <%= asset_path "application.js", push: :script %>
781
- def asset_path(source, push: false, as: nil)
782
- _asset_url(source, push: push, as: as) { _relative_url(source) }
783
- end
784
-
785
- # It generates the absolute URL for the given source.
786
- #
787
- # It can be the name of the asset, coming from the sources or third party
788
- # gems.
789
- #
790
- # Absolute URLs are returned as they are.
791
- #
792
- # If Fingerprint mode is on, it returns the fingerprint URL of the source
793
- #
794
- # If CDN mode is on, it returns the absolute URL of the asset.
795
- #
796
- # @param source [String] the asset name
797
- # @param push [TrueClass, FalseClass, Symbol] HTTP/2 Push Promise/Early Hints flag, or type
798
- # @param as [Symbol] HTTP/2 Push Promise / Early Hints flag type
799
- #
800
- # @return [String] the asset URL
801
- #
802
- # @raise [Hanami::Assets::MissingManifestAssetError] if `fingerprint` or
803
- # `subresource_integrity` modes are on and the asset is missing
804
- # from the manifest
805
- #
806
- # @since 0.1.0
807
- #
808
- # @example Basic Usage
809
- #
810
- # <%= asset_url 'application.js' %>
811
- #
812
- # # "https://bookshelf.org/assets/application.js"
813
- #
814
- # @example Absolute URL
815
- #
816
- # <%= asset_url 'https://code.jquery.com/jquery-2.1.4.min.js' %>
817
- #
818
- # # "https://code.jquery.com/jquery-2.1.4.min.js"
819
- #
820
- # @example Fingerprint Mode
821
- #
822
- # <%= asset_url 'application.js' %>
823
- #
824
- # # "https://bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
825
- #
826
- # @example CDN Mode
827
- #
828
- # <%= asset_url 'application.js' %>
829
- #
830
- # # "https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
831
- #
832
- # @example Enable Push Promise/Early Hints
833
- #
834
- # <%= asset_url "application.js", push: :script %>
835
- def asset_url(source, push: false, as: nil)
836
- _asset_url(source, push: push, as: as) { _absolute_url(source) }
837
- end
838
-
839
- private
840
-
841
- # @since 0.1.0
842
- # @api private
843
- def _safe_tags(*sources)
844
- ::Hanami::Utils::Escape::SafeString.new(
845
- sources.map do |source|
846
- yield source
847
- end.join(NEW_LINE_SEPARATOR)
848
- )
849
- end
850
-
851
- # @since 0.1.0
852
- # @api private
853
- def _asset_url(source, push:, as:)
854
- url = _absolute_url?(source) ? source : yield
855
-
856
- case push
857
- when Symbol
858
- _push_promise(url, as: push)
859
- when TrueClass
860
- _push_promise(url, as: as)
861
- end
862
-
863
- url
864
- end
865
-
866
- # @since 0.1.0
867
- # @api private
868
- def _typed_asset_path(source, ext, push: false, as: nil)
869
- source = "#{source}#{ext}" if _append_extension?(source, ext)
870
- asset_path(source, push: push, as: as)
871
- end
872
-
873
- # @api private
874
- def _subresource_integrity?
875
- !!self.class.assets_configuration.subresource_integrity
876
- end
877
-
878
- # @api private
879
- def _subresource_integrity_value(source, ext)
880
- source = "#{source}#{ext}" unless source =~ /#{Regexp.escape(ext)}\z/
881
- self.class.assets_configuration.subresource_integrity_value(source) unless _absolute_url?(source)
882
- end
883
-
884
- # @since 0.1.0
885
- # @api private
886
- def _absolute_url?(source)
887
- ABSOLUTE_URL_MATCHER.match(source)
888
- end
889
-
890
- # @since 1.2.0
891
- # @api private
892
- def _crossorigin?(source)
893
- return false unless _absolute_url?(source)
894
-
895
- self.class.assets_configuration.crossorigin?(source)
896
- end
897
-
898
- # @since 0.1.0
899
- # @api private
900
- def _relative_url(source)
901
- self.class.assets_configuration.asset_path(source)
902
- end
903
-
904
- # @since 0.1.0
905
- # @api private
906
- def _absolute_url(source)
907
- self.class.assets_configuration.asset_url(source)
908
- end
909
-
910
- # @since 0.1.0
911
- # @api private
912
- def _source_options(src, options, as:, &_blk)
913
- options ||= {}
914
-
915
- if src.respond_to?(:to_hash)
916
- options = src.to_hash
917
- elsif src
918
- options[:src] = asset_path(src, push: options.delete(:push) || false, as: as)
919
- end
920
-
921
- if !options[:src] && !block_given?
922
- raise ArgumentError.new("You should provide a source via `src` option or with a `source` HTML tag")
923
- end
924
-
925
- options
926
- end
927
-
928
- # @since 0.1.0
929
- # @api private
930
- def _push_promise(url, as: nil)
931
- Thread.current[:__hanami_assets] ||= {}
932
- Thread.current[:__hanami_assets][url.to_s] = {as: as, crossorigin: _crossorigin?(url)}
933
-
934
- url
935
- end
936
-
937
- # @since 1.1.0
938
- # @api private
939
- def _append_extension?(source, ext)
940
- source !~ QUERY_STRING_MATCHER && source !~ /#{Regexp.escape(ext)}\z/
941
- end
942
- end
943
- # rubocop:enable Metrics/ModuleLength
944
- end
945
- end