@sumaris-net/ngx-components 21.4.0 → 21.4.1
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/doc/changelog.md +3 -0
- package/fesm2022/sumaris-net.ngx-components.mjs +586 -510
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/assets/i18n/en.json +8 -8
- package/src/assets/i18n/fr.json +8 -8
- package/src/assets/manifest.json +1 -1
- package/types/sumaris-net.ngx-components.d.ts +448 -399
|
@@ -33202,268 +33202,137 @@ class CsvUtils {
|
|
|
33202
33202
|
}
|
|
33203
33203
|
}
|
|
33204
33204
|
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
|
|
33208
|
-
|
|
33209
|
-
|
|
33210
|
-
|
|
33211
|
-
|
|
33212
|
-
|
|
33213
|
-
|
|
33214
|
-
|
|
33215
|
-
medium: [152, 154, 162], // ok
|
|
33216
|
-
dark: [34, 36, 40], // ok
|
|
33217
|
-
red: [255, 0, 0],
|
|
33218
|
-
green: [0, 255, 0],
|
|
33219
|
-
blue: [0, 0, 255],
|
|
33220
|
-
};
|
|
33221
|
-
// Fill a map of Color objects
|
|
33222
|
-
const colorsMap = {};
|
|
33223
|
-
/**
|
|
33224
|
-
* Useful class for color conversion
|
|
33225
|
-
*/
|
|
33226
|
-
// @dynamic
|
|
33227
|
-
class Color {
|
|
33228
|
-
_rgbArray;
|
|
33229
|
-
_opacity;
|
|
33230
|
-
_name;
|
|
33231
|
-
// Helper method, to retrieve a color
|
|
33232
|
-
static get(name) {
|
|
33233
|
-
return colorsMap[name];
|
|
33234
|
-
}
|
|
33235
|
-
static parseRgba(rgba) {
|
|
33236
|
-
if (!rgba || (!rgba.startsWith('rgb(') && !rgba.startsWith('rgba(')))
|
|
33237
|
-
return null;
|
|
33238
|
-
// Parse parts
|
|
33239
|
-
const parts = rgba.replace('rgb(', '').replace('rgba(', '').replace(')', '').split(',');
|
|
33240
|
-
if (parts.length !== 3 && parts.length !== 4)
|
|
33241
|
-
return null;
|
|
33242
|
-
return new Color([+parts[0], +parts[1], +parts[2]], (parts.length === 4 && +parts[3]) || 1, 'custom');
|
|
33243
|
-
}
|
|
33244
|
-
static transparent = function () {
|
|
33245
|
-
return new Color([0, 0, 0], 0, 'translucent');
|
|
33246
|
-
};
|
|
33247
|
-
constructor(_rgbArray, _opacity, _name = 'custom') {
|
|
33248
|
-
this._rgbArray = _rgbArray;
|
|
33249
|
-
this._opacity = _opacity;
|
|
33250
|
-
this._name = _name;
|
|
33251
|
-
}
|
|
33252
|
-
get name() {
|
|
33253
|
-
return this._name;
|
|
33254
|
-
}
|
|
33255
|
-
get opacity() {
|
|
33256
|
-
return isNotNil(this._opacity) ? this._opacity : 1;
|
|
33257
|
-
}
|
|
33258
|
-
get rgb() {
|
|
33259
|
-
return this._rgbArray;
|
|
33260
|
-
}
|
|
33261
|
-
get r() {
|
|
33262
|
-
return this._rgbArray[0];
|
|
33263
|
-
}
|
|
33264
|
-
get g() {
|
|
33265
|
-
return this._rgbArray[1];
|
|
33266
|
-
}
|
|
33267
|
-
get b() {
|
|
33268
|
-
return this._rgbArray[2];
|
|
33205
|
+
// GPL v3 License
|
|
33206
|
+
class FileAttachmentUtils {
|
|
33207
|
+
/**
|
|
33208
|
+
* Get the file name of an attachment (its name, or the last part of its URL)
|
|
33209
|
+
*/
|
|
33210
|
+
static getFilename(attachment) {
|
|
33211
|
+
if (attachment?.name?.trim())
|
|
33212
|
+
return attachment.name.trim();
|
|
33213
|
+
const path = attachment?.url?.split('?')[0];
|
|
33214
|
+
return path?.substring(path.lastIndexOf('/') + 1) || 'attachment';
|
|
33269
33215
|
}
|
|
33270
|
-
|
|
33271
|
-
|
|
33272
|
-
|
|
33273
|
-
|
|
33274
|
-
}
|
|
33275
|
-
return 'rgba(' + this._rgbArray.join(',') + ',' + opacity + ')';
|
|
33216
|
+
/**
|
|
33217
|
+
* Get the Material icon (font ligature) of an attachment
|
|
33218
|
+
*/
|
|
33219
|
+
static getIcon(attachment) {
|
|
33220
|
+
return FileTypeUtils.getIconByFile({ name: FileAttachmentUtils.getFilename(attachment), type: attachment?.mimeType });
|
|
33276
33221
|
}
|
|
33277
33222
|
}
|
|
33223
|
+
|
|
33224
|
+
// GPL v3 License
|
|
33225
|
+
/** Kinds of file that can be previewed (displayed without being downloaded) */
|
|
33226
|
+
const PREVIEW_KINDS = Object.freeze(['image', 'text', 'code']);
|
|
33227
|
+
/** Extra MIME types (or extensions) that can be previewed as text */
|
|
33228
|
+
const PREVIEW_TEXT_MIME_TYPES = /^(text\/(csv|tab-separated-values|markdown|x-markdown)|application\/csv)$/;
|
|
33229
|
+
const PREVIEW_TEXT_EXTENSIONS = Object.freeze(['csv', 'tsv', 'md', 'markdown']);
|
|
33230
|
+
const MARKDOWN_MIME_TYPES = /^text\/(x-)?markdown$/;
|
|
33231
|
+
const MARKDOWN_EXTENSIONS = Object.freeze(['md', 'markdown']);
|
|
33232
|
+
const HTML_MIME_TYPES = /^(text\/html|application\/xhtml\+xml)$/;
|
|
33233
|
+
const HTML_EXTENSIONS = Object.freeze(['html', 'htm', 'xhtml']);
|
|
33278
33234
|
/**
|
|
33279
|
-
*
|
|
33235
|
+
* Download (or read) a remote file, sending the application headers (e.g. authorization) when the file is on the current peer.
|
|
33236
|
+
*
|
|
33237
|
+
* - On Android (Cordova downloader): the file is downloaded by the native download manager (visible in the "Downloads" app);
|
|
33238
|
+
* - Otherwise (web, iOS): the file is fetched as a Blob, then saved using a temporary object URL.
|
|
33280
33239
|
*/
|
|
33281
|
-
|
|
33282
|
-
|
|
33283
|
-
|
|
33284
|
-
options = options || {};
|
|
33285
|
-
return new ColorScale(linearColorGradientWithIntermediate(count, {
|
|
33286
|
-
startColor: options.startColor || undefined,
|
|
33287
|
-
mainColor: options.mainColor || undefined,
|
|
33288
|
-
mainColorIndex: isNotNil(options.mainColorIndex) ? options.mainColorIndex : undefined,
|
|
33289
|
-
endColor: options.endColor || undefined,
|
|
33290
|
-
}), options);
|
|
33291
|
-
};
|
|
33292
|
-
static default() {
|
|
33293
|
-
return ColorScale.custom(25);
|
|
33294
|
-
}
|
|
33240
|
+
class FileDownloadService {
|
|
33241
|
+
network = inject(NetworkService);
|
|
33242
|
+
platform = inject(PlatformService);
|
|
33295
33243
|
/**
|
|
33296
|
-
*
|
|
33297
|
-
|
|
33298
|
-
|
|
33299
|
-
|
|
33300
|
-
return new Array(length || 25).fill(color);
|
|
33301
|
-
}
|
|
33302
|
-
_min;
|
|
33303
|
-
_max;
|
|
33304
|
-
_rangeSize;
|
|
33305
|
-
_legendItems;
|
|
33306
|
-
get min() {
|
|
33307
|
-
return this._min;
|
|
33308
|
-
}
|
|
33309
|
-
get max() {
|
|
33310
|
-
return this._max;
|
|
33244
|
+
* Can the file be previewed (displayed without being downloaded)?
|
|
33245
|
+
*/
|
|
33246
|
+
canPreview(file) {
|
|
33247
|
+
return this.isImage(file) || this.isText(file);
|
|
33311
33248
|
}
|
|
33312
|
-
|
|
33313
|
-
|
|
33314
|
-
// reserved last colors for value > max
|
|
33315
|
-
const nbIntervalBeforeUpper = !options.upperMax ? colorArray.length : colorArray.length - 1;
|
|
33316
|
-
this._min = options.min || 0;
|
|
33317
|
-
this._max = options.max || nbIntervalBeforeUpper;
|
|
33318
|
-
this._rangeSize = Math.round((this._max - this._min) / nbIntervalBeforeUpper);
|
|
33319
|
-
this._legendItems = this.computeLegendItems(colorArray);
|
|
33249
|
+
isImage(file) {
|
|
33250
|
+
return FileTypeUtils.getKindByFile({ name: file?.name, type: file?.mimeType }) === 'image';
|
|
33320
33251
|
}
|
|
33321
|
-
|
|
33322
|
-
|
|
33323
|
-
|
|
33324
|
-
|
|
33252
|
+
isText(file) {
|
|
33253
|
+
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
33254
|
+
if (mimeType && PREVIEW_TEXT_MIME_TYPES.test(mimeType))
|
|
33255
|
+
return true;
|
|
33256
|
+
if (PREVIEW_TEXT_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name)))
|
|
33257
|
+
return true;
|
|
33258
|
+
const kind = FileTypeUtils.getKindByFile({ name: file?.name, type: mimeType });
|
|
33259
|
+
return kind !== 'image' && PREVIEW_KINDS.includes(kind);
|
|
33325
33260
|
}
|
|
33326
|
-
|
|
33327
|
-
|
|
33261
|
+
isMarkdown(file) {
|
|
33262
|
+
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
33263
|
+
return (mimeType && MARKDOWN_MIME_TYPES.test(mimeType)) || MARKDOWN_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
33328
33264
|
}
|
|
33329
|
-
|
|
33330
|
-
|
|
33331
|
-
|
|
33332
|
-
if (value >= this._max)
|
|
33333
|
-
return this._legendItems[this._legendItems.length - 1];
|
|
33334
|
-
const index = Math.floor((value * (this._legendItems.length - 1)) / this._max);
|
|
33335
|
-
return this._legendItems[index];
|
|
33265
|
+
isHtml(file) {
|
|
33266
|
+
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
33267
|
+
return (mimeType && HTML_MIME_TYPES.test(mimeType)) || HTML_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
33336
33268
|
}
|
|
33337
|
-
|
|
33338
|
-
|
|
33269
|
+
/**
|
|
33270
|
+
* Fetch a remote file, as a Blob
|
|
33271
|
+
*/
|
|
33272
|
+
getBlob(url) {
|
|
33273
|
+
return this.network.getBlob(FileDownloadService.normalizeUrl(url));
|
|
33339
33274
|
}
|
|
33340
|
-
|
|
33341
|
-
|
|
33342
|
-
|
|
33343
|
-
|
|
33344
|
-
|
|
33345
|
-
|
|
33346
|
-
|
|
33347
|
-
|
|
33348
|
-
});
|
|
33275
|
+
/**
|
|
33276
|
+
* Remove duplicated slashes in the path of an URL (e.g. 'http://host//api/file' => 'http://host/api/file').
|
|
33277
|
+
* Needed because the server (and its CORS rules) does not match a path with duplicated slashes.
|
|
33278
|
+
*/
|
|
33279
|
+
static normalizeUrl(url) {
|
|
33280
|
+
if (!url)
|
|
33281
|
+
return url;
|
|
33282
|
+
const [path, ...query] = url.split('?');
|
|
33283
|
+
const normalizedPath = path.replace(/([^:\/])\/{2,}/g, '$1/');
|
|
33284
|
+
return [normalizedPath, ...query].join('?');
|
|
33349
33285
|
}
|
|
33350
|
-
|
|
33351
|
-
|
|
33352
|
-
|
|
33353
|
-
|
|
33354
|
-
|
|
33355
|
-
|
|
33356
|
-
* Internal function, that create a colors scale, using iteration
|
|
33357
|
-
*
|
|
33358
|
-
* @param count
|
|
33359
|
-
* @param opacity
|
|
33360
|
-
* @param startColor
|
|
33361
|
-
* @param startState
|
|
33362
|
-
* @returns
|
|
33363
|
-
*/
|
|
33364
|
-
function linearColorGradientWithIntermediate(count, options) {
|
|
33365
|
-
options = options || {};
|
|
33366
|
-
// From [0,1]
|
|
33367
|
-
options.startColor = options.startColor || [255, 255, 190]; // default start = creme
|
|
33368
|
-
options.mainColorIndex = options.mainColorIndex && options.mainColorIndex < count - 1 ? options.mainColorIndex : count - 1;
|
|
33369
|
-
options.endColor = options.endColor || [255, 0, 0]; // default main = red
|
|
33370
|
-
if (!options.mainColor) {
|
|
33371
|
-
return linearColorGradient(count, {
|
|
33372
|
-
startColor: options.startColor,
|
|
33373
|
-
endColor: options.endColor,
|
|
33374
|
-
});
|
|
33286
|
+
/**
|
|
33287
|
+
* Fetch a remote file, as a text (e.g. to preview it)
|
|
33288
|
+
*/
|
|
33289
|
+
async getText(url) {
|
|
33290
|
+
const blob = await this.getBlob(url);
|
|
33291
|
+
return blob.text();
|
|
33375
33292
|
}
|
|
33376
|
-
|
|
33377
|
-
|
|
33378
|
-
|
|
33379
|
-
|
|
33380
|
-
|
|
33381
|
-
|
|
33382
|
-
|
|
33383
|
-
|
|
33384
|
-
|
|
33385
|
-
|
|
33386
|
-
|
|
33293
|
+
/**
|
|
33294
|
+
* Download a remote file
|
|
33295
|
+
*/
|
|
33296
|
+
async download(url, opts) {
|
|
33297
|
+
if (!url)
|
|
33298
|
+
throw new Error("Missing argument 'url'");
|
|
33299
|
+
url = FileDownloadService.normalizeUrl(url);
|
|
33300
|
+
const filename = opts?.filename || this.getFilename(url);
|
|
33301
|
+
// Android: use the native download manager
|
|
33302
|
+
if (this.platform.canDownload) {
|
|
33303
|
+
const headers = Object.entries(this.network.getAppHeaders(url)).map(([header, value]) => ({
|
|
33304
|
+
header,
|
|
33305
|
+
value: Array.isArray(value) ? value.join(', ') : value,
|
|
33387
33306
|
}));
|
|
33307
|
+
await firstValueFrom(this.platform.download({ uri: url, filename, title: filename, mimeType: opts?.mimeType, headers }));
|
|
33308
|
+
return;
|
|
33388
33309
|
}
|
|
33389
|
-
|
|
33390
|
-
|
|
33310
|
+
// Web, iOS: fetch as a Blob (with the authorization headers), then save it
|
|
33311
|
+
const blob = await this.getBlob(url);
|
|
33312
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
33313
|
+
try {
|
|
33314
|
+
FilesUtils.downloadUri(objectUrl, filename);
|
|
33391
33315
|
}
|
|
33392
|
-
|
|
33393
|
-
|
|
33394
|
-
|
|
33395
|
-
options = options || {};
|
|
33396
|
-
// From [0,1]
|
|
33397
|
-
options.startColor = options.startColor || [255, 255, 255]; // default start = white
|
|
33398
|
-
options.endColor = options.endColor || [255, 0, 0]; // default end = red
|
|
33399
|
-
const result = [];
|
|
33400
|
-
const color = options.startColor.slice(); // copy the start color
|
|
33401
|
-
const delta = [
|
|
33402
|
-
Math.round((options.endColor[0] - options.startColor[0]) / count),
|
|
33403
|
-
Math.round((options.endColor[1] - options.startColor[1]) / count),
|
|
33404
|
-
Math.round((options.endColor[2] - options.startColor[2]) / count),
|
|
33405
|
-
];
|
|
33406
|
-
for (let i = 0; i < count - 1; i++) {
|
|
33407
|
-
for (let j = 0; j < 3; j++) {
|
|
33408
|
-
color[j] += delta[j];
|
|
33316
|
+
finally {
|
|
33317
|
+
// Release the object URL, once the download has started
|
|
33318
|
+
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
|
|
33409
33319
|
}
|
|
33410
|
-
result.push(color.slice());
|
|
33411
|
-
}
|
|
33412
|
-
// Force last color = end color
|
|
33413
|
-
result.push(options.endColor.slice());
|
|
33414
|
-
// Output as array
|
|
33415
|
-
return result;
|
|
33416
|
-
}
|
|
33417
|
-
|
|
33418
|
-
class DebugComponent {
|
|
33419
|
-
titlePrefix = 'COMMON.DEBUG.TITLE';
|
|
33420
|
-
title = '';
|
|
33421
|
-
enable = null;
|
|
33422
|
-
expanded = false;
|
|
33423
|
-
expansionPanel;
|
|
33424
|
-
constructor(environment) {
|
|
33425
|
-
this.enable = !environment || !environment.production;
|
|
33426
33320
|
}
|
|
33427
|
-
|
|
33428
|
-
|
|
33321
|
+
/* -- protected methods -- */
|
|
33322
|
+
getFilename(url) {
|
|
33323
|
+
try {
|
|
33324
|
+
return FilesUtils.getFilenameFromUri(url.split('?')[0]) || 'download';
|
|
33325
|
+
}
|
|
33326
|
+
catch {
|
|
33327
|
+
return 'download';
|
|
33328
|
+
}
|
|
33429
33329
|
}
|
|
33430
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type:
|
|
33431
|
-
static
|
|
33432
|
-
}
|
|
33433
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: DebugComponent, decorators: [{
|
|
33434
|
-
type: Component,
|
|
33435
|
-
args: [{ selector: 'app-debug', standalone: false, template: "<!-- debug -->\n<ion-item tappable (click)=\"toggle()\" lines=\"none\" color=\"secondary50\">\n <ion-icon slot=\"start\" name=\"bug\"></ion-icon>\n <ion-label color=\"primary\">{{ titlePrefix | translate }} {{ title }}</ion-label>\n <mat-icon slot=\"end\">{{ expansionPanel.expanded ? 'expand_less' : 'expand_more' }}</mat-icon>\n</ion-item>\n<mat-expansion-panel #expansionPanel [(expanded)]=\"expanded\">\n <ion-text color=\"primary100\">\n <div class=\"ion-margin-start\">\n <ng-content></ng-content>\n </div>\n </ion-text>\n</mat-expansion-panel>\n", styles: ["ion-item mat-icon,ion-item ion-icon{color:var(--ion-color-primary)}ion-item{cursor:pointer!important}.mat-expansion-panel{--expansion-panel-padding: 4px;background:var(--ion-color-secondary50)!important}.mat-expansion-panel.mat-expanded{margin-bottom:4px!important}.mat-expansion-panel .mat-expansion-panel-body *{color:var(--ion-color-primary100)}\n"] }]
|
|
33436
|
-
}], ctorParameters: () => [{ type: Environment, decorators: [{
|
|
33437
|
-
type: Inject,
|
|
33438
|
-
args: [ENVIRONMENT]
|
|
33439
|
-
}, {
|
|
33440
|
-
type: Optional
|
|
33441
|
-
}] }], propDecorators: { titlePrefix: [{
|
|
33442
|
-
type: Input
|
|
33443
|
-
}], title: [{
|
|
33444
|
-
type: Input
|
|
33445
|
-
}], enable: [{
|
|
33446
|
-
type: Input
|
|
33447
|
-
}], expanded: [{
|
|
33448
|
-
type: Input
|
|
33449
|
-
}], expansionPanel: [{
|
|
33450
|
-
type: ViewChild,
|
|
33451
|
-
args: ['expansionPanel']
|
|
33452
|
-
}] } });
|
|
33453
|
-
|
|
33454
|
-
class SharedDebugModule {
|
|
33455
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
33456
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, declarations: [DebugComponent], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe], exports: [DebugComponent] });
|
|
33457
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, providers: [provideChildTranslateService()], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule] });
|
|
33330
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
33331
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, providedIn: 'root' });
|
|
33458
33332
|
}
|
|
33459
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type:
|
|
33460
|
-
type:
|
|
33461
|
-
args: [{
|
|
33462
|
-
imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe],
|
|
33463
|
-
declarations: [DebugComponent],
|
|
33464
|
-
exports: [DebugComponent],
|
|
33465
|
-
providers: [provideChildTranslateService()],
|
|
33466
|
-
}]
|
|
33333
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, decorators: [{
|
|
33334
|
+
type: Injectable,
|
|
33335
|
+
args: [{ providedIn: 'root' }]
|
|
33467
33336
|
}] });
|
|
33468
33337
|
|
|
33469
33338
|
class GitlabUtils {
|
|
@@ -33929,6 +33798,155 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
33929
33798
|
args: ['ready']
|
|
33930
33799
|
}] } });
|
|
33931
33800
|
|
|
33801
|
+
// GPL v3 License
|
|
33802
|
+
/**
|
|
33803
|
+
* Display the content of an attachment (text, markdown, image, HTML), without downloading it
|
|
33804
|
+
*/
|
|
33805
|
+
class FileAttachmentPreviewModal {
|
|
33806
|
+
cd = inject(ChangeDetectorRef);
|
|
33807
|
+
modalCtrl = inject(ModalController);
|
|
33808
|
+
fileDownload = inject(FileDownloadService);
|
|
33809
|
+
mobile = inject(LocalSettingsService).mobile;
|
|
33810
|
+
sanitizer = inject(DomSanitizer);
|
|
33811
|
+
elementRef = inject(ElementRef);
|
|
33812
|
+
attachment;
|
|
33813
|
+
loading = true;
|
|
33814
|
+
error;
|
|
33815
|
+
filename;
|
|
33816
|
+
text;
|
|
33817
|
+
markdown = false;
|
|
33818
|
+
imageUrl;
|
|
33819
|
+
/** HTML content, displayed in a sandboxed iframe (scripts allowed, but isolated from the application: no same origin) */
|
|
33820
|
+
html;
|
|
33821
|
+
/** Display the modal in a large size */
|
|
33822
|
+
maximized = false;
|
|
33823
|
+
async ngOnInit() {
|
|
33824
|
+
this.filename = FileAttachmentUtils.getFilename(this.attachment);
|
|
33825
|
+
const file = { name: this.filename, mimeType: this.attachment?.mimeType };
|
|
33826
|
+
try {
|
|
33827
|
+
if (this.fileDownload.isImage(file)) {
|
|
33828
|
+
const blob = await this.fileDownload.getBlob(this.attachment.url);
|
|
33829
|
+
this.imageUrl = URL.createObjectURL(blob);
|
|
33830
|
+
}
|
|
33831
|
+
else if (this.fileDownload.isHtml(file)) {
|
|
33832
|
+
const content = await this.fileDownload.getText(this.attachment.url);
|
|
33833
|
+
// Safe: the content is only rendered inside a sandboxed iframe (see template)
|
|
33834
|
+
this.html = this.sanitizer.bypassSecurityTrustHtml(content);
|
|
33835
|
+
}
|
|
33836
|
+
else {
|
|
33837
|
+
this.markdown = this.fileDownload.isMarkdown(file);
|
|
33838
|
+
this.text = await this.fileDownload.getText(this.attachment.url);
|
|
33839
|
+
}
|
|
33840
|
+
}
|
|
33841
|
+
catch (err) {
|
|
33842
|
+
console.error(`[file-attachment] Cannot load attachment {${this.attachment?.url}}`, err);
|
|
33843
|
+
this.error = 'FILE.ATTACHMENT.PREVIEW_ERROR';
|
|
33844
|
+
}
|
|
33845
|
+
finally {
|
|
33846
|
+
this.loading = false;
|
|
33847
|
+
this.cd.markForCheck();
|
|
33848
|
+
}
|
|
33849
|
+
}
|
|
33850
|
+
ngOnDestroy() {
|
|
33851
|
+
if (this.imageUrl)
|
|
33852
|
+
URL.revokeObjectURL(this.imageUrl);
|
|
33853
|
+
}
|
|
33854
|
+
async download() {
|
|
33855
|
+
await this.fileDownload.download(this.attachment.url, { filename: this.filename, mimeType: this.attachment.mimeType });
|
|
33856
|
+
}
|
|
33857
|
+
/** Display the modal in a large size (to restore the size: close then reopen the attachment) */
|
|
33858
|
+
maximize() {
|
|
33859
|
+
this.maximized = true;
|
|
33860
|
+
this.elementRef.nativeElement.closest('ion-modal')?.classList.add('modal-large');
|
|
33861
|
+
this.cd.markForCheck();
|
|
33862
|
+
}
|
|
33863
|
+
close() {
|
|
33864
|
+
return this.modalCtrl.dismiss();
|
|
33865
|
+
}
|
|
33866
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentPreviewModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33867
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FileAttachmentPreviewModal, isStandalone: false, selector: "app-file-attachment-preview-modal", inputs: { attachment: "attachment" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'FILE.ATTACHMENT.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33868
|
+
}
|
|
33869
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentPreviewModal, decorators: [{
|
|
33870
|
+
type: Component,
|
|
33871
|
+
args: [{ selector: 'app-file-attachment-preview-modal', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'FILE.ATTACHMENT.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"] }]
|
|
33872
|
+
}], propDecorators: { attachment: [{
|
|
33873
|
+
type: Input
|
|
33874
|
+
}] } });
|
|
33875
|
+
|
|
33876
|
+
// GPL v3 License
|
|
33877
|
+
/**
|
|
33878
|
+
* Display an attachment as a compact badge (chip): icon, file name, and size (in the tooltip).
|
|
33879
|
+
*
|
|
33880
|
+
* - Click on the badge: display the file content in a modal (text, markdown, image, HTML), or download it if not previewable;
|
|
33881
|
+
* - Download part (right side, visible on hover - always visible on mobile): download the file.
|
|
33882
|
+
*/
|
|
33883
|
+
class FileAttachmentComponent {
|
|
33884
|
+
modalCtrl = inject(ModalController);
|
|
33885
|
+
toastCtrl = inject(ToastController);
|
|
33886
|
+
translate = inject(TranslateService);
|
|
33887
|
+
fileDownload = inject(FileDownloadService);
|
|
33888
|
+
getFilename = FileAttachmentUtils.getFilename;
|
|
33889
|
+
getIcon = FileAttachmentUtils.getIcon;
|
|
33890
|
+
canPreview = (attachment) => this.fileDownload.canPreview({ name: FileAttachmentUtils.getFilename(attachment), mimeType: attachment?.mimeType });
|
|
33891
|
+
attachment;
|
|
33892
|
+
/** Bigger badge (touch), with the download part always visible. Default: the mobile local settings */
|
|
33893
|
+
mobile = inject(LocalSettingsService).mobile;
|
|
33894
|
+
/**
|
|
33895
|
+
* Open the attachment: display its content, if possible (text, markdown, image...), or download it
|
|
33896
|
+
*/
|
|
33897
|
+
async open(event) {
|
|
33898
|
+
event?.preventDefault();
|
|
33899
|
+
event?.stopPropagation();
|
|
33900
|
+
const attachment = this.attachment;
|
|
33901
|
+
if (!attachment?.url)
|
|
33902
|
+
return;
|
|
33903
|
+
if (!this.canPreview(attachment)) {
|
|
33904
|
+
return this.download(event);
|
|
33905
|
+
}
|
|
33906
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
33907
|
+
const modal = await this.modalCtrl.create({
|
|
33908
|
+
component: FileAttachmentPreviewModal,
|
|
33909
|
+
componentProps: { attachment },
|
|
33910
|
+
cssClass: hasTopModal ? 'stack-modal' : undefined,
|
|
33911
|
+
});
|
|
33912
|
+
await modal.present();
|
|
33913
|
+
}
|
|
33914
|
+
/**
|
|
33915
|
+
* Download the attachment (using the native download manager on Android)
|
|
33916
|
+
*/
|
|
33917
|
+
async download(event) {
|
|
33918
|
+
event?.preventDefault();
|
|
33919
|
+
event?.stopPropagation();
|
|
33920
|
+
const attachment = this.attachment;
|
|
33921
|
+
if (!attachment?.url)
|
|
33922
|
+
return;
|
|
33923
|
+
try {
|
|
33924
|
+
await this.fileDownload.download(attachment.url, {
|
|
33925
|
+
filename: FileAttachmentUtils.getFilename(attachment),
|
|
33926
|
+
mimeType: attachment.mimeType,
|
|
33927
|
+
});
|
|
33928
|
+
}
|
|
33929
|
+
catch (err) {
|
|
33930
|
+
console.error(`[file-attachment] Cannot download attachment {${attachment.url}}`, err);
|
|
33931
|
+
await Toasts.show(this.toastCtrl, this.translate, { type: 'error', message: 'FILE.ATTACHMENT.DOWNLOAD_ERROR' });
|
|
33932
|
+
}
|
|
33933
|
+
}
|
|
33934
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33935
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FileAttachmentComponent, isStandalone: false, selector: "app-file-attachment", inputs: { attachment: "attachment", mobile: "mobile" }, host: { properties: { "class.mobile": "this.mobile" } }, ngImport: i0, template: "@let filename = attachment | map: getFilename;\n@let previewable = attachment | map: canPreview;\n@let action = (previewable ? 'FILE.ATTACHMENT.BTN_PREVIEW' : 'FILE.ATTACHMENT.BTN_DOWNLOAD') | translate;\n<ion-chip\n class=\"file-attachment\"\n [class.has-download]=\"previewable\"\n [outline]=\"true\"\n [title]=\"filename + (attachment?.size ? ' (' + (attachment.size | fileSize) + ')' : '') + ' - ' + action\"\n (click)=\"open($event)\"\n tappable\n>\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (previewable) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span class=\"attachment-download\" role=\"button\" [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\" (click)=\"download($event)\">\n <mat-icon>download</mat-icon>\n </span>\n }\n</ion-chip>\n", styles: [":host{display:inline-flex;max-width:100%;--file-attachment-height: 22px;--file-attachment-icon-size: 14px;--file-attachment-padding: 6px;--file-attachment-font-size: 12px}:host(.mobile){--file-attachment-height: 32px;--file-attachment-icon-size: 18px;--file-attachment-padding: 10px;--file-attachment-font-size: 14px}.file-attachment{max-width:100%;height:var(--file-attachment-height);min-height:var(--file-attachment-height);margin:0;padding:0 var(--file-attachment-padding);font-size:var(--file-attachment-font-size);border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.file-attachment mat-icon{flex-shrink:0;width:var(--file-attachment-icon-size);height:var(--file-attachment-icon-size);font-size:var(--file-attachment-icon-size);line-height:var(--file-attachment-icon-size)}.file-attachment .attachment-icon{margin-inline-end:4px}.file-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.file-attachment.has-download:hover{padding-inline-end:0}.file-attachment:hover .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}.file-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}:host(.mobile) .file-attachment.has-download{padding-inline-end:0}:host(.mobile) .file-attachment .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}\n"], dependencies: [{ kind: "component", type: i2$2.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }, { kind: "pipe", type: MapPipe, name: "map" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33936
|
+
}
|
|
33937
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentComponent, decorators: [{
|
|
33938
|
+
type: Component,
|
|
33939
|
+
args: [{ selector: 'app-file-attachment', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "@let filename = attachment | map: getFilename;\n@let previewable = attachment | map: canPreview;\n@let action = (previewable ? 'FILE.ATTACHMENT.BTN_PREVIEW' : 'FILE.ATTACHMENT.BTN_DOWNLOAD') | translate;\n<ion-chip\n class=\"file-attachment\"\n [class.has-download]=\"previewable\"\n [outline]=\"true\"\n [title]=\"filename + (attachment?.size ? ' (' + (attachment.size | fileSize) + ')' : '') + ' - ' + action\"\n (click)=\"open($event)\"\n tappable\n>\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (previewable) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span class=\"attachment-download\" role=\"button\" [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\" (click)=\"download($event)\">\n <mat-icon>download</mat-icon>\n </span>\n }\n</ion-chip>\n", styles: [":host{display:inline-flex;max-width:100%;--file-attachment-height: 22px;--file-attachment-icon-size: 14px;--file-attachment-padding: 6px;--file-attachment-font-size: 12px}:host(.mobile){--file-attachment-height: 32px;--file-attachment-icon-size: 18px;--file-attachment-padding: 10px;--file-attachment-font-size: 14px}.file-attachment{max-width:100%;height:var(--file-attachment-height);min-height:var(--file-attachment-height);margin:0;padding:0 var(--file-attachment-padding);font-size:var(--file-attachment-font-size);border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.file-attachment mat-icon{flex-shrink:0;width:var(--file-attachment-icon-size);height:var(--file-attachment-icon-size);font-size:var(--file-attachment-icon-size);line-height:var(--file-attachment-icon-size)}.file-attachment .attachment-icon{margin-inline-end:4px}.file-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.file-attachment.has-download:hover{padding-inline-end:0}.file-attachment:hover .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}.file-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}:host(.mobile) .file-attachment.has-download{padding-inline-end:0}:host(.mobile) .file-attachment .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}\n"] }]
|
|
33940
|
+
}], propDecorators: { attachment: [{
|
|
33941
|
+
type: Input,
|
|
33942
|
+
args: [{ required: true }]
|
|
33943
|
+
}], mobile: [{
|
|
33944
|
+
type: HostBinding,
|
|
33945
|
+
args: ['class.mobile']
|
|
33946
|
+
}, {
|
|
33947
|
+
type: Input
|
|
33948
|
+
}] } });
|
|
33949
|
+
|
|
33932
33950
|
const PRINT_LOADING_STORAGE_KEY_PREFIX = 'print-loading-';
|
|
33933
33951
|
const PRINT_ID_QUERY_PARAM = 'print-id';
|
|
33934
33952
|
class PrintService {
|
|
@@ -34223,6 +34241,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34223
34241
|
args: [DOCUMENT]
|
|
34224
34242
|
}] }] });
|
|
34225
34243
|
|
|
34244
|
+
class DebugComponent {
|
|
34245
|
+
titlePrefix = 'COMMON.DEBUG.TITLE';
|
|
34246
|
+
title = '';
|
|
34247
|
+
enable = null;
|
|
34248
|
+
expanded = false;
|
|
34249
|
+
expansionPanel;
|
|
34250
|
+
constructor(environment) {
|
|
34251
|
+
this.enable = !environment || !environment.production;
|
|
34252
|
+
}
|
|
34253
|
+
toggle() {
|
|
34254
|
+
this.expansionPanel.toggle();
|
|
34255
|
+
}
|
|
34256
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: DebugComponent, deps: [{ token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
34257
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.22", type: DebugComponent, isStandalone: false, selector: "app-debug", inputs: { titlePrefix: "titlePrefix", title: "title", enable: "enable", expanded: "expanded" }, viewQueries: [{ propertyName: "expansionPanel", first: true, predicate: ["expansionPanel"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n<ion-item tappable (click)=\"toggle()\" lines=\"none\" color=\"secondary50\">\n <ion-icon slot=\"start\" name=\"bug\"></ion-icon>\n <ion-label color=\"primary\">{{ titlePrefix | translate }} {{ title }}</ion-label>\n <mat-icon slot=\"end\">{{ expansionPanel.expanded ? 'expand_less' : 'expand_more' }}</mat-icon>\n</ion-item>\n<mat-expansion-panel #expansionPanel [(expanded)]=\"expanded\">\n <ion-text color=\"primary100\">\n <div class=\"ion-margin-start\">\n <ng-content></ng-content>\n </div>\n </ion-text>\n</mat-expansion-panel>\n", styles: ["ion-item mat-icon,ion-item ion-icon{color:var(--ion-color-primary)}ion-item{cursor:pointer!important}.mat-expansion-panel{--expansion-panel-padding: 4px;background:var(--ion-color-secondary50)!important}.mat-expansion-panel.mat-expanded{margin-bottom:4px!important}.mat-expansion-panel .mat-expansion-panel-body *{color:var(--ion-color-primary100)}\n"], dependencies: [{ kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
|
|
34258
|
+
}
|
|
34259
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: DebugComponent, decorators: [{
|
|
34260
|
+
type: Component,
|
|
34261
|
+
args: [{ selector: 'app-debug', standalone: false, template: "<!-- debug -->\n<ion-item tappable (click)=\"toggle()\" lines=\"none\" color=\"secondary50\">\n <ion-icon slot=\"start\" name=\"bug\"></ion-icon>\n <ion-label color=\"primary\">{{ titlePrefix | translate }} {{ title }}</ion-label>\n <mat-icon slot=\"end\">{{ expansionPanel.expanded ? 'expand_less' : 'expand_more' }}</mat-icon>\n</ion-item>\n<mat-expansion-panel #expansionPanel [(expanded)]=\"expanded\">\n <ion-text color=\"primary100\">\n <div class=\"ion-margin-start\">\n <ng-content></ng-content>\n </div>\n </ion-text>\n</mat-expansion-panel>\n", styles: ["ion-item mat-icon,ion-item ion-icon{color:var(--ion-color-primary)}ion-item{cursor:pointer!important}.mat-expansion-panel{--expansion-panel-padding: 4px;background:var(--ion-color-secondary50)!important}.mat-expansion-panel.mat-expanded{margin-bottom:4px!important}.mat-expansion-panel .mat-expansion-panel-body *{color:var(--ion-color-primary100)}\n"] }]
|
|
34262
|
+
}], ctorParameters: () => [{ type: Environment, decorators: [{
|
|
34263
|
+
type: Inject,
|
|
34264
|
+
args: [ENVIRONMENT]
|
|
34265
|
+
}, {
|
|
34266
|
+
type: Optional
|
|
34267
|
+
}] }], propDecorators: { titlePrefix: [{
|
|
34268
|
+
type: Input
|
|
34269
|
+
}], title: [{
|
|
34270
|
+
type: Input
|
|
34271
|
+
}], enable: [{
|
|
34272
|
+
type: Input
|
|
34273
|
+
}], expanded: [{
|
|
34274
|
+
type: Input
|
|
34275
|
+
}], expansionPanel: [{
|
|
34276
|
+
type: ViewChild,
|
|
34277
|
+
args: ['expansionPanel']
|
|
34278
|
+
}] } });
|
|
34279
|
+
|
|
34226
34280
|
class AppMarkdownContent {
|
|
34227
34281
|
cd;
|
|
34228
34282
|
element;
|
|
@@ -34612,6 +34666,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34612
34666
|
args: ['content']
|
|
34613
34667
|
}] } });
|
|
34614
34668
|
|
|
34669
|
+
class SharedDebugModule {
|
|
34670
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
34671
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, declarations: [DebugComponent], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe], exports: [DebugComponent] });
|
|
34672
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, providers: [provideChildTranslateService()], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule] });
|
|
34673
|
+
}
|
|
34674
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, decorators: [{
|
|
34675
|
+
type: NgModule,
|
|
34676
|
+
args: [{
|
|
34677
|
+
imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe],
|
|
34678
|
+
declarations: [DebugComponent],
|
|
34679
|
+
exports: [DebugComponent],
|
|
34680
|
+
providers: [provideChildTranslateService()],
|
|
34681
|
+
}]
|
|
34682
|
+
}] });
|
|
34683
|
+
|
|
34615
34684
|
// function that returns `MarkedOptions` with renderer override
|
|
34616
34685
|
class SharedMarkdownModule {
|
|
34617
34686
|
static forRoot() {
|
|
@@ -34664,29 +34733,258 @@ class SharedMarkdownModule {
|
|
|
34664
34733
|
SharedToolbarModule,
|
|
34665
34734
|
SharedDebugModule, MarkdownModule] });
|
|
34666
34735
|
}
|
|
34667
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedMarkdownModule, decorators: [{
|
|
34668
|
-
type: NgModule,
|
|
34669
|
-
args: [{
|
|
34670
|
-
imports: [
|
|
34671
|
-
CommonModule,
|
|
34672
|
-
SharedIonicModule,
|
|
34673
|
-
TranslatePipe,
|
|
34674
|
-
MarkdownModule.forChild(),
|
|
34675
|
-
// App module
|
|
34676
|
-
SharedToolbarModule,
|
|
34677
|
-
SharedDebugModule,
|
|
34678
|
-
],
|
|
34679
|
-
declarations: [
|
|
34680
|
-
// Component
|
|
34681
|
-
AppMarkdownContent,
|
|
34682
|
-
AppMarkdownModal,
|
|
34683
|
-
// Directive
|
|
34684
|
-
MarkdownDirective,
|
|
34685
|
-
],
|
|
34686
|
-
exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective],
|
|
34687
|
-
providers: [provideChildTranslateService()],
|
|
34688
|
-
}]
|
|
34689
|
-
}] });
|
|
34736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedMarkdownModule, decorators: [{
|
|
34737
|
+
type: NgModule,
|
|
34738
|
+
args: [{
|
|
34739
|
+
imports: [
|
|
34740
|
+
CommonModule,
|
|
34741
|
+
SharedIonicModule,
|
|
34742
|
+
TranslatePipe,
|
|
34743
|
+
MarkdownModule.forChild(),
|
|
34744
|
+
// App module
|
|
34745
|
+
SharedToolbarModule,
|
|
34746
|
+
SharedDebugModule,
|
|
34747
|
+
],
|
|
34748
|
+
declarations: [
|
|
34749
|
+
// Component
|
|
34750
|
+
AppMarkdownContent,
|
|
34751
|
+
AppMarkdownModal,
|
|
34752
|
+
// Directive
|
|
34753
|
+
MarkdownDirective,
|
|
34754
|
+
],
|
|
34755
|
+
exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective],
|
|
34756
|
+
providers: [provideChildTranslateService()],
|
|
34757
|
+
}]
|
|
34758
|
+
}] });
|
|
34759
|
+
|
|
34760
|
+
// GPL v3 License
|
|
34761
|
+
class SharedFileAttachmentModule {
|
|
34762
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
34763
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, declarations: [FileAttachmentComponent, FileAttachmentPreviewModal], imports: [CommonModule, SharedIonicModule, SharedMaterialModule, SharedPipesModule, SharedMarkdownModule, TranslatePipe], exports: [FileAttachmentComponent, FileAttachmentPreviewModal] });
|
|
34764
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, providers: [provideChildTranslateService()], imports: [CommonModule, SharedIonicModule, SharedMaterialModule, SharedPipesModule, SharedMarkdownModule] });
|
|
34765
|
+
}
|
|
34766
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, decorators: [{
|
|
34767
|
+
type: NgModule,
|
|
34768
|
+
args: [{
|
|
34769
|
+
imports: [CommonModule, SharedIonicModule, SharedMaterialModule, SharedPipesModule, SharedMarkdownModule, TranslatePipe],
|
|
34770
|
+
declarations: [FileAttachmentComponent, FileAttachmentPreviewModal],
|
|
34771
|
+
exports: [FileAttachmentComponent, FileAttachmentPreviewModal],
|
|
34772
|
+
providers: [provideChildTranslateService()],
|
|
34773
|
+
}]
|
|
34774
|
+
}] });
|
|
34775
|
+
|
|
34776
|
+
/**
|
|
34777
|
+
* Define here theme colors
|
|
34778
|
+
*/
|
|
34779
|
+
const rgbArrayMap = {
|
|
34780
|
+
white: [255, 255, 255],
|
|
34781
|
+
primary: [20, 67, 145], // ok
|
|
34782
|
+
secondary: [117, 196, 253], // ok
|
|
34783
|
+
tertiary: [91, 94, 244], // ok
|
|
34784
|
+
danger: [245, 61, 61], // ok
|
|
34785
|
+
light: [244, 245, 248], // ok
|
|
34786
|
+
medium: [152, 154, 162], // ok
|
|
34787
|
+
dark: [34, 36, 40], // ok
|
|
34788
|
+
red: [255, 0, 0],
|
|
34789
|
+
green: [0, 255, 0],
|
|
34790
|
+
blue: [0, 0, 255],
|
|
34791
|
+
};
|
|
34792
|
+
// Fill a map of Color objects
|
|
34793
|
+
const colorsMap = {};
|
|
34794
|
+
/**
|
|
34795
|
+
* Useful class for color conversion
|
|
34796
|
+
*/
|
|
34797
|
+
// @dynamic
|
|
34798
|
+
class Color {
|
|
34799
|
+
_rgbArray;
|
|
34800
|
+
_opacity;
|
|
34801
|
+
_name;
|
|
34802
|
+
// Helper method, to retrieve a color
|
|
34803
|
+
static get(name) {
|
|
34804
|
+
return colorsMap[name];
|
|
34805
|
+
}
|
|
34806
|
+
static parseRgba(rgba) {
|
|
34807
|
+
if (!rgba || (!rgba.startsWith('rgb(') && !rgba.startsWith('rgba(')))
|
|
34808
|
+
return null;
|
|
34809
|
+
// Parse parts
|
|
34810
|
+
const parts = rgba.replace('rgb(', '').replace('rgba(', '').replace(')', '').split(',');
|
|
34811
|
+
if (parts.length !== 3 && parts.length !== 4)
|
|
34812
|
+
return null;
|
|
34813
|
+
return new Color([+parts[0], +parts[1], +parts[2]], (parts.length === 4 && +parts[3]) || 1, 'custom');
|
|
34814
|
+
}
|
|
34815
|
+
static transparent = function () {
|
|
34816
|
+
return new Color([0, 0, 0], 0, 'translucent');
|
|
34817
|
+
};
|
|
34818
|
+
constructor(_rgbArray, _opacity, _name = 'custom') {
|
|
34819
|
+
this._rgbArray = _rgbArray;
|
|
34820
|
+
this._opacity = _opacity;
|
|
34821
|
+
this._name = _name;
|
|
34822
|
+
}
|
|
34823
|
+
get name() {
|
|
34824
|
+
return this._name;
|
|
34825
|
+
}
|
|
34826
|
+
get opacity() {
|
|
34827
|
+
return isNotNil(this._opacity) ? this._opacity : 1;
|
|
34828
|
+
}
|
|
34829
|
+
get rgb() {
|
|
34830
|
+
return this._rgbArray;
|
|
34831
|
+
}
|
|
34832
|
+
get r() {
|
|
34833
|
+
return this._rgbArray[0];
|
|
34834
|
+
}
|
|
34835
|
+
get g() {
|
|
34836
|
+
return this._rgbArray[1];
|
|
34837
|
+
}
|
|
34838
|
+
get b() {
|
|
34839
|
+
return this._rgbArray[2];
|
|
34840
|
+
}
|
|
34841
|
+
rgba(opacity) {
|
|
34842
|
+
opacity = isNotNil(opacity) ? opacity : this._opacity;
|
|
34843
|
+
if (isNil(opacity) || opacity < 0 || opacity > 1) {
|
|
34844
|
+
return 'rgb(' + this._rgbArray.join(',') + ')';
|
|
34845
|
+
}
|
|
34846
|
+
return 'rgba(' + this._rgbArray.join(',') + ',' + opacity + ')';
|
|
34847
|
+
}
|
|
34848
|
+
}
|
|
34849
|
+
/**
|
|
34850
|
+
* Helper class for colors scale
|
|
34851
|
+
*/
|
|
34852
|
+
// @dynamic
|
|
34853
|
+
class ColorScale {
|
|
34854
|
+
static custom = (count, options) => {
|
|
34855
|
+
options = options || {};
|
|
34856
|
+
return new ColorScale(linearColorGradientWithIntermediate(count, {
|
|
34857
|
+
startColor: options.startColor || undefined,
|
|
34858
|
+
mainColor: options.mainColor || undefined,
|
|
34859
|
+
mainColorIndex: isNotNil(options.mainColorIndex) ? options.mainColorIndex : undefined,
|
|
34860
|
+
endColor: options.endColor || undefined,
|
|
34861
|
+
}), options);
|
|
34862
|
+
};
|
|
34863
|
+
static default() {
|
|
34864
|
+
return ColorScale.custom(25);
|
|
34865
|
+
}
|
|
34866
|
+
/**
|
|
34867
|
+
* Create an array with the given color
|
|
34868
|
+
**/
|
|
34869
|
+
static fix(length, colorName) {
|
|
34870
|
+
const color = Color.get(colorName || 'primary').rgba(0.5);
|
|
34871
|
+
return new Array(length || 25).fill(color);
|
|
34872
|
+
}
|
|
34873
|
+
_min;
|
|
34874
|
+
_max;
|
|
34875
|
+
_rangeSize;
|
|
34876
|
+
_legendItems;
|
|
34877
|
+
get min() {
|
|
34878
|
+
return this._min;
|
|
34879
|
+
}
|
|
34880
|
+
get max() {
|
|
34881
|
+
return this._max;
|
|
34882
|
+
}
|
|
34883
|
+
constructor(colorArray, options) {
|
|
34884
|
+
options = options || {};
|
|
34885
|
+
// reserved last colors for value > max
|
|
34886
|
+
const nbIntervalBeforeUpper = !options.upperMax ? colorArray.length : colorArray.length - 1;
|
|
34887
|
+
this._min = options.min || 0;
|
|
34888
|
+
this._max = options.max || nbIntervalBeforeUpper;
|
|
34889
|
+
this._rangeSize = Math.round((this._max - this._min) / nbIntervalBeforeUpper);
|
|
34890
|
+
this._legendItems = this.computeLegendItems(colorArray);
|
|
34891
|
+
}
|
|
34892
|
+
get legend() {
|
|
34893
|
+
return {
|
|
34894
|
+
items: this._legendItems,
|
|
34895
|
+
};
|
|
34896
|
+
}
|
|
34897
|
+
getValueColor(value) {
|
|
34898
|
+
return this.getLegendForValue(value).color;
|
|
34899
|
+
}
|
|
34900
|
+
getLegendForValue(value) {
|
|
34901
|
+
if (value <= this._min)
|
|
34902
|
+
return this._legendItems[0];
|
|
34903
|
+
if (value >= this._max)
|
|
34904
|
+
return this._legendItems[this._legendItems.length - 1];
|
|
34905
|
+
const index = Math.floor((value * (this._legendItems.length - 1)) / this._max);
|
|
34906
|
+
return this._legendItems[index];
|
|
34907
|
+
}
|
|
34908
|
+
getLegendAtIndex(index) {
|
|
34909
|
+
return this._legendItems[index];
|
|
34910
|
+
}
|
|
34911
|
+
computeLegendItems(colorArray) {
|
|
34912
|
+
return colorArray.map((color, index) => {
|
|
34913
|
+
const start = index * this._rangeSize;
|
|
34914
|
+
const end = start + this._rangeSize;
|
|
34915
|
+
return {
|
|
34916
|
+
color: new Color(color),
|
|
34917
|
+
label: end < this._max ? `${start.toLocaleString()} - ${end.toLocaleString()}` : ` >= ${start}`,
|
|
34918
|
+
};
|
|
34919
|
+
});
|
|
34920
|
+
}
|
|
34921
|
+
}
|
|
34922
|
+
// Fill colorsMap
|
|
34923
|
+
Object.getOwnPropertyNames(rgbArrayMap).forEach((key) => {
|
|
34924
|
+
colorsMap[key] = new Color(rgbArrayMap[key], 1, key);
|
|
34925
|
+
});
|
|
34926
|
+
/**
|
|
34927
|
+
* Internal function, that create a colors scale, using iteration
|
|
34928
|
+
*
|
|
34929
|
+
* @param count
|
|
34930
|
+
* @param opacity
|
|
34931
|
+
* @param startColor
|
|
34932
|
+
* @param startState
|
|
34933
|
+
* @returns
|
|
34934
|
+
*/
|
|
34935
|
+
function linearColorGradientWithIntermediate(count, options) {
|
|
34936
|
+
options = options || {};
|
|
34937
|
+
// From [0,1]
|
|
34938
|
+
options.startColor = options.startColor || [255, 255, 190]; // default start = creme
|
|
34939
|
+
options.mainColorIndex = options.mainColorIndex && options.mainColorIndex < count - 1 ? options.mainColorIndex : count - 1;
|
|
34940
|
+
options.endColor = options.endColor || [255, 0, 0]; // default main = red
|
|
34941
|
+
if (!options.mainColor) {
|
|
34942
|
+
return linearColorGradient(count, {
|
|
34943
|
+
startColor: options.startColor,
|
|
34944
|
+
endColor: options.endColor,
|
|
34945
|
+
});
|
|
34946
|
+
}
|
|
34947
|
+
else {
|
|
34948
|
+
// Step 1: startColor -> mainColor
|
|
34949
|
+
const result = linearColorGradient(options.mainColorIndex + 1, {
|
|
34950
|
+
startColor: options.startColor,
|
|
34951
|
+
endColor: options.mainColor,
|
|
34952
|
+
});
|
|
34953
|
+
// Step 2: mainColor -> endColor
|
|
34954
|
+
if (options.mainColorIndex < count - 1) {
|
|
34955
|
+
return result.concat(linearColorGradient(count - options.mainColorIndex, {
|
|
34956
|
+
startColor: options.mainColor,
|
|
34957
|
+
endColor: options.endColor,
|
|
34958
|
+
}));
|
|
34959
|
+
}
|
|
34960
|
+
else {
|
|
34961
|
+
return result;
|
|
34962
|
+
}
|
|
34963
|
+
}
|
|
34964
|
+
}
|
|
34965
|
+
function linearColorGradient(count, options) {
|
|
34966
|
+
options = options || {};
|
|
34967
|
+
// From [0,1]
|
|
34968
|
+
options.startColor = options.startColor || [255, 255, 255]; // default start = white
|
|
34969
|
+
options.endColor = options.endColor || [255, 0, 0]; // default end = red
|
|
34970
|
+
const result = [];
|
|
34971
|
+
const color = options.startColor.slice(); // copy the start color
|
|
34972
|
+
const delta = [
|
|
34973
|
+
Math.round((options.endColor[0] - options.startColor[0]) / count),
|
|
34974
|
+
Math.round((options.endColor[1] - options.startColor[1]) / count),
|
|
34975
|
+
Math.round((options.endColor[2] - options.startColor[2]) / count),
|
|
34976
|
+
];
|
|
34977
|
+
for (let i = 0; i < count - 1; i++) {
|
|
34978
|
+
for (let j = 0; j < 3; j++) {
|
|
34979
|
+
color[j] += delta[j];
|
|
34980
|
+
}
|
|
34981
|
+
result.push(color.slice());
|
|
34982
|
+
}
|
|
34983
|
+
// Force last color = end color
|
|
34984
|
+
result.push(options.endColor.slice());
|
|
34985
|
+
// Output as array
|
|
34986
|
+
return result;
|
|
34987
|
+
}
|
|
34690
34988
|
|
|
34691
34989
|
const APP_ABOUT_DEVELOPERS = new InjectionToken('aboutDevelopers');
|
|
34692
34990
|
const APP_ABOUT_PARTNERS = new InjectionToken('aboutPartners');
|
|
@@ -39819,120 +40117,6 @@ function providePersonService() {
|
|
|
39819
40117
|
};
|
|
39820
40118
|
}
|
|
39821
40119
|
|
|
39822
|
-
// GPL v3 License
|
|
39823
|
-
/** Kinds of file that can be previewed (displayed without being downloaded) */
|
|
39824
|
-
const PREVIEW_KINDS = Object.freeze(['image', 'text', 'code']);
|
|
39825
|
-
/** Extra MIME types (or extensions) that can be previewed as text */
|
|
39826
|
-
const PREVIEW_TEXT_MIME_TYPES = /^(text\/(csv|tab-separated-values|markdown|x-markdown)|application\/csv)$/;
|
|
39827
|
-
const PREVIEW_TEXT_EXTENSIONS = Object.freeze(['csv', 'tsv', 'md', 'markdown']);
|
|
39828
|
-
const MARKDOWN_MIME_TYPES = /^text\/(x-)?markdown$/;
|
|
39829
|
-
const MARKDOWN_EXTENSIONS = Object.freeze(['md', 'markdown']);
|
|
39830
|
-
const HTML_MIME_TYPES = /^(text\/html|application\/xhtml\+xml)$/;
|
|
39831
|
-
const HTML_EXTENSIONS = Object.freeze(['html', 'htm', 'xhtml']);
|
|
39832
|
-
/**
|
|
39833
|
-
* Download (or read) a remote file, sending the application headers (e.g. authorization) when the file is on the current peer.
|
|
39834
|
-
*
|
|
39835
|
-
* - On Android (Cordova downloader): the file is downloaded by the native download manager (visible in the "Downloads" app);
|
|
39836
|
-
* - Otherwise (web, iOS): the file is fetched as a Blob, then saved using a temporary object URL.
|
|
39837
|
-
*/
|
|
39838
|
-
class FileDownloadService {
|
|
39839
|
-
network = inject(NetworkService);
|
|
39840
|
-
platform = inject(PlatformService);
|
|
39841
|
-
/**
|
|
39842
|
-
* Can the file be previewed (displayed without being downloaded)?
|
|
39843
|
-
*/
|
|
39844
|
-
canPreview(file) {
|
|
39845
|
-
return this.isImage(file) || this.isText(file);
|
|
39846
|
-
}
|
|
39847
|
-
isImage(file) {
|
|
39848
|
-
return FileTypeUtils.getKindByFile({ name: file?.name, type: file?.mimeType }) === 'image';
|
|
39849
|
-
}
|
|
39850
|
-
isText(file) {
|
|
39851
|
-
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
39852
|
-
if (mimeType && PREVIEW_TEXT_MIME_TYPES.test(mimeType))
|
|
39853
|
-
return true;
|
|
39854
|
-
if (PREVIEW_TEXT_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name)))
|
|
39855
|
-
return true;
|
|
39856
|
-
const kind = FileTypeUtils.getKindByFile({ name: file?.name, type: mimeType });
|
|
39857
|
-
return kind !== 'image' && PREVIEW_KINDS.includes(kind);
|
|
39858
|
-
}
|
|
39859
|
-
isMarkdown(file) {
|
|
39860
|
-
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
39861
|
-
return (mimeType && MARKDOWN_MIME_TYPES.test(mimeType)) || MARKDOWN_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
39862
|
-
}
|
|
39863
|
-
isHtml(file) {
|
|
39864
|
-
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
39865
|
-
return (mimeType && HTML_MIME_TYPES.test(mimeType)) || HTML_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
39866
|
-
}
|
|
39867
|
-
/**
|
|
39868
|
-
* Fetch a remote file, as a Blob
|
|
39869
|
-
*/
|
|
39870
|
-
getBlob(url) {
|
|
39871
|
-
return this.network.getBlob(FileDownloadService.normalizeUrl(url));
|
|
39872
|
-
}
|
|
39873
|
-
/**
|
|
39874
|
-
* Remove duplicated slashes in the path of an URL (e.g. 'http://host//api/file' => 'http://host/api/file').
|
|
39875
|
-
* Needed because the server (and its CORS rules) does not match a path with duplicated slashes.
|
|
39876
|
-
*/
|
|
39877
|
-
static normalizeUrl(url) {
|
|
39878
|
-
if (!url)
|
|
39879
|
-
return url;
|
|
39880
|
-
const [path, ...query] = url.split('?');
|
|
39881
|
-
const normalizedPath = path.replace(/([^:\/])\/{2,}/g, '$1/');
|
|
39882
|
-
return [normalizedPath, ...query].join('?');
|
|
39883
|
-
}
|
|
39884
|
-
/**
|
|
39885
|
-
* Fetch a remote file, as a text (e.g. to preview it)
|
|
39886
|
-
*/
|
|
39887
|
-
async getText(url) {
|
|
39888
|
-
const blob = await this.getBlob(url);
|
|
39889
|
-
return blob.text();
|
|
39890
|
-
}
|
|
39891
|
-
/**
|
|
39892
|
-
* Download a remote file
|
|
39893
|
-
*/
|
|
39894
|
-
async download(url, opts) {
|
|
39895
|
-
if (!url)
|
|
39896
|
-
throw new Error("Missing argument 'url'");
|
|
39897
|
-
url = FileDownloadService.normalizeUrl(url);
|
|
39898
|
-
const filename = opts?.filename || this.getFilename(url);
|
|
39899
|
-
// Android: use the native download manager
|
|
39900
|
-
if (this.platform.canDownload) {
|
|
39901
|
-
const headers = Object.entries(this.network.getAppHeaders(url)).map(([header, value]) => ({
|
|
39902
|
-
header,
|
|
39903
|
-
value: Array.isArray(value) ? value.join(', ') : value,
|
|
39904
|
-
}));
|
|
39905
|
-
await firstValueFrom(this.platform.download({ uri: url, filename, title: filename, mimeType: opts?.mimeType, headers }));
|
|
39906
|
-
return;
|
|
39907
|
-
}
|
|
39908
|
-
// Web, iOS: fetch as a Blob (with the authorization headers), then save it
|
|
39909
|
-
const blob = await this.getBlob(url);
|
|
39910
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
39911
|
-
try {
|
|
39912
|
-
FilesUtils.downloadUri(objectUrl, filename);
|
|
39913
|
-
}
|
|
39914
|
-
finally {
|
|
39915
|
-
// Release the object URL, once the download has started
|
|
39916
|
-
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
|
|
39917
|
-
}
|
|
39918
|
-
}
|
|
39919
|
-
/* -- protected methods -- */
|
|
39920
|
-
getFilename(url) {
|
|
39921
|
-
try {
|
|
39922
|
-
return FilesUtils.getFilenameFromUri(url.split('?')[0]) || 'download';
|
|
39923
|
-
}
|
|
39924
|
-
catch {
|
|
39925
|
-
return 'download';
|
|
39926
|
-
}
|
|
39927
|
-
}
|
|
39928
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
39929
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, providedIn: 'root' });
|
|
39930
|
-
}
|
|
39931
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, decorators: [{
|
|
39932
|
-
type: Injectable,
|
|
39933
|
-
args: [{ providedIn: 'root' }]
|
|
39934
|
-
}] });
|
|
39935
|
-
|
|
39936
40120
|
// GPL v3 License
|
|
39937
40121
|
const APP_FILE_TRANSFER_SERVICE = new InjectionToken('FileTransferService');
|
|
39938
40122
|
|
|
@@ -40406,97 +40590,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
40406
40590
|
}] }] });
|
|
40407
40591
|
|
|
40408
40592
|
class FeedAttachmentUtils {
|
|
40593
|
+
/**
|
|
40594
|
+
* Convert a feed attachment (JSON Feed format) into a generic file attachment
|
|
40595
|
+
*/
|
|
40596
|
+
static toFileAttachment(attachment) {
|
|
40597
|
+
if (!attachment)
|
|
40598
|
+
return undefined;
|
|
40599
|
+
return {
|
|
40600
|
+
url: attachment.url,
|
|
40601
|
+
name: attachment.title?.trim() || undefined,
|
|
40602
|
+
mimeType: attachment.mime_type,
|
|
40603
|
+
size: attachment.size_in_bytes,
|
|
40604
|
+
};
|
|
40605
|
+
}
|
|
40409
40606
|
/**
|
|
40410
40607
|
* Get the file name of an attachment (its title, or the last part of its URL)
|
|
40411
40608
|
*/
|
|
40412
40609
|
static getFilename(attachment) {
|
|
40413
|
-
|
|
40414
|
-
return attachment.title.trim();
|
|
40415
|
-
const path = attachment?.url?.split('?')[0];
|
|
40416
|
-
return path?.substring(path.lastIndexOf('/') + 1) || 'attachment';
|
|
40610
|
+
return FileAttachmentUtils.getFilename(FeedAttachmentUtils.toFileAttachment(attachment));
|
|
40417
40611
|
}
|
|
40418
40612
|
/**
|
|
40419
40613
|
* Get the Material icon (font ligature) of an attachment
|
|
40420
40614
|
*/
|
|
40421
40615
|
static getIcon(attachment) {
|
|
40422
|
-
return
|
|
40423
|
-
}
|
|
40424
|
-
}
|
|
40425
|
-
|
|
40426
|
-
// GPL v3 License
|
|
40427
|
-
/**
|
|
40428
|
-
* Display the content of a feed attachment (text, markdown, image), without downloading it
|
|
40429
|
-
*/
|
|
40430
|
-
class FeedAttachmentPreviewModal {
|
|
40431
|
-
cd = inject(ChangeDetectorRef);
|
|
40432
|
-
modalCtrl = inject(ModalController);
|
|
40433
|
-
fileDownload = inject(FileDownloadService);
|
|
40434
|
-
mobile = inject(LocalSettingsService).mobile;
|
|
40435
|
-
sanitizer = inject(DomSanitizer);
|
|
40436
|
-
elementRef = inject(ElementRef);
|
|
40437
|
-
attachment;
|
|
40438
|
-
loading = true;
|
|
40439
|
-
error;
|
|
40440
|
-
filename;
|
|
40441
|
-
text;
|
|
40442
|
-
markdown = false;
|
|
40443
|
-
imageUrl;
|
|
40444
|
-
/** HTML content, displayed in a sandboxed iframe (scripts allowed, but isolated from the application: no same origin) */
|
|
40445
|
-
html;
|
|
40446
|
-
/** Display the modal in a large size (same as the feed details modal) */
|
|
40447
|
-
maximized = false;
|
|
40448
|
-
async ngOnInit() {
|
|
40449
|
-
this.filename = FeedAttachmentUtils.getFilename(this.attachment);
|
|
40450
|
-
const file = { name: this.filename, mimeType: this.attachment?.mime_type };
|
|
40451
|
-
try {
|
|
40452
|
-
if (this.fileDownload.isImage(file)) {
|
|
40453
|
-
const blob = await this.fileDownload.getBlob(this.attachment.url);
|
|
40454
|
-
this.imageUrl = URL.createObjectURL(blob);
|
|
40455
|
-
}
|
|
40456
|
-
else if (this.fileDownload.isHtml(file)) {
|
|
40457
|
-
const content = await this.fileDownload.getText(this.attachment.url);
|
|
40458
|
-
// Safe: the content is only rendered inside a sandboxed iframe (see template)
|
|
40459
|
-
this.html = this.sanitizer.bypassSecurityTrustHtml(content);
|
|
40460
|
-
}
|
|
40461
|
-
else {
|
|
40462
|
-
this.markdown = this.fileDownload.isMarkdown(file);
|
|
40463
|
-
this.text = await this.fileDownload.getText(this.attachment.url);
|
|
40464
|
-
}
|
|
40465
|
-
}
|
|
40466
|
-
catch (err) {
|
|
40467
|
-
console.error(`[feed-attachment] Cannot load attachment {${this.attachment?.url}}`, err);
|
|
40468
|
-
this.error = 'SOCIAL.FEED.ATTACHMENTS.PREVIEW_ERROR';
|
|
40469
|
-
}
|
|
40470
|
-
finally {
|
|
40471
|
-
this.loading = false;
|
|
40472
|
-
this.cd.markForCheck();
|
|
40473
|
-
}
|
|
40474
|
-
}
|
|
40475
|
-
ngOnDestroy() {
|
|
40476
|
-
if (this.imageUrl)
|
|
40477
|
-
URL.revokeObjectURL(this.imageUrl);
|
|
40478
|
-
}
|
|
40479
|
-
async download() {
|
|
40480
|
-
await this.fileDownload.download(this.attachment.url, { filename: this.filename, mimeType: this.attachment.mime_type });
|
|
40481
|
-
}
|
|
40482
|
-
/** Display the modal in a large size (to restore the size: close then reopen the attachment) */
|
|
40483
|
-
maximize() {
|
|
40484
|
-
this.maximized = true;
|
|
40485
|
-
this.elementRef.nativeElement.closest('ion-modal')?.classList.add('modal-large');
|
|
40486
|
-
this.cd.markForCheck();
|
|
40487
|
-
}
|
|
40488
|
-
close() {
|
|
40489
|
-
return this.modalCtrl.dismiss();
|
|
40616
|
+
return FileAttachmentUtils.getIcon(FeedAttachmentUtils.toFileAttachment(attachment));
|
|
40490
40617
|
}
|
|
40491
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedAttachmentPreviewModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
40492
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FeedAttachmentPreviewModal, isStandalone: false, selector: "app-feed-attachment-preview-modal", inputs: { attachment: "attachment" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
40493
40618
|
}
|
|
40494
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedAttachmentPreviewModal, decorators: [{
|
|
40495
|
-
type: Component,
|
|
40496
|
-
args: [{ selector: 'app-feed-attachment-preview-modal', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"] }]
|
|
40497
|
-
}], propDecorators: { attachment: [{
|
|
40498
|
-
type: Input
|
|
40499
|
-
}] } });
|
|
40500
40619
|
|
|
40501
40620
|
class FeedDirective {
|
|
40502
40621
|
element;
|
|
@@ -40650,9 +40769,6 @@ class FeedsComponent {
|
|
|
40650
40769
|
platform = inject(PlatformService);
|
|
40651
40770
|
networkService = inject(NetworkService);
|
|
40652
40771
|
router = inject(Router);
|
|
40653
|
-
fileDownload = inject(FileDownloadService);
|
|
40654
|
-
modalCtrl = inject(ModalController);
|
|
40655
|
-
toastCtrl = inject(ToastController);
|
|
40656
40772
|
version;
|
|
40657
40773
|
modalItemId;
|
|
40658
40774
|
onRefresh = new EventEmitter();
|
|
@@ -40876,47 +40992,7 @@ class FeedsComponent {
|
|
|
40876
40992
|
getVisibleEntries(feeds, filterItem) {
|
|
40877
40993
|
return (feeds || []).flatMap((feed) => (feed?.items || []).filter((item) => !filterItem || filterItem(item)).map((item) => ({ feed, item })));
|
|
40878
40994
|
}
|
|
40879
|
-
|
|
40880
|
-
getAttachmentIcon = FeedAttachmentUtils.getIcon;
|
|
40881
|
-
canPreviewAttachment = (attachment) => this.fileDownload.canPreview({ name: FeedAttachmentUtils.getFilename(attachment), mimeType: attachment?.mime_type });
|
|
40882
|
-
/**
|
|
40883
|
-
* Open an attachment: display its content, if possible (text, markdown, image...), or download it
|
|
40884
|
-
*/
|
|
40885
|
-
async openAttachment(event, attachment) {
|
|
40886
|
-
event?.preventDefault();
|
|
40887
|
-
event?.stopPropagation();
|
|
40888
|
-
if (!attachment?.url)
|
|
40889
|
-
return;
|
|
40890
|
-
if (!this.canPreviewAttachment(attachment)) {
|
|
40891
|
-
return this.downloadAttachment(event, attachment);
|
|
40892
|
-
}
|
|
40893
|
-
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
40894
|
-
const modal = await this.modalCtrl.create({
|
|
40895
|
-
component: FeedAttachmentPreviewModal,
|
|
40896
|
-
componentProps: { attachment },
|
|
40897
|
-
cssClass: hasTopModal ? 'stack-modal' : undefined,
|
|
40898
|
-
});
|
|
40899
|
-
await modal.present();
|
|
40900
|
-
}
|
|
40901
|
-
/**
|
|
40902
|
-
* Download an attachment (using the native download manager on Android)
|
|
40903
|
-
*/
|
|
40904
|
-
async downloadAttachment(event, attachment) {
|
|
40905
|
-
event?.preventDefault();
|
|
40906
|
-
event?.stopPropagation();
|
|
40907
|
-
if (!attachment?.url)
|
|
40908
|
-
return;
|
|
40909
|
-
try {
|
|
40910
|
-
await this.fileDownload.download(attachment.url, {
|
|
40911
|
-
filename: FeedAttachmentUtils.getFilename(attachment),
|
|
40912
|
-
mimeType: attachment.mime_type,
|
|
40913
|
-
});
|
|
40914
|
-
}
|
|
40915
|
-
catch (err) {
|
|
40916
|
-
console.error(`[feed-component] Cannot download attachment {${attachment.url}}`, err);
|
|
40917
|
-
await Toasts.show(this.toastCtrl, this.translate, { type: 'error', message: 'SOCIAL.FEED.ATTACHMENTS.DOWNLOAD_ERROR' });
|
|
40918
|
-
}
|
|
40919
|
-
}
|
|
40995
|
+
toFileAttachment = FeedAttachmentUtils.toFileAttachment;
|
|
40920
40996
|
getFeedHomeUrl(feed) {
|
|
40921
40997
|
return feed.home_page_url ?? JsonFeedUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
|
|
40922
40998
|
}
|
|
@@ -40982,11 +41058,11 @@ class FeedsComponent {
|
|
|
40982
41058
|
}
|
|
40983
41059
|
}
|
|
40984
41060
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedsComponent, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT }, { token: APP_FEED_SERVICE, optional: true }, { token: AccountService }, { token: i2$2.AlertController }], target: i0.ɵɵFactoryTarget.Component });
|
|
40985
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FeedsComponent, isStandalone: false, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }, { propertyName: "feedContent", first: true, predicate: ["feedContent"], descendants: true }, { propertyName: "feedItems", first: true, predicate: ["feedItems"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\" [class.mobile]=\"mobile\">\n @for (attachment of item.attachments; track attachment.url) {\n @let filename = attachment | map: getAttachmentFilename;\n @let canPreview = attachment | map: canPreviewAttachment;\n @let action =\n (canPreview ? 'SOCIAL.FEED.ATTACHMENTS.BTN_PREVIEW' : 'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD')\n | translate;\n <ion-chip\n class=\"feed-attachment\"\n [class.has-download]=\"canPreview\"\n [outline]=\"true\"\n [title]=\"\n filename +\n (attachment.size_in_bytes ? ' (' + (attachment.size_in_bytes | fileSize) + ')' : '') +\n ' - ' +\n action\n \"\n (click)=\"openAttachment($event, attachment)\"\n tappable\n >\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (canPreview) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span\n class=\"attachment-download\"\n role=\"button\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n (click)=\"downloadAttachment($event, attachment)\"\n >\n <mat-icon>download</mat-icon>\n </span>\n }\n </ion-chip>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}.feed-attachments .feed-attachment{--feed-attachment-height: 22px;--feed-attachment-icon-size: 14px;--feed-attachment-padding: 6px;max-width:100%;height:var(--feed-attachment-height);min-height:var(--feed-attachment-height);margin:0;padding:0 var(--feed-attachment-padding);font-size:12px;border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.feed-attachments .feed-attachment mat-icon{flex-shrink:0;width:var(--feed-attachment-icon-size);height:var(--feed-attachment-icon-size);font-size:var(--feed-attachment-icon-size);line-height:var(--feed-attachment-icon-size)}.feed-attachments .feed-attachment .attachment-icon{margin-inline-end:4px}.feed-attachments .feed-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed-attachments .feed-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.feed-attachments .feed-attachment.has-download:hover{padding-inline-end:0}.feed-attachments .feed-attachment:hover .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}.feed-attachments .feed-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}.feed-attachments.mobile .feed-attachment{--feed-attachment-height: 32px;--feed-attachment-icon-size: 18px;--feed-attachment-padding: 10px;font-size:14px}.feed-attachments.mobile .feed-attachment.has-download{padding-inline-end:0}.feed-attachments.mobile .feed-attachment .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}\n"], dependencies: [{ kind: "component", type: i2$2.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$2.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$2.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonModal, selector: "ion-modal" }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
41061
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FeedsComponent, isStandalone: false, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }, { propertyName: "feedContent", first: true, predicate: ["feedContent"], descendants: true }, { propertyName: "feedItems", first: true, predicate: ["feedItems"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\">\n @for (attachment of item.attachments; track attachment.url) {\n <app-file-attachment class=\"feed-attachment\" [attachment]=\"attachment | map: toFileAttachment\" [mobile]=\"mobile\"></app-file-attachment>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}\n"], dependencies: [{ kind: "component", type: i2$2.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$2.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$2.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonModal, selector: "ion-modal" }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FileAttachmentComponent, selector: "app-file-attachment", inputs: ["attachment", "mobile"] }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
40986
41062
|
}
|
|
40987
41063
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedsComponent, decorators: [{
|
|
40988
41064
|
type: Component,
|
|
40989
|
-
args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\" [class.mobile]=\"mobile\">\n @for (attachment of item.attachments; track attachment.url) {\n @let filename = attachment | map: getAttachmentFilename;\n @let canPreview = attachment | map: canPreviewAttachment;\n @let action =\n (canPreview ? 'SOCIAL.FEED.ATTACHMENTS.BTN_PREVIEW' : 'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD')\n | translate;\n <ion-chip\n class=\"feed-attachment\"\n [class.has-download]=\"canPreview\"\n [outline]=\"true\"\n [title]=\"\n filename +\n (attachment.size_in_bytes ? ' (' + (attachment.size_in_bytes | fileSize) + ')' : '') +\n ' - ' +\n action\n \"\n (click)=\"openAttachment($event, attachment)\"\n tappable\n >\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (canPreview) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span\n class=\"attachment-download\"\n role=\"button\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n (click)=\"downloadAttachment($event, attachment)\"\n >\n <mat-icon>download</mat-icon>\n </span>\n }\n </ion-chip>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}.feed-attachments .feed-attachment{--feed-attachment-height: 22px;--feed-attachment-icon-size: 14px;--feed-attachment-padding: 6px;max-width:100%;height:var(--feed-attachment-height);min-height:var(--feed-attachment-height);margin:0;padding:0 var(--feed-attachment-padding);font-size:12px;border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.feed-attachments .feed-attachment mat-icon{flex-shrink:0;width:var(--feed-attachment-icon-size);height:var(--feed-attachment-icon-size);font-size:var(--feed-attachment-icon-size);line-height:var(--feed-attachment-icon-size)}.feed-attachments .feed-attachment .attachment-icon{margin-inline-end:4px}.feed-attachments .feed-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed-attachments .feed-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.feed-attachments .feed-attachment.has-download:hover{padding-inline-end:0}.feed-attachments .feed-attachment:hover .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}.feed-attachments .feed-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}.feed-attachments.mobile .feed-attachment{--feed-attachment-height: 32px;--feed-attachment-icon-size: 18px;--feed-attachment-padding: 10px;font-size:14px}.feed-attachments.mobile .feed-attachment.has-download{padding-inline-end:0}.feed-attachments.mobile .feed-attachment .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}\n"] }]
|
|
41065
|
+
args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\">\n @for (attachment of item.attachments; track attachment.url) {\n <app-file-attachment class=\"feed-attachment\" [attachment]=\"attachment | map: toFileAttachment\" [mobile]=\"mobile\"></app-file-attachment>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}\n"] }]
|
|
40990
41066
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
40991
41067
|
type: Inject,
|
|
40992
41068
|
args: [ENVIRONMENT]
|
|
@@ -45061,16 +45137,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
45061
45137
|
|
|
45062
45138
|
class FeedModule {
|
|
45063
45139
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
45064
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, declarations: [FeedsComponent, FeedPage, FeedDirective
|
|
45065
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, providers: [provideChildTranslateService()], imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, MessageModule] });
|
|
45140
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, declarations: [FeedsComponent, FeedPage, FeedDirective], imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, SharedFileAttachmentModule, MessageModule], exports: [FeedsComponent, FeedPage, FeedDirective, SharedFileAttachmentModule] });
|
|
45141
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, providers: [provideChildTranslateService()], imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, SharedFileAttachmentModule, MessageModule, SharedFileAttachmentModule] });
|
|
45066
45142
|
}
|
|
45067
45143
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, decorators: [{
|
|
45068
45144
|
type: NgModule,
|
|
45069
45145
|
args: [{
|
|
45070
45146
|
// MessageModule: the feed service opens the message modal (to edit a feed item)
|
|
45071
|
-
imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, MessageModule],
|
|
45072
|
-
declarations: [FeedsComponent, FeedPage, FeedDirective
|
|
45073
|
-
exports: [FeedsComponent, FeedPage, FeedDirective,
|
|
45147
|
+
imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, SharedFileAttachmentModule, MessageModule],
|
|
45148
|
+
declarations: [FeedsComponent, FeedPage, FeedDirective],
|
|
45149
|
+
exports: [FeedsComponent, FeedPage, FeedDirective, SharedFileAttachmentModule],
|
|
45074
45150
|
providers: [provideChildTranslateService()],
|
|
45075
45151
|
}]
|
|
45076
45152
|
}] });
|
|
@@ -58766,5 +58842,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
58766
58842
|
* Generated bundle index. Do not edit.
|
|
58767
58843
|
*/
|
|
58768
58844
|
|
|
58769
|
-
export { ADMIN_ICONS, APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_ACCOUNT_SERVICE, APP_ACCOUNT_SERVICE_OPTIONS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FILE_TRANSFER_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_SERVICE, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOME_TOOLBAR_BUTTONS, APP_HOTKEYS_CONFIG, APP_INPUT_FIELD, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_MESSAGE_SERVICE_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PERSON_SERVICE, APP_PERSON_SERVICE_OPTIONS, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_SHOW_TOOLTIP, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TABLE_OPTIONS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractPersonService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppHomePageModule, AppIconComponent, AppIconModule, AppIconSelectorField, AppIconSelectorModal, AppIconSelectorModule, AppImageGalleryComponent, AppImageGallerySlideshowComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSelectUsersModal, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextFormModule, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppWebSocket, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFindPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutoTooltipDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_ICONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangeCaseToUnderscorePipe, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_ISO_PATTERNS, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateFromPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EmptyMissingTranslationHandler, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FILE_KIND_ICONS, FeedAttachmentPreviewModal, FeedAttachmentUtils, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileDownloadService, FileResponse, FileService, FileSizePipe, FileTypeUtils, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayAtControlPipe, FormArrayAtGroupPipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetNamePipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GitlabUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, HttpUtils, IMAGE_DEFAULTS, ION_ICONS, IPosition, IconSelectorTestPage, IconSelectorTestingModule, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImageSizes, ImagesUtils, InMemoryEntitiesService, InputFieldTestPage, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, ListFormTestPage, ListFormTestingModule, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialAutocompleteFooterDirective, MaterialAutocompleteHeaderDirective, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageAttachment, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NativeWebSocket, NavActionsColumnComponent, NestedTableTestPage, NetworkService, NetworkUtils, NewTokenForm, NewTokenModal, NgInitDirective, NgVarDirective, NoHtmlPipe, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PLUS_PLACEHOLDER_CHAR_REGEXP_GLOBAL, PRINT_ID_QUERY_PARAM, PRINT_LOADING_STORAGE_KEY_PREFIX, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFilterAdditionalFields, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, PrintService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, ReferentialsToStringPipe, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, RxStateOutput, RxStateProperty, RxStateRegister, RxStateSelect, SCRYPT_PARAMS, SETTINGS_COMPACT_ROWS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_ICONS, SHARED_IONIC_COMPONENTS, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_ICONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, STARTUP_DATA_STORAGE_KEY, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedIonicModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StartupService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StorageServiceWrapper, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, TRACKED_QUERIES_STORAGE_KEY, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, TimeFormatPipe, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, USER_CONTROLLER, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserController, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, assignSkipUndefined, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, collectByPropertyPath, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createApolloClientOptions, createAppStartupInitializer, createPromiseEvent, createPromiseEventEmitter, createTestingImageDataUrl, createTrackerLink, decorateWithTakeUntil, deepMergeSkipUndefined, departmentToString, departmentsToString, disableAndClearControl, disableAndClearControls, disableControl, disableControls, emitPromiseEvent, enableControl, enableControls, enableRxStateProdMode, entityToString, equals, equalsOrNil, escapeRegExp, expansionAnimation, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorLuminance, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormControlName, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hasFocusWithin, hexToRgb, hexToRgbArray, initArrayControlsFromValues, initializeSharedModule, interpolateString, intersectArrays, isAndroid, isAppInputField, isAppTextInputField, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isLightColor, isMacOS, isMobile, isMutationOperation, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isSubscriptionOperation, isTouchUi, isValidVersion, isVersionCompatible, isWebAnimationsSupported, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, loggerLink, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mergeObjectsWithoutUndefined, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseBooleanText, parseLatLongText, parseLatitudeOrLongitude, parseVersion, propertiesPathComparator, propertyComparator, propertyPathComparator, provideAccountService, provideAllIonicIcons, provideIonicIcons, provideIonicIconsInitializer, providePersonService, referentialToString, referentialsToString, registerIonicIcons, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, restoreTrackedQueries, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, setControlRequired, setControlsEnabled, setFormErrors, setPropertyByPath, setTabIndex, sleep, slideDownAnimation, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitArrayInChunks, splitById, splitByProperty, splitDegreesToDDArray, splitDegreesToDDMMArray, splitDegreesToDDMMSSArray, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toLoadData, toLoadResult, toNotNil, toNumber, trimEmptyToNull, truncateHtml, typeTextIntoInput, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
58845
|
+
export { ADMIN_ICONS, APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_ACCOUNT_SERVICE, APP_ACCOUNT_SERVICE_OPTIONS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FILE_TRANSFER_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_SERVICE, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOME_TOOLBAR_BUTTONS, APP_HOTKEYS_CONFIG, APP_INPUT_FIELD, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_MESSAGE_SERVICE_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PERSON_SERVICE, APP_PERSON_SERVICE_OPTIONS, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_SHOW_TOOLTIP, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TABLE_OPTIONS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractPersonService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppHomePageModule, AppIconComponent, AppIconModule, AppIconSelectorField, AppIconSelectorModal, AppIconSelectorModule, AppImageGalleryComponent, AppImageGallerySlideshowComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSelectUsersModal, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextFormModule, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppWebSocket, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFindPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutoTooltipDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_ICONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangeCaseToUnderscorePipe, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_ISO_PATTERNS, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateFromPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EmptyMissingTranslationHandler, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FILE_KIND_ICONS, FeedAttachmentUtils, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileAttachmentComponent, FileAttachmentPreviewModal, FileAttachmentUtils, FileDownloadService, FileResponse, FileService, FileSizePipe, FileTypeUtils, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayAtControlPipe, FormArrayAtGroupPipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetNamePipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GitlabUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, HttpUtils, IMAGE_DEFAULTS, ION_ICONS, IPosition, IconSelectorTestPage, IconSelectorTestingModule, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImageSizes, ImagesUtils, InMemoryEntitiesService, InputFieldTestPage, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, ListFormTestPage, ListFormTestingModule, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialAutocompleteFooterDirective, MaterialAutocompleteHeaderDirective, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageAttachment, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NativeWebSocket, NavActionsColumnComponent, NestedTableTestPage, NetworkService, NetworkUtils, NewTokenForm, NewTokenModal, NgInitDirective, NgVarDirective, NoHtmlPipe, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PLUS_PLACEHOLDER_CHAR_REGEXP_GLOBAL, PRINT_ID_QUERY_PARAM, PRINT_LOADING_STORAGE_KEY_PREFIX, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFilterAdditionalFields, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, PrintService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, ReferentialsToStringPipe, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, RxStateOutput, RxStateProperty, RxStateRegister, RxStateSelect, SCRYPT_PARAMS, SETTINGS_COMPACT_ROWS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_ICONS, SHARED_IONIC_COMPONENTS, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_ICONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, STARTUP_DATA_STORAGE_KEY, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFileAttachmentModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedIonicModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StartupService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StorageServiceWrapper, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, TRACKED_QUERIES_STORAGE_KEY, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, TimeFormatPipe, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, USER_CONTROLLER, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserController, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, assignSkipUndefined, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, collectByPropertyPath, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createApolloClientOptions, createAppStartupInitializer, createPromiseEvent, createPromiseEventEmitter, createTestingImageDataUrl, createTrackerLink, decorateWithTakeUntil, deepMergeSkipUndefined, departmentToString, departmentsToString, disableAndClearControl, disableAndClearControls, disableControl, disableControls, emitPromiseEvent, enableControl, enableControls, enableRxStateProdMode, entityToString, equals, equalsOrNil, escapeRegExp, expansionAnimation, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorLuminance, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormControlName, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hasFocusWithin, hexToRgb, hexToRgbArray, initArrayControlsFromValues, initializeSharedModule, interpolateString, intersectArrays, isAndroid, isAppInputField, isAppTextInputField, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isLightColor, isMacOS, isMobile, isMutationOperation, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isSubscriptionOperation, isTouchUi, isValidVersion, isVersionCompatible, isWebAnimationsSupported, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, loggerLink, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mergeObjectsWithoutUndefined, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseBooleanText, parseLatLongText, parseLatitudeOrLongitude, parseVersion, propertiesPathComparator, propertyComparator, propertyPathComparator, provideAccountService, provideAllIonicIcons, provideIonicIcons, provideIonicIconsInitializer, providePersonService, referentialToString, referentialsToString, registerIonicIcons, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, restoreTrackedQueries, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, setControlRequired, setControlsEnabled, setFormErrors, setPropertyByPath, setTabIndex, sleep, slideDownAnimation, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitArrayInChunks, splitById, splitByProperty, splitDegreesToDDArray, splitDegreesToDDMMArray, splitDegreesToDDMMSSArray, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toLoadData, toLoadResult, toNotNil, toNumber, trimEmptyToNull, truncateHtml, typeTextIntoInput, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
58770
58846
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|