alchemy-media 0.7.2 → 0.7.4

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.7.4 (2023-03-13)
2
+
3
+ * By default, conceal SVGs that have the `graphics-symbol` role from assistive technologies
4
+
5
+ ## 0.7.3 (2023-02-26)
6
+
7
+ * Add `Media#downloadImage()` method to the Media helper
8
+ * Add `al-icon-stack` element, to stack items on top of each other
9
+ * Fix `svg` files not being able to be uploaded or served
10
+ * Round image resizes to the highest 100 pixels
11
+ * Add `max_page_width` plugin option, which will be applied to the user's max screen size
12
+
1
13
  ## 0.7.2 (2023-01-23)
2
14
 
3
15
  * Make `al-icon` center its contents
@@ -23,4 +23,26 @@ al-icon {
23
23
  transform: translate(-50%, -50%);
24
24
  }
25
25
  }
26
+
27
+ &[size="2"] {
28
+ font-size: 2em;
29
+ }
30
+
31
+ &[size="3"] {
32
+ font-size: 3em;
33
+ }
34
+
35
+ &[size="4"] {
36
+ font-size: 4em;
37
+ }
38
+ }
39
+
40
+ al-icon-stack {
41
+ display: inline-grid;
42
+ grid-template-columns: 1fr;
43
+ grid-template-rows: 1fr;
44
+
45
+ al-icon {
46
+ grid-area: 1 / 1;
47
+ }
26
48
  }
@@ -47,6 +47,15 @@ Icon.setAttribute('icon-name');
47
47
  */
48
48
  Icon.setAttribute('icon-flags');
49
49
 
50
+ /**
51
+ * Set the size attribute
52
+ *
53
+ * @author Jelle De Loecker <jelle@elevenways.be>
54
+ * @since 0.7.3
55
+ * @version 0.7.3
56
+ */
57
+ Icon.setAttribute('size', {type: 'number'});
58
+
50
59
  /**
51
60
  * Set the default role
52
61
  *
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The al-icon-stack element
3
+ *
4
+ * @author Jelle De Loecker <jelle@elevenways.be>
5
+ * @since 0.7.3
6
+ * @version 0.7.3
7
+ */
8
+ const IconStack = Function.inherits('Alchemy.Element.Media.Base', 'AlIconStack');
9
+
10
+ /**
11
+ * Set the default role
12
+ *
13
+ * @author Jelle De Loecker <jelle@elevenways.be>
14
+ * @since 0.7.3
15
+ * @version 0.7.3
16
+ */
17
+ IconStack.setRole('graphics-symbol');
18
+
19
+ /**
20
+ * Set the size attribute
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 0.7.3
24
+ * @version 0.7.3
25
+ */
26
+ IconStack.setAttribute('size', {type: 'number'});
package/element/al_svg.js CHANGED
@@ -21,7 +21,7 @@ Svg.setAttribute('src');
21
21
  *
22
22
  * @author Jelle De Loecker <jelle@elevenways.be>
23
23
  * @since 0.5.1
24
- * @version 0.7.1
24
+ * @version 0.7.4
25
25
  */
