alchemy-media 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/assets/stylesheets/alchemy_media.scss +1 -0
- package/assets/stylesheets/{element/alchemy_file.scss → media_elements/_file.scss} +27 -1
- package/assets/stylesheets/{alchemy_icons.scss → media_elements/_icon.scss} +1 -1
- package/assets/stylesheets/media_elements/_image.scss +25 -0
- package/assets/stylesheets/{alchemy_svg.scss → media_elements/_svg.scss} +0 -0
- package/assets/stylesheets/media_elements/index.scss +4 -0
- package/bootstrap.js +10 -1
- package/controller/media_files_controller.js +101 -20
- package/element/00-media_base.js +17 -0
- package/element/al_file.js +59 -13
- package/element/{al_ico_element.js → al_icon.js} +69 -21
- package/element/al_image.js +3 -5
- package/element/al_svg.js +2 -11
- package/helper/media_helper.js +24 -1
- package/helper/widgets/image_widget.js +8 -8
- package/lib/media_types/image_media_type.js +3 -3
- package/model/media_file_model.js +44 -13
- package/package.json +2 -2
- package/view/element/al_file.hwk +31 -4
- package/view/element/al_file_selection.hwk +35 -0
- package/view/form/inputs/edit/file_preview.hwk +5 -0
- package/view/form/inputs/view_inline/file.hwk +8 -0
- package/view/form/inputs/view_inline/file_preview.hwk +8 -0
- package/view/form/wrappers/edit/file_preview.hwk +13 -0
- package/assets/stylesheets/alchemy_icons_fafree.scss +0 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 0.7.0 (2022-11-02)
|
|
2
|
+
|
|
3
|
+
* Use the `al-` prefix for custom elements
|
|
4
|
+
* Rename `al-ico` to `al-icon`
|
|
5
|
+
* Allow selecting existing uploaded files in an `al-file` element
|
|
6
|
+
|
|
7
|
+
## 0.6.4 (2022-10-13)
|
|
8
|
+
|
|
9
|
+
* Add the `Media#loadIconFont()` helper method
|
|
10
|
+
* Make the `image` route serve up filetype thumbnails for non-images
|
|
11
|
+
* Add file preview image to the chimera edit view of MediaFile
|
|
12
|
+
* Add thumbnail column to MediaFile chimera index view
|
|
13
|
+
* Add `prefix` parameter to the `MediaFile#data` route
|
|
14
|
+
|
|
1
15
|
## 0.6.3 (2022-07-23)
|
|
2
16
|
|
|
3
17
|
* Fix `al-file` element showing wrong buttons on load
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "media_elements/index.scss";
|
|
@@ -37,7 +37,7 @@ al-file {
|
|
|
37
37
|
visibility: hidden;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
al-
|
|
40
|
+
al-icon {
|
|
41
41
|
position: absolute;
|
|
42
42
|
top: 0;
|
|
43
43
|
left: 0;
|
|
@@ -92,4 +92,30 @@ al-file {
|
|
|
92
92
|
button {
|
|
93
93
|
min-height: 2rem;
|
|
94
94
|
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
al-field[field-view="file_preview"] {
|
|
98
|
+
[data-he-name="field"] .field {
|
|
99
|
+
padding: 0;
|
|
100
|
+
|
|
101
|
+
img {
|
|
102
|
+
max-height: 25rem;
|
|
103
|
+
object-fit: contain;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
al-field[field-view="file_preview"],
|
|
109
|
+
al-field[field-view="file"],
|
|
110
|
+
al-field[field-type="file"] {
|
|
111
|
+
|
|
112
|
+
&[mode="inline"] {
|
|
113
|
+
.field img {
|
|
114
|
+
object-fit: contain;
|
|
115
|
+
width: 100px;
|
|
116
|
+
height: 100px;
|
|
117
|
+
display: block;
|
|
118
|
+
margin: auto;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
95
121
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
al-image {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-block;
|
|
4
|
+
overflow: hidden;
|
|
5
|
+
|
|
6
|
+
.placeholder {
|
|
7
|
+
position: absolute;
|
|
8
|
+
left: 0;
|
|
9
|
+
top: 0;
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100%;
|
|
12
|
+
transform: scale(1.03);
|
|
13
|
+
filter: blur(8px);
|
|
14
|
+
z-index: -1;
|
|
15
|
+
transition: 0.1s transform;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.final {
|
|
19
|
+
height: auto;
|
|
20
|
+
width: auto;
|
|
21
|
+
opacity: 0;
|
|
22
|
+
transition: 0.1s opacity, 0.1s transform;
|
|
23
|
+
transform: scale(1.03)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
File without changes
|
package/bootstrap.js
CHANGED
|
@@ -43,9 +43,18 @@ alchemy.createDir(options.cache);
|
|
|
43
43
|
Router.get('Media::static', /\/media\/static\/(.*)*/, 'MediaFile#serveStatic');
|
|
44
44
|
Router.get('Media::image', options.url + '/{id}', 'MediaFile#image');
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// The prefix is added at the end of the route so it does not
|
|
47
|
+
// change the user's active_prefix
|
|
48
|
+
Router.get('MediaFile#data', '/media/data/{prefix}/{id}', 'MediaFile#data');
|
|
47
49
|
Router.get('MediaFile#info', '/media/info', 'MediaFile#info');
|
|
48
50
|
|
|
51
|
+
Router.add({
|
|
52
|
+
name : 'MediaFile#recordsource',
|
|
53
|
+
methods : ['get'],
|
|
54
|
+
paths : '/media/recordsource',
|
|
55
|
+
permission : 'media.recordsource',
|
|
56
|
+
});
|
|
57
|
+
|
|
49
58
|
// Allow dummy extensions
|
|
50
59
|
Router.get('Media::fileextension', '/media/file/{id}.{extension}', 'MediaFile#file');
|
|
51
60
|
|
|
@@ -13,16 +13,14 @@ var fs = require('fs'),
|
|
|
13
13
|
* @since 0.0.1
|
|
14
14
|
* @version 0.3.0
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
MediaFile.super.call(this, conduit, options);
|
|
18
|
-
});
|
|
16
|
+
const MediaFiles = Function.inherits('Alchemy.Controller', 'MediaFile');
|
|
19
17
|
|
|
20
18
|
/**
|
|
21
19
|
* Serve a thumbnail
|
|
22
20
|
*
|
|
23
21
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
24
22
|
* @since 0.0.1
|
|
25
|
-
* @version 0.4
|
|
23
|
+
* @version 0.6.4
|
|
26
24
|
*
|
|
27
25
|
* @param {Conduit} conduit
|
|
28
26
|
*/
|
|
@@ -35,21 +33,15 @@ MediaFiles.setAction(function thumbnail(conduit, id) {
|
|
|
35
33
|
// Get the requested file
|
|
36
34
|
this.getModel('MediaFile').getFile(id, function gotFile(err, record) {
|
|
37
35
|
|
|
38
|
-
var Type;
|
|
39
|
-
|
|
40
36
|
if (err) {
|
|
41
37
|
return conduit.notFound(err);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
Type = MediaTypes[record.type];
|
|
45
|
-
|
|
46
|
-
if (!Type) {
|
|
47
|
-
Type = Classes.Alchemy.MediaType;
|
|
48
|
-
}
|
|
40
|
+
const Type = MediaTypes[record.type] || Classes.Alchemy.MediaType;
|
|
49
41
|
|
|
50
42
|
if (Type) {
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
let instance = new Type();
|
|
44
|
+
instance.thumbnail(conduit, record);
|
|
53
45
|
} else {
|
|
54
46
|
conduit.error('Error generating thumbnail of ' + record.type);
|
|
55
47
|
}
|
|
@@ -100,7 +92,7 @@ MediaFiles.setAction(function serveStatic(conduit, path) {
|
|
|
100
92
|
*
|
|
101
93
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
102
94
|
* @since 0.0.1
|
|
103
|
-
* @version 0.4
|
|
95
|
+
* @version 0.6.4
|
|
104
96
|
*
|
|
105
97
|
* @param {Conduit} conduit
|
|
106
98
|
*/
|
|
@@ -112,12 +104,22 @@ MediaFiles.setAction(function image(conduit, id) {
|
|
|
112
104
|
|
|
113
105
|
this.getModel('MediaFile').getFile(id, function gotFile(err, record) {
|
|
114
106
|
|
|
115
|
-
|
|
107
|
+
if (err) {
|
|
108
|
+
return conduit.error(err);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const Image = new MediaTypes.image;
|
|
116
112
|
|
|
117
113
|
if (!record) {
|
|
118
114
|
return Image.placeholder(conduit, {text: 404, status: 404});
|
|
119
115
|
}
|
|
120
116
|
|
|
117
|
+
if (record.type != 'image') {
|
|
118
|
+
const Type = MediaTypes[record.type] || Classes.Alchemy.MediaType;
|
|
119
|
+
let instance = new Type();
|
|
120
|
+
return instance.thumbnail(conduit, record);
|
|
121
|
+
}
|
|
122
|
+
|
|
121
123
|
Image.serve(conduit, record);
|
|
122
124
|
});
|
|
123
125
|
});
|
|
@@ -315,14 +317,23 @@ MediaFiles.setAction(function uploadsingle(conduit) {
|
|
|
315
317
|
/**
|
|
316
318
|
* Get the file data
|
|
317
319
|
*
|
|
318
|
-
* @author Jelle De Loecker <jelle@
|
|
320
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
319
321
|
* @since 0.5.1
|
|
320
|
-
* @version 0.
|
|
322
|
+
* @version 0.6.4
|
|
321
323
|
*
|
|
322
|
-
* @param {Conduit}
|
|
323
|
-
* @param {
|
|
324
|
+
* @param {Conduit} conduit
|
|
325
|
+
* @param {ObjectId} media_file_id
|
|
324
326
|
*/
|
|
325
|
-
MediaFiles.setAction(function data(conduit,
|
|
327
|
+
MediaFiles.setAction(async function data(conduit, prefix, media_file_id) {
|
|
328
|
+
|
|
329
|
+
conduit.prefix = prefix;
|
|
330
|
+
|
|
331
|
+
let media_file = await this.model.findById(media_file_id);
|
|
332
|
+
|
|
333
|
+
if (!media_file) {
|
|
334
|
+
return conduit.notFound();
|
|
335
|
+
}
|
|
336
|
+
|
|
326
337
|
conduit.setHeader('cache-control', 'public, max-age=3600, must-revalidate');
|
|
327
338
|
|
|
328
339
|
conduit.end({
|
|
@@ -371,4 +382,74 @@ MediaFiles.setAction(function info(conduit) {
|
|
|
371
382
|
conduit.end(size);
|
|
372
383
|
});
|
|
373
384
|
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Recordsource action
|
|
389
|
+
*
|
|
390
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
391
|
+
* @since 0.7.0
|
|
392
|
+
* @version 0.7.0
|
|
393
|
+
*
|
|
394
|
+
* @param {Conduit} conduit
|
|
395
|
+
*/
|
|
396
|
+
MediaFiles.setAction(async function recordsource(conduit) {
|
|
397
|
+
|
|
398
|
+
const model = this.model;
|
|
399
|
+
|
|
400
|
+
let crit = model.find();
|
|
401
|
+
|
|
402
|
+
model.disableTranslations();
|
|
403
|
+
|
|
404
|
+
let page_size = conduit.param('page_size'),
|
|
405
|
+
filters = conduit.param('filters'),
|
|
406
|
+
fields = conduit.param('fields'),
|
|
407
|
+
page = conduit.param('page'),
|
|
408
|
+
sort = conduit.param('sort');
|
|
409
|
+
|
|
410
|
+
if (fields) {
|
|
411
|
+
|
|
412
|
+
// @TODO: fix FieldSet being sent with regular json?
|
|
413
|
+
if (fields.fields) {
|
|
414
|
+
fields = fields.fields;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
crit.select(fields);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (page_size && !page) {
|
|
421
|
+
page = 1;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (page) {
|
|
425
|
+
crit.page(page, page_size);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (sort?.field && sort?.dir) {
|
|
429
|
+
crit.sort([sort.field, sort.dir]);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (filters) {
|
|
433
|
+
let key,
|
|
434
|
+
val;
|
|
435
|
+
|
|
436
|
+
for (key in filters) {
|
|
437
|
+
val = filters[key];
|
|
438
|
+
|
|
439
|
+
// The value should always be a string,
|
|
440
|
+
// so anything that is falsy can be ignored
|
|
441
|
+
if (!val) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
val = RegExp.interpretWildcard('*' + val + '*', 'i');
|
|
446
|
+
crit.where(key).equals(val);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let records = await model.find('all', crit);
|
|
451
|
+
|
|
452
|
+
conduit.end({
|
|
453
|
+
records : records
|
|
454
|
+
});
|
|
374
455
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The base class for all other media elements
|
|
3
|
+
*
|
|
4
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
5
|
+
* @since 0.7.0
|
|
6
|
+
* @version 0.7.0
|
|
7
|
+
*/
|
|
8
|
+
const Base = Function.inherits('Alchemy.Element', 'Alchemy.Element.Media', 'Base');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The stylesheet to load for this element
|
|
12
|
+
*
|
|
13
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
14
|
+
* @since 0.7.0
|
|
15
|
+
* @version 0.7.0
|
|
16
|
+
*/
|
|
17
|
+
Base.setStylesheetFile('alchemy_media');
|
package/element/al_file.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
5
5
|
* @since 0.6.0
|
|
6
|
-
* @version 0.
|
|
6
|
+
* @version 0.7.0
|
|
7
7
|
*/
|
|
8
|
-
const AlFile = Function.inherits('Alchemy.Element.
|
|
8
|
+
const AlFile = Function.inherits('Alchemy.Element.Media.Base', 'AlFile');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The template code
|
|
@@ -16,15 +16,6 @@ const AlFile = Function.inherits('Alchemy.Element.App', 'AlFile');
|
|
|
16
16
|
*/
|
|
17
17
|
AlFile.setTemplateFile('element/al_file');
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* The stylesheet to load for this element
|
|
21
|
-
*
|
|
22
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
23
|
-
* @since 0.6.0
|
|
24
|
-
* @version 0.6.0
|
|
25
|
-
*/
|
|
26
|
-
AlFile.setStylesheetFile('element/alchemy_file');
|
|
27
|
-
|
|
28
19
|
/**
|
|
29
20
|
* Getter for the drop target
|
|
30
21
|
*
|
|
@@ -61,6 +52,15 @@ AlFile.addElementGetter('preview_element', '.al-file-preview');
|
|
|
61
52
|
*/
|
|
62
53
|
AlFile.addElementGetter('remove_button', '.al-file-remove');
|
|
63
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Getter for the select button
|
|
57
|
+
*
|
|
58
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
59
|
+
* @since 0.7.0
|
|
60
|
+
* @version 0.7.0
|
|
61
|
+
*/
|
|
62
|
+
AlFile.addElementGetter('select_button', '.al-file-select');
|
|
63
|
+
|
|
64
64
|
/**
|
|
65
65
|
* Getter for the uploading-icon
|
|
66
66
|
*
|
|
@@ -245,6 +245,11 @@ AlFile.setMethod(function introduced() {
|
|
|
245
245
|
this.value = null;
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
+
if (this.select_button) {
|
|
249
|
+
this.select_button.addEventListener('click', e => {
|
|
250
|
+
this.showExistingFileSelection();
|
|
251
|
+
});
|
|
252
|
+
}
|
|
248
253
|
});
|
|
249
254
|
|
|
250
255
|
/**
|
|
@@ -265,11 +270,52 @@ AlFile.setMethod(function _getContent(callback) {
|
|
|
265
270
|
return callback(err);
|
|
266
271
|
}
|
|
267
272
|
|
|
268
|
-
console.log('Size:', info);
|
|
269
|
-
|
|
270
273
|
that.innerHTML = '<img class="final" src="/media/static/' + src + '?width=50%25" width=' + info.width + ' height=' + info.height +' style="width:400px">'
|
|
271
274
|
+ '<img class="placeholder" src="/media/static/' + src + '?width=20px">';
|
|
272
275
|
|
|
273
276
|
callback(null);
|
|
274
277
|
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Select existing files
|
|
282
|
+
*
|
|
283
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
284
|
+
* @since 0.7.0
|
|
285
|
+
* @version 0.7.0
|
|
286
|
+
*/
|
|
287
|
+
AlFile.setMethod(async function showExistingFileSelection() {
|
|
288
|
+
|
|
289
|
+
let variables = {};
|
|
290
|
+
|
|
291
|
+
await hawkejs.scene.render('element/al_file_selection', variables);
|
|
292
|
+
|
|
293
|
+
let dialog_contents = document.querySelector('he-dialog [data-he-template="element/al_file_selection"]');
|
|
294
|
+
|
|
295
|
+
if (!dialog_contents) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let dialog = dialog_contents.queryParents('he-dialog'),
|
|
300
|
+
button = dialog_contents.querySelector('.btn-apply');
|
|
301
|
+
|
|
302
|
+
dialog_contents.classList.add('default-form-editor');
|
|
303
|
+
hawkejs.scene.enableStyle('chimera/chimera');
|
|
304
|
+
|
|
305
|
+
button.addEventListener('click', e => {
|
|
306
|
+
e.preventDefault();
|
|
307
|
+
|
|
308
|
+
let table = dialog.querySelector('al-table');
|
|
309
|
+
|
|
310
|
+
if (table) {
|
|
311
|
+
let row = table.active_row;
|
|
312
|
+
|
|
313
|
+
if (row && row.dataset.pk) {
|
|
314
|
+
this.value = row.dataset.pk;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
dialog.remove();
|
|
319
|
+
});
|
|
320
|
+
|
|
275
321
|
});
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
* The al-ico element
|
|
3
|
-
*
|
|
4
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
5
|
-
* @since 0.6.0
|
|
6
|
-
* @version 0.6.0
|
|
7
|
-
*/
|
|
8
|
-
const Icon = Function.inherits('Alchemy.Element', 'AlIco');
|
|
1
|
+
const BUSY = Symbol('busy');
|
|
9
2
|
|
|
10
3
|
/**
|
|
11
|
-
* The
|
|
4
|
+
* The al-icon element
|
|
12
5
|
*
|
|
13
|
-
* @author Jelle De Loecker
|
|
6
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
14
7
|
* @since 0.6.0
|
|
15
|
-
* @version 0.
|
|
8
|
+
* @version 0.7.0
|
|
16
9
|
*/
|
|
17
|
-
Icon.
|
|
10
|
+
const Icon = Function.inherits('Alchemy.Element.Media.Base', 'AlIcon');
|
|
18
11
|
|
|
19
12
|
/**
|
|
20
13
|
* The source to use
|
|
@@ -45,15 +38,35 @@ Icon.setAttribute('icon-style');
|
|
|
45
38
|
*/
|
|
46
39
|
Icon.setAttribute('icon-name');
|
|
47
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Extra options/flags
|
|
43
|
+
*
|
|
44
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
45
|
+
* @since 0.7.0
|
|
46
|
+
* @version 0.7.0
|
|
47
|
+
*/
|
|
48
|
+
Icon.setAttribute('icon-flags');
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Refresh the icon when these attributes change
|
|
52
|
+
*
|
|
53
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
54
|
+
* @since 0.7.0
|
|
55
|
+
* @version 0.7.0
|
|
56
|
+
*/
|
|
57
|
+
Icon.addObservedAttribute(['icon-style', 'icon-name', 'icon-flags'], function onChange() {
|
|
58
|
+
this.refresh();
|
|
59
|
+
});
|
|
60
|
+
|
|
48
61
|
/**
|
|
49
62
|
* The element is being retained
|
|
50
63
|
*
|
|
51
64
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
52
65
|
* @since 0.6.3
|
|
53
|
-
* @version 0.
|
|
66
|
+
* @version 0.7.0
|
|
54
67
|
*/
|
|
55
68
|
Icon.setMethod(function retained() {
|
|
56
|
-
this.
|
|
69
|
+
this.refresh(true);
|
|
57
70
|
});
|
|
58
71
|
|
|
59
72
|
/**
|
|
@@ -61,10 +74,36 @@ Icon.setMethod(function retained() {
|
|
|
61
74
|
*
|
|
62
75
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
63
76
|
* @since 0.6.3
|
|
64
|
-
* @version 0.
|
|
77
|
+
* @version 0.7.0
|
|
65
78
|
*/
|
|
66
79
|
Icon.setMethod(function introduced() {
|
|
80
|
+
this.refresh();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Refresh the icon
|
|
85
|
+
*
|
|
86
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
87
|
+
* @since 0.7.0
|
|
88
|
+
* @version 0.7.0
|
|
89
|
+
*
|
|
90
|
+
* @param {Boolean} force
|
|
91
|
+
*/
|
|
92
|
+
Icon.setMethod(function refresh(force) {
|
|
93
|
+
|
|
94
|
+
if (this[BUSY] && !force) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!force && Blast.isNode) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this[BUSY] = true;
|
|
103
|
+
|
|
67
104
|
this.setCssClasses();
|
|
105
|
+
|
|
106
|
+
this[BUSY] = false;
|
|
68
107
|
});
|
|
69
108
|
|
|
70
109
|
/**
|
|
@@ -80,6 +119,8 @@ Icon.setMethod(function setIcon(info) {
|
|
|
80
119
|
return;
|
|
81
120
|
}
|
|
82
121
|
|
|
122
|
+
this[BUSY] = true;
|
|
123
|
+
|
|
83
124
|
if (typeof info == 'string') {
|
|
84
125
|
info = info.split(' ');
|
|
85
126
|
|
|
@@ -105,6 +146,8 @@ Icon.setMethod(function setIcon(info) {
|
|
|
105
146
|
if (!this.icon_style) {
|
|
106
147
|
this.icon_style = 'duotone';
|
|
107
148
|
}
|
|
149
|
+
|
|
150
|
+
this.refresh(true);
|
|
108
151
|
});
|
|
109
152
|
|
|
110
153
|
/**
|
|
@@ -112,7 +155,7 @@ Icon.setMethod(function setIcon(info) {
|
|
|
112
155
|
*
|
|
113
156
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
114
157
|
* @since 0.6.3
|
|
115
|
-
* @version 0.6.
|
|
158
|
+
* @version 0.6.4
|
|
116
159
|
*/
|
|
117
160
|
Icon.setMethod(function setCssClasses() {
|
|
118
161
|
|
|
@@ -120,14 +163,13 @@ Icon.setMethod(function setCssClasses() {
|
|
|
120
163
|
return;
|
|
121
164
|
}
|
|
122
165
|
|
|
166
|
+
// Load the appropriate font style
|
|
167
|
+
this.hawkejs_renderer.helpers.Media.loadIconFont();
|
|
168
|
+
|
|
123
169
|
let fa_pro = this.hawkejs_renderer.expose('fontawesome_pro'),
|
|
124
170
|
style = this.icon_style || 'regular';
|
|
125
171
|
|
|
126
|
-
if (fa_pro) {
|
|
127
|
-
this.hawkejs_renderer.style(fa_pro);
|
|
128
|
-
} else {
|
|
129
|
-
this.hawkejs_renderer.style('alchemy_icons_fafree');
|
|
130
|
-
|
|
172
|
+
if (!fa_pro) {
|
|
131
173
|
if (style == 'duotone' || style == 'light' || style == 'thin' || style == 'regular') {
|
|
132
174
|
style = 'solid';
|
|
133
175
|
}
|
|
@@ -144,4 +186,10 @@ Icon.setMethod(function setCssClasses() {
|
|
|
144
186
|
|
|
145
187
|
this.classList.add('fa-' + (this.icon_style || 'solid'));
|
|
146
188
|
this.classList.add('fa-' + this.icon_name);
|
|
189
|
+
|
|
190
|
+
let flags = (this.icon_flags || '').split(/\s+/);
|
|
191
|
+
|
|
192
|
+
for (let flag of flags) {
|
|
193
|
+
this.classList.add('fa-' + flag);
|
|
194
|
+
}
|
|
147
195
|
});
|
package/element/al_image.js
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
5
5
|
* @since 0.4.1
|
|
6
|
-
* @version 0.
|
|
6
|
+
* @version 0.7.0
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
return AlImage.super.call(this);
|
|
10
|
-
});
|
|
8
|
+
const AlImage = Function.inherits('Alchemy.Element.Media.Base', 'AlImage');
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* CSS
|
|
@@ -35,7 +33,7 @@ AlImage.setMethod(function introduced() {
|
|
|
35
33
|
|
|
36
34
|
var placeholder = this.querySelector('.placeholder'),
|
|
37
35
|
final = this.querySelector('.final');
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
final.onload = function() {
|
|
40
38
|
console.log('Final is loaded!');
|
|
41
39
|
final.style.opacity = 1;
|
package/element/al_svg.js
CHANGED
|
@@ -3,18 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
5
5
|
* @since 0.5.1
|
|
6
|
-
* @version 0.
|
|
7
|
-
*/
|
|
8
|
-
const Svg = Function.inherits('Alchemy.Element', 'AlSvg');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The stylesheet to load for this element
|
|
12
|
-
*
|
|
13
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
14
|
-
* @since 0.5.1
|
|
15
|
-
* @version 0.5.1
|
|
6
|
+
* @version 0.7.0
|
|
16
7
|
*/
|
|
17
|
-
Svg.
|
|
8
|
+
const Svg = Function.inherits('Alchemy.Element.Media.Base', 'AlSvg');
|
|
18
9
|
|
|
19
10
|
/**
|
|
20
11
|
* The location of the svg
|
package/helper/media_helper.js
CHANGED
|
@@ -88,6 +88,28 @@ Media.setStatic(function loadImagesBasedOnSize() {
|
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Load the icon font
|
|
93
|
+
*
|
|
94
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
95
|
+
* @since 0.6.4
|
|
96
|
+
* @version 0.6.4
|
|
97
|
+
*
|
|
98
|
+
* @return {string}
|
|
99
|
+
*/
|
|
100
|
+
Media.setMethod(function loadIconFont() {
|
|
101
|
+
|
|
102
|
+
let font_style = this.hawkejs_renderer.expose('fontawesome_pro');
|
|
103
|
+
|
|
104
|
+
if (!font_style) {
|
|
105
|
+
font_style = 'alchemy_icons_fafree';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.hawkejs_renderer.style(font_style);
|
|
109
|
+
|
|
110
|
+
return font_style;
|
|
111
|
+
});
|
|
112
|
+
|
|
91
113
|
/**
|
|
92
114
|
* Apply directive to an element
|
|
93
115
|
*
|
|
@@ -173,7 +195,8 @@ Media.setMethod(function applyDirective(element, image, options) {
|
|
|
173
195
|
this.view.helpers.Alchemy.getResource({
|
|
174
196
|
name: 'MediaFile#data',
|
|
175
197
|
params: {
|
|
176
|
-
id: image
|
|
198
|
+
id : image,
|
|
199
|
+
prefix : this.view.expose('active_prefix') || '__',
|
|
177
200
|
}
|
|
178
201
|
}, function gotResult(err, data) {
|
|
179
202
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @constructor
|
|
5
5
|
*
|
|
6
6
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
7
|
-
* @since 0.
|
|
8
|
-
* @version 0.
|
|
7
|
+
* @since 0.6.0
|
|
8
|
+
* @version 0.6.0
|
|
9
9
|
*
|
|
10
10
|
* @param {Object} data
|
|
11
11
|
*/
|
|
@@ -15,8 +15,8 @@ const Image = Function.inherits('Alchemy.Widget', 'Image');
|
|
|
15
15
|
* Prepare the schema
|
|
16
16
|
*
|
|
17
17
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
18
|
-
* @since 0.
|
|
19
|
-
* @version 0.
|
|
18
|
+
* @since 0.6.0
|
|
19
|
+
* @version 0.6.0
|
|
20
20
|
*/
|
|
21
21
|
Image.constitute(function prepareSchema() {
|
|
22
22
|
|
|
@@ -29,8 +29,8 @@ Image.constitute(function prepareSchema() {
|
|
|
29
29
|
* Populate the widget
|
|
30
30
|
*
|
|
31
31
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
32
|
-
* @since 0.
|
|
33
|
-
* @version 0.
|
|
32
|
+
* @since 0.6.0
|
|
33
|
+
* @version 0.6.4
|
|
34
34
|
*
|
|
35
35
|
* @param {HTMLElement} widget
|
|
36
36
|
*/
|
|
@@ -40,7 +40,7 @@ Image.setMethod(function populateWidget() {
|
|
|
40
40
|
|
|
41
41
|
this.hawkejs_renderer.helpers.Media.applyDirective(img, this.config.image);
|
|
42
42
|
|
|
43
|
-
populateWidget.super.call(this);
|
|
44
|
-
|
|
45
43
|
this.widget.append(img);
|
|
44
|
+
|
|
45
|
+
return populateWidget.super.call(this);
|
|
46
46
|
});
|
|
@@ -14,10 +14,10 @@ var exiv2 = alchemy.use('@11ways/exiv2'),
|
|
|
14
14
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
15
15
|
* @since 0.0.1
|
|
16
16
|
* @version 0.2.0
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} options
|
|
17
19
|
*/
|
|
18
|
-
|
|
19
|
-
ImageMediaType.super.call(this, options);
|
|
20
|
-
});
|
|
20
|
+
const ImageMedia = Function.inherits('Alchemy.MediaType', 'ImageMediaType');
|
|
21
21
|
|
|
22
22
|
ImageMedia.setProperty('exivPath', alchemy.plugins.media.exiv2);
|
|
23
23
|
ImageMedia.setProperty('hashType', alchemy.plugins.media.hash);
|
|
@@ -19,32 +19,48 @@ var MediaFile = Function.inherits('Alchemy.Model', function MediaFile(options) {
|
|
|
19
19
|
|
|
20
20
|
MediaFile.setProperty('types', alchemy.shared('Media.types'));
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* The default sort options
|
|
24
|
+
*
|
|
25
|
+
* @type {Object}
|
|
26
|
+
*/
|
|
27
|
+
MediaFile.prepareProperty('sort', function sort() {
|
|
28
|
+
return {created: -1};
|
|
29
|
+
});
|
|
30
|
+
|
|
22
31
|
/**
|
|
23
32
|
* Constitute the class wide schema
|
|
24
33
|
*
|
|
25
|
-
* @author Jelle De Loecker <jelle@
|
|
34
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
26
35
|
* @since 0.2.0
|
|
27
|
-
* @version 0.
|
|
36
|
+
* @version 0.6.4
|
|
28
37
|
*/
|
|
29
38
|
MediaFile.constitute(function addFields() {
|
|
30
39
|
|
|
31
|
-
this.addField('name', 'String'
|
|
32
|
-
|
|
40
|
+
this.addField('name', 'String', {
|
|
41
|
+
description: 'The name of the file',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.addField('filename', 'String', {
|
|
45
|
+
description : 'The actual filename',
|
|
46
|
+
});
|
|
33
47
|
|
|
34
48
|
this.addField('type', 'Enum', {
|
|
35
|
-
values: alchemy.getClassGroup('media_type')
|
|
49
|
+
values: alchemy.getClassGroup('media_type'),
|
|
50
|
+
description: 'The type of file',
|
|
36
51
|
});
|
|
37
52
|
|
|
38
53
|
this.addField('extra', 'Object');
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
55
|
+
this.addField('title', 'String', {
|
|
56
|
+
translatable : alchemy.plugins.media.translatable,
|
|
57
|
+
description : 'The title of the file (will be used in the title attribute)',
|
|
58
|
+
});
|
|
45
59
|
|
|
46
|
-
this.addField('
|
|
47
|
-
|
|
60
|
+
this.addField('alt', 'String', {
|
|
61
|
+
translatable : alchemy.plugins.media.translatable,
|
|
62
|
+
description : 'The alternative information of the file (will be used in the alt attribute)',
|
|
63
|
+
});
|
|
48
64
|
|
|
49
65
|
this.belongsTo('MediaRaw');
|
|
50
66
|
});
|
|
@@ -54,7 +70,7 @@ MediaFile.constitute(function addFields() {
|
|
|
54
70
|
*
|
|
55
71
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
56
72
|
* @since 0.2.0
|
|
57
|
-
* @version 0.
|
|
73
|
+
* @version 0.6.4
|
|
58
74
|
*/
|
|
59
75
|
MediaFile.constitute(function chimeraConfig() {
|
|
60
76
|
|
|
@@ -69,6 +85,15 @@ MediaFile.constitute(function chimeraConfig() {
|
|
|
69
85
|
// Get the list group
|
|
70
86
|
list = this.chimera.getActionFields('list');
|
|
71
87
|
|
|
88
|
+
list.addField('_id', {
|
|
89
|
+
view : 'file_preview',
|
|
90
|
+
wrapper : 'file_preview',
|
|
91
|
+
title : 'Thumbnail',
|
|
92
|
+
filter : false,
|
|
93
|
+
sortable : false,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
list.addField('created');
|
|
72
97
|
list.addField('name');
|
|
73
98
|
list.addField('filename');
|
|
74
99
|
list.addField('type');
|
|
@@ -78,6 +103,12 @@ MediaFile.constitute(function chimeraConfig() {
|
|
|
78
103
|
// Get the edit group
|
|
79
104
|
edit = this.chimera.getActionFields('edit');
|
|
80
105
|
|
|
106
|
+
edit.addField('_id', {
|
|
107
|
+
view : 'file_preview',
|
|
108
|
+
wrapper : 'file_preview',
|
|
109
|
+
title : 'Preview',
|
|
110
|
+
});
|
|
111
|
+
|
|
81
112
|
edit.addField('name');
|
|
82
113
|
edit.addField('filename');
|
|
83
114
|
edit.addField('type');
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy-media",
|
|
3
3
|
"description": "The media plugin for Alchemy",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type" : "git",
|
|
7
|
-
"url" : "https://github.com/
|
|
7
|
+
"url" : "https://github.com/11ways/alchemy-media.git"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@11ways/exiv2" : "~0.6.4",
|
package/view/element/al_file.hwk
CHANGED
|
@@ -1,19 +1,46 @@
|
|
|
1
1
|
<label class="al-file-drop-target">
|
|
2
2
|
<div class="al-file-preview"></div>
|
|
3
|
-
|
|
4
|
-
<al-
|
|
3
|
+
|
|
4
|
+
<al-icon
|
|
5
|
+
icon-name="spinner"
|
|
6
|
+
icon-style="duotone"
|
|
7
|
+
class="uploading-icon fa-spin-pulse"
|
|
8
|
+
></al-icon>
|
|
9
|
+
|
|
10
|
+
<al-icon
|
|
11
|
+
icon-name="cloud-arrow-up"
|
|
12
|
+
icon-style="duotone"
|
|
13
|
+
class="empty-icon"
|
|
14
|
+
><% if (self.value) $0.hidden = true %></al-icon>
|
|
15
|
+
|
|
5
16
|
<input type="file" tabindex="-1" class="al-file-input" hidden>
|
|
6
17
|
</label>
|
|
7
18
|
|
|
8
19
|
<div class="al-file-right">
|
|
9
20
|
<button class="al-file-choose-existing">
|
|
10
|
-
<al-
|
|
21
|
+
<al-icon
|
|
22
|
+
icon-name="image"
|
|
23
|
+
icon-style="duotone"
|
|
24
|
+
></al-icon>
|
|
11
25
|
{%t "select-uploaded-file" %}
|
|
12
26
|
</button>
|
|
13
27
|
|
|
14
28
|
<button class="al-file-remove">
|
|
15
29
|
<% if (!self.value) $0.hidden = true %>
|
|
16
|
-
<al-
|
|
30
|
+
<al-icon
|
|
31
|
+
icon-name="trash-can"
|
|
32
|
+
icon-style="duotone"
|
|
33
|
+
></al-icon>
|
|
17
34
|
{%t "remove-current-file" %}
|
|
18
35
|
</button>
|
|
36
|
+
|
|
37
|
+
{% if Acl.hasPermission('media.recordsource') %}
|
|
38
|
+
<button class="al-file-select">
|
|
39
|
+
<al-icon
|
|
40
|
+
icon-name="gallery-thumbnails"
|
|
41
|
+
icon-style="duotone"
|
|
42
|
+
></al-icon>
|
|
43
|
+
{%t "select-existing-file" %}
|
|
44
|
+
</button>
|
|
45
|
+
{% /if %}
|
|
19
46
|
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<% makeDialog() %>
|
|
2
|
+
<% addClass('alchemy-file-selection-dialog') %>
|
|
3
|
+
|
|
4
|
+
<%
|
|
5
|
+
|
|
6
|
+
fieldset = new Blast.Classes.Alchemy.Criteria.FieldSet('list', 'MediaFile');
|
|
7
|
+
|
|
8
|
+
fieldset.addField('_id', {
|
|
9
|
+
view : 'file_preview',
|
|
10
|
+
wrapper : 'file_preview',
|
|
11
|
+
title : 'Thumbnail',
|
|
12
|
+
filter : false,
|
|
13
|
+
sortable : false,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
fieldset.addField('filename');
|
|
17
|
+
fieldset.addField('type');
|
|
18
|
+
%>
|
|
19
|
+
|
|
20
|
+
<div class="alchemy-file-selection-wrapper">
|
|
21
|
+
|
|
22
|
+
<al-table
|
|
23
|
+
id="al-file-selection-table"
|
|
24
|
+
purpose="view"
|
|
25
|
+
mode="inline"
|
|
26
|
+
page-size=20
|
|
27
|
+
show-filters
|
|
28
|
+
#fieldset={% fieldset %}
|
|
29
|
+
#recordsource={% {route: 'MediaFile#recordsource'} %}
|
|
30
|
+
></al-table>
|
|
31
|
+
|
|
32
|
+
<button class="btn btn-apply">
|
|
33
|
+
Apply
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<div class="form-field-info">
|
|
2
|
+
<al-label>
|
|
3
|
+
<span
|
|
4
|
+
data-he-name="field-title"
|
|
5
|
+
data-he-slot="field-title"
|
|
6
|
+
><%= alchemy_field.field_title %></span>
|
|
7
|
+
<small
|
|
8
|
+
data-he-name="field-description"
|
|
9
|
+
data-he-slot="field-description"
|
|
10
|
+
><%= alchemy_field.field_description %></small>
|
|
11
|
+
</al-label>
|
|
12
|
+
</div>
|
|
13
|
+
<div data-he-name="field"></div>
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
@use "fontawesome6/fontawesome.scss";
|
|
2
|
-
@use "fontawesome6/solid.scss";
|
|
3
|
-
@use "fontawesome6/brands.scss";
|
|
4
|
-
|
|
5
|
-
al-ico {
|
|
6
|
-
@extend %fa-icon;
|
|
7
|
-
|
|
8
|
-
&[type="arrow-left"] {
|
|
9
|
-
@extend .fas, .fa-angle-left;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
&[type="arrow-left-double"] {
|
|
13
|
-
@extend .fas, .fa-angle-double-left;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
&[type="arrow-right"] {
|
|
17
|
-
@extend .fas, .fa-angle-right;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
&[type="arrow-right-double"] {
|
|
21
|
-
@extend .fas, .fa-angle-double-right;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
&[type="sorting-arrow"] {
|
|
25
|
-
@extend .fas;
|
|
26
|
-
|
|
27
|
-
&.down {
|
|
28
|
-
@extend .fa-sort-alpha-up;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
&.up {
|
|
32
|
-
@extend .fa-sort-alpha-down;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
&[type="menu"] {
|
|
37
|
-
@extend .fas, .fa-bars;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
&[type="edit"] {
|
|
41
|
-
@extend .fas, .fa-pencil-alt;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
&[type="zoom-in"] {
|
|
45
|
-
@extend .fas, .fa-search-plus;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
&[type="microphone"] {
|
|
49
|
-
@extend .fas, .fa-microphone-alt;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
&[type="video"] {
|
|
53
|
-
@extend .fas, .fa-video;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
&[type="plus"] {
|
|
57
|
-
@extend .fas, .fa-plus;
|
|
58
|
-
}
|
|
59
|
-
}
|