hanami-assets 1.3.4 → 2.1.0.beta2

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