26
26
  Svg.setMethod(async function injectSvg() {
27
27
 
@@ -59,6 +59,9 @@ Svg.setMethod(async function injectSvg() {
59
59
 
60
60
  if (!this.hasAttribute('aria-label')) {
61
61
  this.setAttribute('aria-label', '');
62
+
63
+ // graphics-symbol images are not important, so they can be hidden
64
+ this.setAttribute('aria-hidden', 'true');
62
65
  }
63
66
  }
64
67
 
@@ -457,13 +457,14 @@ Media.setMethod(function imageCssSet(image_id, options) {
457
457
  /**
458
458
  * Output an img element
459
459
  *
460
- * @author Jelle De Loecker <jelle@develry.be>
460
+ * @author Jelle De Loecker <jelle@elevenways.be>
461
461
  * @since 0.2.0
462
- * @version 0.2.0
462
+ * @version 0.7.3
463
463
  *
464
464
  * @param {String} image_id
465
+ * @param {Object} options
465
466
  *
466
- * @return {URL}
467
+ * @return {HTMLElement}
467
468
  */
468
469
  Media.setMethod(function image(image_id, options) {
469
470
 
@@ -497,6 +498,10 @@ Media.setMethod(function image(image_id, options) {
497
498
  // Create the element
498
499
  element = this.view.createElement('img');
499
500
 
501
+ if (options.onload) {
502
+ element.onload = options.onload;
503
+ }
504
+
500
505
  // Set the source attribute
501
506
  element.setAttribute('src', url);
502
507
 
@@ -525,6 +530,36 @@ Media.setMethod(function image(image_id, options) {
525
530
  return element;
526
531
  });
527
532
 
533
+ /**
534
+ * Get a downloaded image
535
+ *
536
+ * @author Jelle De Loecker <jelle@elevenways.be>
537
+ * @since 0.7.3
538
+ * @version 0.7.3
539
+ *
540
+ * @param {String} image_id
541
+ * @param {Object} options
542
+ *
543
+ * @return {HTMLElement}
544
+ */
545
+ Media.setMethod(function downloadImage(image_id, options) {
546
+
547
+ let pledge = new Pledge(),
548
+ element;
549
+
550
+ if (!options) {
551
+ options = {};
552
+ }
553
+
554
+ options.onload = () => {
555
+ pledge.resolve(element);
556
+ };
557
+
558
+ element = this.image(image_id, options);
559
+
560
+ return pledge;
561
+ });
562
+
528
563
  /**
529
564
  * Output a figure element
530
565
  *
@@ -16,7 +16,7 @@ const Image = Function.inherits('Alchemy.Widget', 'Image');
16
16
  *
17
17
  * @author Jelle De Loecker <jelle@elevenways.be>
18
18
  * @since 0.6.0
19
- * @version 0.7.1
19
+ * @version 0.7.3
20
20
  */
21
21
  Image.constitute(function prepareSchema() {
22
22
 
@@ -28,7 +28,7 @@ Image.constitute(function prepareSchema() {
28
28
  this.schema.addField('width_hint', 'Number', {
29
29
  description : 'Approximate maximum width of the image in pixels or as a percentage of the total page width.',
30
30
  widget_config_editable: true,
31
- default: '25%',
31
+ default: '50%',
32
32
  });
33
33
 
34
34
  this.schema.addField('lazy_load', 'Boolean', {
@@ -43,7 +43,7 @@ Image.constitute(function prepareSchema() {
43
43
  *
44
44
  * @author Jelle De Loecker <jelle@elevenways.be>
45
45
  * @since 0.6.0
46
- * @version 0.7.1
46
+ * @version 0.7.3
47
47
  *
48
48
  * @param {HTMLElement} widget
49
49
  */
@@ -55,7 +55,7 @@ Image.setMethod(function populateWidget() {
55
55
  lazy_load = this.config.lazy_load;
56
56
 
57
57
  if (width_hint === null || width_hint === '' || typeof width_hint == 'undefined') {
58
- width_hint = '25%';
58
+ width_hint = '50%';
59
59
  }
60
60
 
61
61
  if (lazy_load == null) {
@@ -33,31 +33,32 @@ ImageMedia.setProperty('typeMap', {
33
33
  /**
34
34
  * Generate a thumbnail of this type
35
35
  *
36
- * @author Jelle De Loecker <jelle@develry.be>
36
+ * @author Jelle De Loecker <jelle@elevenways.be>
37
37
  * @since 0.0.1
38
- * @version 0.6.0
38
+ * @version 0.7.3
39
39
  *
40
40
  * @param {Conduit} conduit
41
41
  * @param {Object} record
42
42
  */
43
43
  ImageMedia.setMethod(function thumbnail(conduit, record) {
44
44
 
45
- var dimension,
46
- options;
45
+ const path = record?.path;
47
46
 
48
- if (!record || !record.path) {
47
+ if (!path) {
49
48
  return conduit.notFound('Image could not be found');
50
49
  }
51
50
 
52
- if (record.MediaRaw && record.MediaRaw.mimetype == 'image/svg') {
53
- return conduit.serveFile(record.path, {
51
+ let mimetype = record?.MediaRaw?.mimetype;
52
+
53
+ if (typeof mimetype == 'string' && mimetype.startsWith('image/svg')) {
54
+ return conduit.serveFile(path, {
54
55
  mimetype : 'image/svg+xml',
55
56
  disposition : false
56
57
  });
57
58
  }
58
59
 
59
- dimension = 100 * this.getDpr(conduit);
60
- options = {};
60
+ let dimension = 100 * this.getDpr(conduit);
61
+ let options = {};
61
62
 
62
63
  // Set the square dimensions
63
64
  options.width = dimension;
@@ -79,12 +80,124 @@ ImageMedia.setMethod(function thumbnail(conduit, record) {
79
80
  });
80
81
  });
81
82
 
83
+ /**
84
+ * Get the wanted image dimension
85
+ *
86
+ * @author Jelle De Loecker <jelle@elevenways.be>
87
+ * @since 0.7.3
88
+ * @version 0.7.3
89
+ *
90
+ * @param {Conduit} conduit
91
+ *
92
+ * @return {Object}
93
+ */
94
+ ImageMedia.setMethod(function getResizeDimension(conduit) {
95
+
96
+ const query = conduit.url.query;
97
+
98
+ if (!query.width && !query.height) {
99
+ return;
100
+ }
101
+
102
+ let resolution = conduit.cookie('mediaResolution') || {},
103
+ dpr = this.getDpr(conduit);
104
+
105
+ let width = getResizedDimension(query, 'width', resolution, dpr),
106
+ height = getResizedDimension(query, 'height', resolution, dpr);
107
+
108
+ return {
109
+ width,
110
+ height
111
+ };
112
+ });
113
+
114
+ /**
115
+ * Get the wanted image dimension
116
+ *
117
+ * @author Jelle De Loecker <jelle@elevenways.be>
118
+ * @since 0.7.3
119
+ * @version 0.7.3
120
+ *
121
+ * @param {Conduit} conduit
122
+ * @param {String} type 'width' or 'height'
123
+ * @param {Object} resolution
124
+ * @param {Number} dpr
125
+ *
126
+ * @return {Object}
127
+ */
128
+ function getResizedDimension(query, type, resolution, dpr) {
129
+
130
+ if (type !== 'width' && type !== 'height') {
131
+ throw new Error('Invalid dimension type: ' + type);
132
+ }
133
+
134
+ let size_query = query[type];
135
+
136
+ if (!size_query) {
137
+ return;
138
+ }
139
+
140
+ let size_string = String(size_query);
141
+ let size = parseInt(size_string);
142
+
143
+ if (!size) {
144
+ if (type == 'width') {
145
+ size = 1920;
146
+ } else {
147
+ size = 1080;
148
+ }
149
+
150
+ size_string = ''+size;
151
+ }
152
+
153
+ if (size_string.includes('%') || size_string.includes('vw') || size_string.includes('vh')) {
154
+ let base_size = Number(resolution[type]) || query['max' + type.capitalize()],
155
+ percentage = size;
156
+
157
+ if (!base_size) {
158
+ if (type == 'width') {
159
+ base_size = 1920;
160
+ } else {
161
+ base_size = 1080;
162
+ }
163
+ }
164
+
165
+ // It's called "max_page_width", but height can also be checked against this
166
+ if (base_size > alchemy.plugins.media.max_page_width) {
167
+ base_size = alchemy.plugins.media.max_page_width;
168
+ }
169
+
170
+ size = (base_size * percentage) / 100;
171
+ }
172
+
173
+ size *= dpr;
174
+
175
+ // Round to the nearest 100
176
+ size = Math.ceil(size / 100) * 100;
177
+
178
+ if (!size) {
179
+ size = 100;
180
+ }
181
+
182
+ if (type == 'width') {
183
+ if (size > 3840) {
184
+ size = 3840;
185
+ }
186
+ } else if (type == 'height') {
187
+ if (size > 2160) {
188
+ size = 2160;
189
+ }
190
+ }
191
+
192
+ return size;
193
+ };
194
+
82
195
  /**
83
196
  * Serve this file
84
197
  *
85
- * @author Jelle De Loecker <jelle@develry.be>
198
+ * @author Jelle De Loecker <jelle@elevenways.be>
86
199
  * @since 0.0.1
87
- * @version 0.6.1
200
+ * @version 0.7.3
88
201
  *
89
202
  * @param {Conduit} conduit
90
203
  * @param {Object} record
@@ -97,7 +210,9 @@ ImageMedia.setMethod(function serve(conduit, record, options) {
97
210
  return onError('Empty file record', 404);
98
211
  }
99
212
 
100
- if (record.MediaRaw && record.MediaRaw.mimetype == 'image/svg') {
213
+ let mimetype = record?.MediaRaw?.mimetype;
214
+
215
+ if (typeof mimetype == 'string' && mimetype.startsWith('image/svg')) {
101
216
  return conduit.serveFile(record.path, {
102
217
  mimetype : 'image/svg+xml',
103
218
  disposition : false
@@ -166,42 +281,7 @@ ImageMedia.setMethod(function serve(conduit, record, options) {
166
281
 
167
282
  if (query.width || query.height) {
168
283
 
169
- resolution = conduit.cookie('mediaResolution') || {};
170
-
171
- if (query.width) {
172
- width = parseInt(query.width) || 1920;
173
-
174
- if (String(query.width).indexOf('%') > -1 || String(query.width).indexOf('vw') > -1) {
175
- baseWidth = Number(resolution.width) || query.maxWidth || 1920;
176
- width = (baseWidth * width)/100;
177
- width *= dpr;
178
-
179
- // Round to the nearest 10
180
- width = Math.ceil(width / 10) * 10;
181
- } else {
182
- width *= dpr;
183
- }
184
- }
185
-
186
- if (query.height) {
187
- height = parseInt(query.height) || 1080;
188
-
189
- if (String(query.height).indexOf('%') > -1 || String(query.height).indexOf('vh') > -1) {
190
- baseHeight = Number(resolution.height) || query.maxHeight || 1080;
191
- height = (baseHeight * height)/100;
192
- height *= dpr;
193
-
194
- // Round to the nearest 10
195
- height = Math.ceil(height / 10) * 10;
196
- } else {
197
- height *= dpr;
198
- }
199
- }
200
-
201
- resizeOptions = {
202
- width: width,
203
- height: height
204
- };
284
+ resizeOptions = this.getResizeDimension(conduit);
205
285
 
206
286
  if (query.quality) {
207
287
  if (query.quality > 0 && query.quality < 100) {
@@ -277,30 +357,24 @@ ImageMedia.setMethod(function serve(conduit, record, options) {
277
357
  /**
278
358
  * Generate a placeholder
279
359
  *
280
- * @author Jelle De Loecker <jelle@develry.be>
360
+ * @author Jelle De Loecker <jelle@elevenways.be>
281
361
  * @since 0.1.0
282
- * @version 0.4.0
362
+ * @version 0.7.3
283
363
  *
284
364
  * @param {Conduit} conduit
285
365
  * @param {Object} options
286
366
  */
287
367
  ImageMedia.setMethod(function placeholder(conduit, options) {
288
368
 
289
- var defaultOptions,
290
- resolution = conduit.cookie('mediaResolution') || {},
291
- baseHeight,
292
- baseWidth,
369
+ let default_options,
293
370
  profile,
294
- height,
295
- width,
296
- query = conduit.url.query,
297
- dpr = this.getDpr(conduit);
371
+ query = conduit.url.query;
298
372
 
299
373
  if (options == null) {
300
374
  options = {};
301
375
  }
302
376
 
303
- defaultOptions = {
377
+ default_options = {
304
378
  width: 300,
305
379
  height: 300
306
380
  };
@@ -314,47 +388,23 @@ ImageMedia.setMethod(function placeholder(conduit, options) {
314
388
  options.height = profile.height || profile.width;
315
389
  }
316
390
 
317
- if (query.width) {
318
- width = parseInt(query.width) || 1920;
319
-
320
- // See if the given width is a percentage
321
- if (String(query.width).indexOf('%') > -1 || String(query.width).indexOf('vw') > -1) {
322
- baseWidth = Number(resolution.width) || query.maxWidth || 1920;
323
- width = (baseWidth * width)/100;
324
- width *= dpr;
391
+ if (query.width || query.height) {
392
+ let resize_options = this.getResizeDimension(conduit);
325
393
 
326
- // Round to the nearest 10
327
- width = Math.ceil(width / 10) * 10;
328
- } else {
329
- width *= dpr;
394
+ if (resize_options.width) {
395
+ options.width = resize_options.width;
330
396
  }
331
397
 
332
- options.width = width;
333
- }
334
-
335
- if (query.height) {
336
- height = parseInt(query.height) || 1080;
337
-
338
- // See if the given width is a percentage
339
- if (String(query.height).indexOf('%') > -1 || String(query.height).indexOf('vh') > -1) {
340
- baseHeight = Number(resolution.height) || query.maxHeight || 1080;
341
- height = (baseHeight * height)/100;
342
- height *= dpr;
343
-
344
- // Round to the nearest 10
345
- height = Math.ceil(height / 10) * 10;
346
- } else {
347
- height *= dpr;
398
+ if (resize_options.height) {
399
+ options.height = resize_options.height;
348
400
  }
349
-
350
- options.height = height;
351
401
  }
352
402
 
353
403
  if (!options.text) {
354
404
  options.text = conduit.param('text');
355
405
  }
356
406
 
357
- options = Object.assign({}, defaultOptions, options);
407
+ options = Object.assign({}, default_options, options);
358
408
 
359
409
  this.veronica.placeholder(options, function gotPlaceholderPath(err, filepath) {
360
410
  conduit.serveFile(filepath, {disposition: false});
@@ -411,15 +461,17 @@ ImageMedia.setMethod(function getSize(conduit, filePath, options, callback) {
411
461
  * This method should strip out the meta data,
412
462
  * so it can be stored inside MediaFile instead of MediaRaw
413
463
  *
414
- * @author Jelle De Loecker <jelle@develry.be>
464
+ * @author Jelle De Loecker <jelle@elevenways.be>
415
465
  * @since 0.0.1
416
- * @version 0.6.0
466
+ * @version 0.7.3
417
467
  */
418
468
  ImageMedia.setMethod(function normalize(filePath, baseInfo, callback) {
419
469
 
420
- var that = this;
470
+ const that = this;
471
+
472
+ let mimetype = baseInfo?.mimetype;
421
473
 
422
- if (baseInfo && baseInfo.mimetype == 'image/svg') {
474
+ if (typeof mimetype == 'string' && mimetype.startsWith('image/svg')) {
423
475
  return callback(null, filePath);
424
476
  }
425
477
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alchemy-media",
3
3
  "description": "The media plugin for Alchemy",
4
- "version": "0.7.2",
4
+ "version": "0.7.4",
5
5
  "repository": {
6
6
  "type" : "git",
7
7
  "url" : "https://github.com/11ways/alchemy-media.git"