hanami-assets 1.3.5 → 2.1.0.beta2

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -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 +106 -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