hoodcms 5.0.7 → 5.0.11
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/README.md +8 -6
- package/dist/js/admin.js +5 -5
- package/dist/js/app.js +4 -11
- package/dist/js/core/Alerts.d.ts +22 -0
- package/dist/js/core/Alerts.js +151 -0
- package/dist/js/core/BaseSite.d.ts +6 -0
- package/dist/js/core/BaseSite.js +14 -0
- package/dist/js/core/ColorPicker.d.ts +5 -0
- package/dist/js/core/ColorPicker.js +71 -0
- package/dist/js/core/DataList.d.ts +35 -0
- package/dist/js/core/DataList.js +56 -0
- package/dist/js/core/Editors.d.ts +16 -0
- package/dist/js/core/Editors.js +137 -0
- package/dist/js/core/Handlers.d.ts +47 -0
- package/dist/js/core/Handlers.js +160 -0
- package/dist/js/core/Helpers.d.ts +8 -0
- package/dist/js/core/Helpers.js +50 -0
- package/dist/js/core/HoodApi.d.ts +25 -0
- package/dist/js/core/HoodApi.js +22 -0
- package/dist/js/core/Inline.d.ts +27 -0
- package/dist/js/core/Inline.js +63 -0
- package/dist/js/core/Loader.d.ts +5 -0
- package/dist/js/core/Loader.js +13 -0
- package/dist/js/core/Media.d.ts +113 -0
- package/dist/js/core/Media.js +363 -0
- package/dist/js/core/Modal.d.ts +35 -0
- package/dist/js/core/Modal.js +69 -0
- package/dist/js/core/RandomStringGenerator.d.ts +11 -0
- package/dist/js/core/RandomStringGenerator.js +23 -0
- package/dist/js/core/Response.d.ts +20 -0
- package/dist/js/core/Response.js +13 -0
- package/dist/js/core/Uploader.d.ts +7 -0
- package/dist/js/core/Uploader.js +134 -0
- package/dist/js/core/Validator.d.ts +27 -0
- package/dist/js/core/Validator.js +80 -0
- package/dist/js/extensions/jqueryExtensions.d.ts +11 -0
- package/dist/js/extensions/jqueryExtensions.js +99 -0
- package/dist/js/extensions/numberExtensions.d.ts +8 -0
- package/dist/js/extensions/numberExtensions.js +19 -0
- package/dist/js/extensions/stringExtensions.d.ts +12 -0
- package/dist/js/extensions/stringExtensions.js +49 -0
- package/dist/js/index.d.ts +23 -0
- package/dist/js/index.js +23 -0
- package/dist/js/interfaces/KeyValue.d.ts +4 -0
- package/dist/js/interfaces/KeyValue.js +1 -0
- package/dist/js/login.js +1 -1
- package/dist/js/models/Content.d.ts +50 -0
- package/dist/js/models/Content.js +1 -0
- package/dist/js/models/Property.d.ts +9 -0
- package/dist/js/models/Property.js +2 -0
- package/dist/js/models/Users.d.ts +7 -0
- package/dist/js/models/Users.js +2 -0
- package/package.json +58 -55
- package/src/css/admin.css.map +1 -1
- package/src/css/editor.css.map +1 -1
- package/src/css/install.css.map +1 -1
- package/src/css/login.css.map +1 -1
- package/src/js/admin.js +231 -221
- package/src/js/admin.js.map +1 -1
- package/src/js/app.js +12 -30254
- package/src/js/app.js.map +1 -1
- package/src/js/login.js +1 -1
- package/src/scss/admin.scss +2 -6
- package/src/scss/app.scss +2 -2
- package/src/scss/editor.scss +2 -3
- package/src/scss/install.scss +1 -1
- package/src/scss/login.scss +2 -16
- package/src/ts/admin.ts +2 -6
- package/src/ts/app.ts +0 -2
- package/src/ts/core/BaseSite.ts +21 -0
- package/src/ts/core/Handlers.ts +0 -2
- package/src/ts/core/Helpers.ts +4 -0
- package/src/ts/index.ts +26 -0
- package/src/ts/hood.ts +0 -5
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { Alerts } from "./Alerts";
|
|
2
|
+
import { Response } from "./Response";
|
|
3
|
+
import { DataList } from "./DataList";
|
|
4
|
+
import { ModalController } from "./Modal";
|
|
5
|
+
import { Validator } from "./Validator";
|
|
6
|
+
import { Inline } from "./Inline";
|
|
7
|
+
export class MediaService {
|
|
8
|
+
constructor(element, options) {
|
|
9
|
+
this.options = {
|
|
10
|
+
action: 'show',
|
|
11
|
+
size: 'large'
|
|
12
|
+
};
|
|
13
|
+
this.galleryInitialised = false;
|
|
14
|
+
this.selectedMedia = new Array();
|
|
15
|
+
this.element = element;
|
|
16
|
+
if (!this.element) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.options = Object.assign(Object.assign({}, this.options), options);
|
|
20
|
+
$('body').off('click', '.media-delete', this.delete.bind(this));
|
|
21
|
+
$('body').on('click', '.media-delete', this.delete.bind(this));
|
|
22
|
+
$(this.element).on('click', '.media-item', this.action.bind(this));
|
|
23
|
+
$(this.element).on('click', '.media-create-directory', this.createDirectory.bind(this));
|
|
24
|
+
$(this.element).on('click', '.media-delete-directory', this.deleteDirectory.bind(this));
|
|
25
|
+
this.media = new DataList(this.element, {
|
|
26
|
+
onLoad: this.options.onListLoad,
|
|
27
|
+
onError: this.options.onError,
|
|
28
|
+
onRender: this.options.onListRender,
|
|
29
|
+
onComplete: function (html, sender) {
|
|
30
|
+
this.initUploader();
|
|
31
|
+
// if this is gallery type, add the "Add to gallery button" and hook it to the add function
|
|
32
|
+
if (this.options.action == 'gallery' && !this.galleryInitialised) {
|
|
33
|
+
$('#media-select-modal .modal-footer').removeClass('d-none');
|
|
34
|
+
$('#media-select-modal .modal-footer').on('click', this.galleryAdd.bind(this));
|
|
35
|
+
this.galleryInitialised = true;
|
|
36
|
+
}
|
|
37
|
+
if (this.options.onListComplete) {
|
|
38
|
+
this.options.onListComplete(html, sender);
|
|
39
|
+
}
|
|
40
|
+
}.bind(this)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
initUploader() {
|
|
44
|
+
this.uploadButton = document.getElementById('media-add');
|
|
45
|
+
this.uploader = document.getElementById('media-upload');
|
|
46
|
+
if (!this.uploadButton || !this.uploader)
|
|
47
|
+
return;
|
|
48
|
+
this.progressArea = document.getElementById('media-progress');
|
|
49
|
+
this.progressText = $('<div class="progress-text text-muted mb-3"><i class="fa fa-info-circle me-2"></i>Uploading file <span></span>...</div>');
|
|
50
|
+
this.progress = $('<div class="progress"><div class= "progress-bar progress-bar-striped" role="progressbar" style="width:0%" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100" ></div></div>');
|
|
51
|
+
this.progressText.appendTo(this.progressArea);
|
|
52
|
+
this.progress.appendTo(this.progressArea);
|
|
53
|
+
var dz = null;
|
|
54
|
+
$("#media-upload").dropzone({
|
|
55
|
+
url: $("#media-upload").data('url') + "?directoryId=" + $("#media-list > #upload-directory-id").val(),
|
|
56
|
+
thumbnailWidth: 80,
|
|
57
|
+
thumbnailHeight: 80,
|
|
58
|
+
parallelUploads: 5,
|
|
59
|
+
paramName: 'files',
|
|
60
|
+
acceptedFiles: $("#media-upload").data('types') || ".png,.jpg,.jpeg,.gif",
|
|
61
|
+
autoProcessQueue: true,
|
|
62
|
+
previewsContainer: false,
|
|
63
|
+
clickable: "#media-add",
|
|
64
|
+
dictDefaultMessage: '<span><i class="fa fa-cloud-upload fa-4x"></i><br />Drag and drop files here, or simply click me!</div>',
|
|
65
|
+
dictResponseError: 'Error while uploading file!',
|
|
66
|
+
init: function () {
|
|
67
|
+
dz = this;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
dz.on("error", function (file, errormessage) {
|
|
71
|
+
Alerts.warning(errormessage);
|
|
72
|
+
}.bind(this));
|
|
73
|
+
dz.on("success", function (file, data) {
|
|
74
|
+
Response.process(data);
|
|
75
|
+
}.bind(this));
|
|
76
|
+
dz.on("addedfile", function (file) {
|
|
77
|
+
this.progress.find('.progress-bar').css({ width: 0 + "%" });
|
|
78
|
+
this.progressText.find('span').html(0 + "%");
|
|
79
|
+
}.bind(this));
|
|
80
|
+
// Update the total progress bar
|
|
81
|
+
dz.on("totaluploadprogress", function (totalProgress, totalBytes, totalBytesSent) {
|
|
82
|
+
this.progress.find('.progress-bar').css({ width: totalProgress + "%" });
|
|
83
|
+
this.progressText.find('span').html(Math.round(totalProgress) + "% - " + totalBytesSent.formatKilobytes() + " / " + totalBytes.formatKilobytes());
|
|
84
|
+
}.bind(this));
|
|
85
|
+
dz.on("sending", function (file) {
|
|
86
|
+
// Show the total progress bar when upload starts
|
|
87
|
+
this.progressArea.classList.remove('collapse');
|
|
88
|
+
this.progress.find('.progress-bar').css({ width: 0 + "%" });
|
|
89
|
+
this.progressText.find('span').html(0 + "%");
|
|
90
|
+
}.bind(this));
|
|
91
|
+
// Hide the total progress bar when nothing's uploading anymore
|
|
92
|
+
dz.on("complete", function (file) {
|
|
93
|
+
this.media.reload();
|
|
94
|
+
}.bind(this));
|
|
95
|
+
// Hide the total progress bar when nothing's uploading anymore
|
|
96
|
+
dz.on("queuecomplete", function () {
|
|
97
|
+
this.progressArea.classList.add('collapse');
|
|
98
|
+
this.media.reload();
|
|
99
|
+
}.bind(this));
|
|
100
|
+
}
|
|
101
|
+
createDirectory(e) {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
e.stopPropagation();
|
|
104
|
+
let createDirectoryModal = new ModalController({
|
|
105
|
+
onComplete: function () {
|
|
106
|
+
let form = document.getElementById('content-directories-edit-form');
|
|
107
|
+
new Validator(form, {
|
|
108
|
+
onComplete: function (response) {
|
|
109
|
+
Response.process(response, 5000);
|
|
110
|
+
if (response.success) {
|
|
111
|
+
this.media.reload();
|
|
112
|
+
createDirectoryModal.close();
|
|
113
|
+
}
|
|
114
|
+
}.bind(this)
|
|
115
|
+
});
|
|
116
|
+
}.bind(this)
|
|
117
|
+
});
|
|
118
|
+
createDirectoryModal.show($(e.currentTarget).attr('href'), this.element);
|
|
119
|
+
}
|
|
120
|
+
deleteDirectory(e) {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
e.stopPropagation();
|
|
123
|
+
Alerts.confirm({}, function (result) {
|
|
124
|
+
if (result.isConfirmed) {
|
|
125
|
+
Inline.post(e.currentTarget.href, e.currentTarget, function (response, sender) {
|
|
126
|
+
// Refresh the list, using the parent directory id - stored in the response's data array.
|
|
127
|
+
Response.process(response, 5000);
|
|
128
|
+
if (response.data.length > 0) {
|
|
129
|
+
var listUrl = document.createElement('a');
|
|
130
|
+
listUrl.href = $(this.element).data('url');
|
|
131
|
+
listUrl.search = `?dir=${response.data[0]}`;
|
|
132
|
+
this.media.reload(new URL(listUrl.href));
|
|
133
|
+
}
|
|
134
|
+
}.bind(this));
|
|
135
|
+
}
|
|
136
|
+
}.bind(this));
|
|
137
|
+
}
|
|
138
|
+
uploadUrl() {
|
|
139
|
+
return $("#media-upload").data('url') + "?directoryId=" + $("#media-list > #upload-directory-id").val();
|
|
140
|
+
}
|
|
141
|
+
action(e) {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
// Load media object from clicked item in the media list
|
|
145
|
+
let mediaObject = $(e.currentTarget).data('json');
|
|
146
|
+
// Perform the chosen action, which is set on the service's options when loaded.
|
|
147
|
+
switch (this.options.action) {
|
|
148
|
+
case 'select':
|
|
149
|
+
this.select(mediaObject, e);
|
|
150
|
+
break;
|
|
151
|
+
case 'insert':
|
|
152
|
+
this.insert(mediaObject, e);
|
|
153
|
+
break;
|
|
154
|
+
case 'attach':
|
|
155
|
+
this.attach(mediaObject, e);
|
|
156
|
+
break;
|
|
157
|
+
case 'gallery':
|
|
158
|
+
this.galleryClick(mediaObject, e);
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
this.show(mediaObject, e);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
show(mediaObject, sender) {
|
|
166
|
+
// Load the media as a new blade to display.
|
|
167
|
+
this.currentBlade = new ModalController();
|
|
168
|
+
this.currentBlade.show($(sender.target).data('blade'), sender.target);
|
|
169
|
+
// TODO: On close, reload the list and reinstate the modal??
|
|
170
|
+
}
|
|
171
|
+
select(mediaObject, e) {
|
|
172
|
+
Alerts.log(`[MediaService.select] Selecting media object id ${mediaObject.id} - ${mediaObject.filename} and inserting ${this.options.size} url to target: ${this.options.target}`);
|
|
173
|
+
if (this.options.target) {
|
|
174
|
+
let target = $(this.options.target);
|
|
175
|
+
switch (this.options.size) {
|
|
176
|
+
case 'thumb':
|
|
177
|
+
target.val(mediaObject.thumbUrl);
|
|
178
|
+
break;
|
|
179
|
+
case 'small':
|
|
180
|
+
target.val(mediaObject.smallUrl);
|
|
181
|
+
break;
|
|
182
|
+
case 'medium':
|
|
183
|
+
target.val(mediaObject.mediumUrl);
|
|
184
|
+
break;
|
|
185
|
+
case 'large':
|
|
186
|
+
target.val(mediaObject.largeUrl);
|
|
187
|
+
break;
|
|
188
|
+
case 'full':
|
|
189
|
+
target.val(mediaObject.url);
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (this.options.refresh) {
|
|
194
|
+
MediaService.refresh(mediaObject, this.options.refresh);
|
|
195
|
+
}
|
|
196
|
+
// Run the callback for onAction
|
|
197
|
+
if (this.options.onAction) {
|
|
198
|
+
this.options.onAction(mediaObject);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
insert(mediaObject, e) {
|
|
202
|
+
// basic functionality to insert the correct string from the media response (from uploader) to given input element.
|
|
203
|
+
Alerts.log(`[MediaService.insert] Selecting media object id ${mediaObject.id} - ${mediaObject.filename} and inserting ${this.options.size} image to target editor: ${this.options.target}`);
|
|
204
|
+
this.options.targetEditor.insertContent('<img alt="' + mediaObject.filename + '" src="' + mediaObject.url + '" class="img-fluid" />');
|
|
205
|
+
// Run the callback for onAction
|
|
206
|
+
if (this.options.onAction) {
|
|
207
|
+
this.options.onAction(mediaObject);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
attach(mediaObject, e) {
|
|
211
|
+
// once file is uploaded to given directory, send media id to the given attach endpoint.
|
|
212
|
+
Alerts.log(`[MediaService.attach] Attaching media object id ${mediaObject.id} - ${mediaObject.filename} to url: ${this.options.url}`);
|
|
213
|
+
$.post(this.options.url, { mediaId: mediaObject.id }, function (response) {
|
|
214
|
+
Response.process(response, 5000);
|
|
215
|
+
MediaService.refresh(response.media, this.options.refresh);
|
|
216
|
+
// Run the callback for onAction
|
|
217
|
+
if (this.options.onAction) {
|
|
218
|
+
this.options.onAction(mediaObject);
|
|
219
|
+
}
|
|
220
|
+
}.bind(this));
|
|
221
|
+
}
|
|
222
|
+
galleryClick(mediaObject, e) {
|
|
223
|
+
// once file is uploaded to given directory, send media id to the given attach endpoint.
|
|
224
|
+
if (!this.isMediaSelected(mediaObject)) {
|
|
225
|
+
Alerts.log(`[MediaService.galleryClick] Adding to selected media objects - id ${mediaObject.id} - ${mediaObject.filename}.`);
|
|
226
|
+
this.selectedMedia.push(mediaObject);
|
|
227
|
+
$(e.currentTarget).parents('.media-item').addClass('active');
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
Alerts.log(`[MediaService.galleryClick] Removing media from selection - id ${mediaObject.id} - ${mediaObject.filename}.}`);
|
|
231
|
+
this.selectedMedia = this.selectedMedia.filter(function (obj) {
|
|
232
|
+
return obj.id !== mediaObject.id;
|
|
233
|
+
});
|
|
234
|
+
$(e.currentTarget).parents('.media-item').removeClass('active');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
galleryAdd(e) {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
e.stopPropagation();
|
|
240
|
+
// once file is uploaded to given directory, send media id to the given attach endpoint.
|
|
241
|
+
Alerts.log(`[MediaService.galleryAdd] Adding ${this.selectedMedia.length} selected media objects to url: ${this.options.url}`);
|
|
242
|
+
let mediaIds = this.selectedMedia.map(function (v) {
|
|
243
|
+
return v.id;
|
|
244
|
+
});
|
|
245
|
+
// create the url to send to (add media id's to it as query params)
|
|
246
|
+
$.post(this.options.url, { media: mediaIds }, function (data) {
|
|
247
|
+
Response.process(data);
|
|
248
|
+
// refresh the gallery - -
|
|
249
|
+
let galleryEl = document.getElementById(this.options.target);
|
|
250
|
+
let gallery = galleryEl.hoodDataList;
|
|
251
|
+
gallery.reload();
|
|
252
|
+
// Run the callback for onAction
|
|
253
|
+
if (this.options.onAction) {
|
|
254
|
+
this.options.onAction(data.media);
|
|
255
|
+
}
|
|
256
|
+
}.bind(this));
|
|
257
|
+
}
|
|
258
|
+
isMediaSelected(mediaObject) {
|
|
259
|
+
let added = false;
|
|
260
|
+
this.selectedMedia.forEach(function (value, index, array) {
|
|
261
|
+
if (value.id == mediaObject.id) {
|
|
262
|
+
added = true;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
return added;
|
|
266
|
+
}
|
|
267
|
+
static refresh(media, refresh) {
|
|
268
|
+
let icon = media.icon;
|
|
269
|
+
if (media.genericFileType === "Image") {
|
|
270
|
+
icon = media.mediumUrl;
|
|
271
|
+
}
|
|
272
|
+
if (refresh) {
|
|
273
|
+
let $image = $(refresh);
|
|
274
|
+
$image.css({
|
|
275
|
+
'background-image': 'url(' + icon + ')'
|
|
276
|
+
});
|
|
277
|
+
$image.find('img').attr('src', icon);
|
|
278
|
+
$image.removeClass('loading');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
delete(e) {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
e.stopPropagation();
|
|
284
|
+
Alerts.confirm({
|
|
285
|
+
html: 'This file will be permanently deleted, are you sure?'
|
|
286
|
+
}, function (result) {
|
|
287
|
+
if (result.isConfirmed) {
|
|
288
|
+
Inline.post(e.currentTarget.href, e.currentTarget, function (response) {
|
|
289
|
+
Response.process(response, 5000);
|
|
290
|
+
this.media.reload();
|
|
291
|
+
if (this.currentBlade) {
|
|
292
|
+
this.currentBlade.close();
|
|
293
|
+
}
|
|
294
|
+
}.bind(this));
|
|
295
|
+
}
|
|
296
|
+
}.bind(this));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
export class MediaModal {
|
|
300
|
+
constructor() {
|
|
301
|
+
$('body').on('click', '[data-hood-media=attach],[data-hood-media=select],[data-hood-media=gallery]', this.load.bind(this));
|
|
302
|
+
$('body').on('click', '[data-hood-media=clear]', this.clear.bind(this));
|
|
303
|
+
$('[data-hood-media=gallery]').each(this.initGallery.bind(this));
|
|
304
|
+
}
|
|
305
|
+
initGallery(index, element) {
|
|
306
|
+
// setup the gallery list also, just a simple list jobby, and attach it to the
|
|
307
|
+
let el = document.getElementById(element.dataset.hoodMediaTarget);
|
|
308
|
+
if (el) {
|
|
309
|
+
new DataList(el, {
|
|
310
|
+
onComplete: function (data, sender = null) {
|
|
311
|
+
Alerts.log('Finished loading gallery media list.', 'info');
|
|
312
|
+
}.bind(this)
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
load(e) {
|
|
317
|
+
e.preventDefault();
|
|
318
|
+
e.stopPropagation();
|
|
319
|
+
this.element = e.currentTarget;
|
|
320
|
+
if (this.modal && this.modal.isOpen) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
this.modal = new ModalController({
|
|
324
|
+
onComplete: function (sender) {
|
|
325
|
+
this.list = document.getElementById('media-list');
|
|
326
|
+
this.service = new MediaService(this.list, {
|
|
327
|
+
action: this.element.dataset.hoodMedia,
|
|
328
|
+
url: this.element.dataset.hoodMediaUrl,
|
|
329
|
+
refresh: this.element.dataset.hoodMediaRefresh,
|
|
330
|
+
target: this.element.dataset.hoodMediaTarget,
|
|
331
|
+
size: this.element.dataset.hoodMediaSize,
|
|
332
|
+
beforeAction: function (sender, mediaObject) {
|
|
333
|
+
}.bind(this),
|
|
334
|
+
onAction: function (sender, mediaObject) {
|
|
335
|
+
this.modal.close();
|
|
336
|
+
}.bind(this),
|
|
337
|
+
onListLoad: (sender) => {
|
|
338
|
+
},
|
|
339
|
+
onListRender: (data) => {
|
|
340
|
+
return data;
|
|
341
|
+
},
|
|
342
|
+
onListComplete: (data) => {
|
|
343
|
+
},
|
|
344
|
+
onError: (jqXHR, textStatus, errorThrown) => {
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
}.bind(this)
|
|
348
|
+
});
|
|
349
|
+
this.modal.show($(e.currentTarget).data('hood-media-list'), e.currentTarget);
|
|
350
|
+
}
|
|
351
|
+
clear(e) {
|
|
352
|
+
e.preventDefault();
|
|
353
|
+
e.stopPropagation();
|
|
354
|
+
Alerts.confirm({}, function (result) {
|
|
355
|
+
if (result.isConfirmed) {
|
|
356
|
+
Inline.post(e.currentTarget.href, e.currentTarget, function (response) {
|
|
357
|
+
Response.process(response, 5000);
|
|
358
|
+
MediaService.refresh(response.media, e.currentTarget.dataset.hoodMediaRefresh);
|
|
359
|
+
}.bind(this));
|
|
360
|
+
}
|
|
361
|
+
}.bind(this));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Modal } from "bootstrap";
|
|
2
|
+
export interface ModalOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Called before the data is fetched.
|
|
5
|
+
*/
|
|
6
|
+
onLoad?: (sender: HTMLElement) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Called before the fetched HTML is rendered to the list. Must return the data back to datalist to render.
|
|
9
|
+
*/
|
|
10
|
+
onRender?: (sender: HTMLElement, html: string) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Called when loading and rendering is complete.
|
|
13
|
+
*/
|
|
14
|
+
onComplete?: (sender: HTMLElement) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Called when loading and rendering is complete.
|
|
17
|
+
*/
|
|
18
|
+
onHidden?: (sender: HTMLElement) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Called when loading and rendering is complete.
|
|
21
|
+
*/
|
|
22
|
+
onError?: (jqXHR: any, textStatus: any, errorThrown: any) => void;
|
|
23
|
+
closePrevious?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare class ModalController {
|
|
26
|
+
element: HTMLElement;
|
|
27
|
+
modal: Modal;
|
|
28
|
+
isOpen: boolean;
|
|
29
|
+
options: ModalOptions;
|
|
30
|
+
constructor(options?: ModalOptions);
|
|
31
|
+
show(url: string | URL, sender?: HTMLElement): void;
|
|
32
|
+
close(): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
createElementFromHTML(htmlString: string): HTMLElement;
|
|
35
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Modal } from "bootstrap";
|
|
2
|
+
import { Inline } from "./Inline";
|
|
3
|
+
export class ModalController {
|
|
4
|
+
constructor(options = null) {
|
|
5
|
+
this.isOpen = false;
|
|
6
|
+
this.options = {
|
|
7
|
+
closePrevious: true
|
|
8
|
+
};
|
|
9
|
+
this.options = Object.assign(Object.assign({}, this.options), options);
|
|
10
|
+
}
|
|
11
|
+
show(url, sender = null) {
|
|
12
|
+
if (this.options.onLoad) {
|
|
13
|
+
this.options.onLoad(this.element);
|
|
14
|
+
}
|
|
15
|
+
if (this.isOpen) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
this.isOpen = true;
|
|
19
|
+
$.get(url, function (data) {
|
|
20
|
+
if (this.modal && this.options.closePrevious) {
|
|
21
|
+
this.close();
|
|
22
|
+
}
|
|
23
|
+
if (this.options.onRender) {
|
|
24
|
+
data = this.options.onRender(this.element, data);
|
|
25
|
+
}
|
|
26
|
+
this.element = this.createElementFromHTML(data);
|
|
27
|
+
this.element.classList.add('hood-inline-modal');
|
|
28
|
+
$('body').append(this.element);
|
|
29
|
+
this.modal = new Modal(this.element, {});
|
|
30
|
+
this.modal.show();
|
|
31
|
+
// Workaround for sweetalert popups.
|
|
32
|
+
this.element.addEventListener('shown.bs.modal', function () {
|
|
33
|
+
$(document).off('focusin.modal');
|
|
34
|
+
}.bind(this));
|
|
35
|
+
this.element.addEventListener('hidden.bs.modal', function () {
|
|
36
|
+
if (this.options.onHidden) {
|
|
37
|
+
this.options.onHidden(this.element);
|
|
38
|
+
}
|
|
39
|
+
this.dispose();
|
|
40
|
+
this.isOpen = false;
|
|
41
|
+
}.bind(this));
|
|
42
|
+
if (this.options.onComplete) {
|
|
43
|
+
this.options.onComplete(this.element);
|
|
44
|
+
}
|
|
45
|
+
}.bind(this))
|
|
46
|
+
.fail(function (xhr, textStatus, errorThrown) {
|
|
47
|
+
this.isOpen = false;
|
|
48
|
+
if (this.options.onError) {
|
|
49
|
+
this.options.onError(xhr, textStatus, errorThrown);
|
|
50
|
+
}
|
|
51
|
+
Inline.handleError(xhr, textStatus, errorThrown);
|
|
52
|
+
}.bind(this));
|
|
53
|
+
}
|
|
54
|
+
close() {
|
|
55
|
+
if (this.modal) {
|
|
56
|
+
this.modal.hide();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
dispose() {
|
|
60
|
+
this.modal.dispose();
|
|
61
|
+
this.element.remove();
|
|
62
|
+
}
|
|
63
|
+
createElementFromHTML(htmlString) {
|
|
64
|
+
var div = document.createElement('div');
|
|
65
|
+
div.innerHTML = htmlString.trim();
|
|
66
|
+
// Change this to div.childNodes to support multiple top-level nodes
|
|
67
|
+
return div.firstChild;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare interface RandomStringGeneratorOptions {
|
|
2
|
+
specials?: string;
|
|
3
|
+
alpha?: string;
|
|
4
|
+
numeric?: string;
|
|
5
|
+
numSpecial?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class RandomStringGenerator {
|
|
8
|
+
options: RandomStringGeneratorOptions;
|
|
9
|
+
constructor(options?: RandomStringGeneratorOptions);
|
|
10
|
+
generate(length: number): string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class RandomStringGenerator {
|
|
2
|
+
constructor(options) {
|
|
3
|
+
this.options = {
|
|
4
|
+
specials: '!@#$&*',
|
|
5
|
+
alpha: 'abcdefghijklmnopqrstuvwxyz',
|
|
6
|
+
numeric: '0123456789',
|
|
7
|
+
numSpecial: 2
|
|
8
|
+
};
|
|
9
|
+
this.options = Object.assign(Object.assign({}, this.options), options);
|
|
10
|
+
}
|
|
11
|
+
generate(length) {
|
|
12
|
+
let password = '';
|
|
13
|
+
let len = Math.ceil((length - this.options.numSpecial) / 2);
|
|
14
|
+
for (let i = 0; i < len; i++) {
|
|
15
|
+
password += this.options.alpha.charAt(Math.floor(Math.random() * this.options.alpha.length));
|
|
16
|
+
password += this.options.numeric.charAt(Math.floor(Math.random() * this.options.numeric.length));
|
|
17
|
+
}
|
|
18
|
+
for (let j = 0; j < this.options.numSpecial; j++)
|
|
19
|
+
password += this.options.specials.charAt(Math.floor(Math.random() * this.options.specials.length));
|
|
20
|
+
password = password.split('').sort(function () { return 0.5 - Math.random(); }).join('');
|
|
21
|
+
return password;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { KeyValue } from "../interfaces/KeyValue";
|
|
2
|
+
import { MediaObject } from "./Media";
|
|
3
|
+
export declare interface Response {
|
|
4
|
+
data: any[];
|
|
5
|
+
count: number;
|
|
6
|
+
url: string;
|
|
7
|
+
title: string;
|
|
8
|
+
message: string;
|
|
9
|
+
success: boolean;
|
|
10
|
+
exception: KeyValue<string, string>[];
|
|
11
|
+
errors: string;
|
|
12
|
+
error: string;
|
|
13
|
+
media: MediaObject;
|
|
14
|
+
mediaJson: string;
|
|
15
|
+
process(autoHide: number): void;
|
|
16
|
+
}
|
|
17
|
+
export declare class Response {
|
|
18
|
+
constructor();
|
|
19
|
+
static process(data: Response | any, autoHide?: number): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Alerts } from "./Alerts";
|
|
2
|
+
export class Response {
|
|
3
|
+
constructor() {
|
|
4
|
+
}
|
|
5
|
+
static process(data, autoHide = 5000) {
|
|
6
|
+
if (data.success) {
|
|
7
|
+
Alerts.success(data.message, data.title, autoHide);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
Alerts.error(data.errors, data.title, autoHide);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Alerts } from './Alerts';
|
|
2
|
+
export class Uploader {
|
|
3
|
+
constructor() {
|
|
4
|
+
if ($('.image-uploader').length || $('.gallery-uploader').length) {
|
|
5
|
+
$(".upload-progress-bar").hide();
|
|
6
|
+
$('.image-uploader').each(this.singleImage);
|
|
7
|
+
$('.gallery-uploader').each(this.gallery);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
refreshImage(sender, data) {
|
|
11
|
+
$(sender.data('preview')).css({
|
|
12
|
+
'background-image': 'url(' + data.media.smallUrl + ')'
|
|
13
|
+
});
|
|
14
|
+
$(sender.data('preview')).find('img').attr('src', data.media.smallUrl);
|
|
15
|
+
}
|
|
16
|
+
singleImage() {
|
|
17
|
+
let tag = '#' + $(this).attr('id');
|
|
18
|
+
let $tag = $(tag);
|
|
19
|
+
let jsontag = '#' + $(this).attr('json');
|
|
20
|
+
let avatarDropzone = null;
|
|
21
|
+
$tag.dropzone({
|
|
22
|
+
url: $tag.data('url'),
|
|
23
|
+
maxFiles: 1,
|
|
24
|
+
paramName: 'file',
|
|
25
|
+
parallelUploads: 1,
|
|
26
|
+
acceptedFiles: $tag.data('types') || ".png,.jpg,.jpeg,.gif",
|
|
27
|
+
autoProcessQueue: true,
|
|
28
|
+
previewsContainer: false,
|
|
29
|
+
clickable: tag,
|
|
30
|
+
init: function () {
|
|
31
|
+
avatarDropzone = this;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
avatarDropzone.on("addedfile", function () {
|
|
35
|
+
});
|
|
36
|
+
avatarDropzone.on("totaluploadprogress", function (progress) {
|
|
37
|
+
$(".upload-progress-bar." + tag.replace('#', '') + " .progress-bar").css({ width: progress + "%" });
|
|
38
|
+
});
|
|
39
|
+
avatarDropzone.on("sending", function (file) {
|
|
40
|
+
$(".upload-progress-bar." + tag.replace('#', '')).show();
|
|
41
|
+
$($tag.data('preview')).addClass('loading');
|
|
42
|
+
});
|
|
43
|
+
avatarDropzone.on("queuecomplete", function (progress) {
|
|
44
|
+
$(".upload-progress-bar." + tag.replace('#', '')).hide();
|
|
45
|
+
});
|
|
46
|
+
avatarDropzone.on("success", function (file, response) {
|
|
47
|
+
if (response.success) {
|
|
48
|
+
if (response.media) {
|
|
49
|
+
$(jsontag).val(JSON.stringify(response.media));
|
|
50
|
+
$($tag.data('preview')).css({
|
|
51
|
+
'background-image': 'url(' + response.media.smallUrl + ')'
|
|
52
|
+
});
|
|
53
|
+
$($tag.data('preview')).find('img').attr('src', response.media.smallUrl);
|
|
54
|
+
}
|
|
55
|
+
Alerts.success("New image added!");
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
Alerts.error("There was a problem adding the image: " + response.error);
|
|
59
|
+
}
|
|
60
|
+
avatarDropzone.removeFile(file);
|
|
61
|
+
$($tag.data('preview')).removeClass('loading');
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
gallery() {
|
|
65
|
+
let tag = '#' + $(this).attr('id');
|
|
66
|
+
let $tag = $(tag);
|
|
67
|
+
let previewNode = document.querySelector(tag + "-template");
|
|
68
|
+
previewNode.id = "";
|
|
69
|
+
let previewTemplate = previewNode.parentNode.innerHTML;
|
|
70
|
+
previewNode.parentNode.removeChild(previewNode);
|
|
71
|
+
let galleryDropzone = null;
|
|
72
|
+
$tag.dropzone({
|
|
73
|
+
url: $tag.data('url'),
|
|
74
|
+
thumbnailWidth: 80,
|
|
75
|
+
thumbnailHeight: 80,
|
|
76
|
+
parallelUploads: 5,
|
|
77
|
+
previewTemplate: previewTemplate,
|
|
78
|
+
paramName: 'files',
|
|
79
|
+
acceptedFiles: $tag.data('types') || ".png,.jpg,.jpeg,.gif",
|
|
80
|
+
autoProcessQueue: true,
|
|
81
|
+
previewsContainer: "#previews",
|
|
82
|
+
clickable: ".fileinput-button",
|
|
83
|
+
init: function () {
|
|
84
|
+
galleryDropzone = this;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
$(tag + " .cancel").hide();
|
|
88
|
+
galleryDropzone.on("addedfile", function (file) {
|
|
89
|
+
$(file.previewElement.querySelector(".complete")).hide();
|
|
90
|
+
$(file.previewElement.querySelector(".cancel")).show();
|
|
91
|
+
$(tag + " .cancel").show();
|
|
92
|
+
});
|
|
93
|
+
// Update the total progress bar
|
|
94
|
+
galleryDropzone.on("totaluploadprogress", function (totalProgress, totalBytes, totalBytesSent) {
|
|
95
|
+
let progressBar = document.querySelector("#total-progress .progress-bar");
|
|
96
|
+
progressBar.style.width = totalProgress + "%";
|
|
97
|
+
});
|
|
98
|
+
galleryDropzone.on("sending", function (file) {
|
|
99
|
+
// Show the total progress bar when upload starts
|
|
100
|
+
let progressBar = document.querySelector("#total-progress");
|
|
101
|
+
progressBar.style.opacity = "1";
|
|
102
|
+
});
|
|
103
|
+
// Hide the total progress bar when nothing's uploading anymore
|
|
104
|
+
galleryDropzone.on("complete", function (file) {
|
|
105
|
+
$(file.previewElement.querySelector(".cancel")).hide();
|
|
106
|
+
$(file.previewElement.querySelector(".progress")).hide();
|
|
107
|
+
$(file.previewElement.querySelector(".complete")).show();
|
|
108
|
+
console.error("Uploader.Gallery.Dropzone.OnComplete - Inline.Refresh('.gallery') is not implemented.");
|
|
109
|
+
//Inline.Refresh('.gallery');
|
|
110
|
+
});
|
|
111
|
+
// Hide the total progress bar when nothing's uploading anymore
|
|
112
|
+
galleryDropzone.on("queuecomplete", function (progress) {
|
|
113
|
+
let totalProgress = document.querySelector("#total-progress");
|
|
114
|
+
totalProgress.style.opacity = "0";
|
|
115
|
+
$(tag + " .cancel").hide();
|
|
116
|
+
});
|
|
117
|
+
galleryDropzone.on("success", function (file, response) {
|
|
118
|
+
console.error("Uploader.Gallery.Dropzone.OnSuccess - Inline.Refresh('.gallery') is not implemented.");
|
|
119
|
+
//Inline.Refresh('.gallery');
|
|
120
|
+
if (response.success) {
|
|
121
|
+
Alerts.success("New images added!");
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
Alerts.error("There was a problem adding the profile image: " + response.error);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// Setup the buttons for all transfers
|
|
128
|
+
// The "add files" button doesn't need to be setup because the config
|
|
129
|
+
// `clickable` has already been specified.
|
|
130
|
+
$(".actions .cancel").click(function () {
|
|
131
|
+
galleryDropzone.removeAllFiles(true);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|