Newer
Older
import { COMMA } from '@angular/cdk/keycodes';
import { Component, OnInit, Inject } from '@angular/core';

Alex ORLUC
committed
import { HttpClient } from '@angular/common/http';
import { LANG } from '../../translate.component';
import { NotificationService } from '../../notification.service';
import { Observable, of } from 'rxjs';
import { MatDialog, MAT_DIALOG_DATA, MatDialogRef, MatChipInputEvent } from '@angular/material';
Guillaume Heurtier
committed
import { switchMap, map, catchError, filter, exhaustMap, tap, debounceTime, distinctUntilChanged } from 'rxjs/operators';

Alex ORLUC
committed
import { FormControl } from '@angular/forms';
import { FunctionsService } from '../../../service/functions.service';
import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop';

Alex ORLUC
committed
import { ContactService } from '../../../service/contact.service';
import { AppService } from '../../../service/app.service';
import { ConfirmComponent } from '../../../plugins/modal/confirm.component';
import { PrivilegeService } from '../../../service/privileges.service';
import { HeaderService } from '../../../service/header.service';

Alex ORLUC
committed
declare function $j(selector: any): any;

Alex ORLUC
committed
declare var tinymce: any;

Alex ORLUC
committed
@Component({
selector: 'app-sent-resource-page',
templateUrl: "sent-resource-page.component.html",
styleUrls: ['sent-resource-page.component.scss'],

Alex ORLUC
committed
providers: [ContactService, AppService],

Alex ORLUC
committed
})
export class SentResourcePageComponent implements OnInit {

Alex ORLUC
committed
lang: any = LANG;
loading: boolean = true;
readonly separatorKeysCodes: number[] = [COMMA];

Alex ORLUC
committed
availableEmailModels: any[] = [];
availableSignEmailModels: any[] = [];
resourceData: any = null;
availableSenders: any[] = [];
currentSender: any = {};

Alex ORLUC
committed
recipients: any[] = [];
copies: any[] = [];
invisibleCopies: any[] = [];
recipientsInput: FormControl = new FormControl();

Alex ORLUC
committed
emailSignListForm = new FormControl();
templateEmailListForm = new FormControl();
filteredEmails: Observable<string[]>;
showCopies: boolean = false;
showInvisibleCopies: boolean = false;

Alex ORLUC
committed
emailId: number = null;

Alex ORLUC
committed
emailStatus: string = 'WAITING';

Alex ORLUC
committed
currentEmailAttachTool: string = '';
emailAttachTool: any = {

Alex ORLUC
committed
document: {
icon: 'fa fa-file',

Alex ORLUC
committed
list: []

Alex ORLUC
committed
notes: {
icon: 'fas fa-pen-square',

Alex ORLUC
committed
list: []

Alex ORLUC
committed
attachments: {
icon: 'fa fa-paperclip',

Alex ORLUC
committed
list: []
emailAttach: any = {};

Alex ORLUC
committed
pdfMode: boolean = false;
htmlMode: boolean = true;

Alex ORLUC
committed
constructor(
public http: HttpClient,
private notify: NotificationService,
@Inject(MAT_DIALOG_DATA) public data: any,

Alex ORLUC
committed
public dialog: MatDialog,
public dialogRef: MatDialogRef<SentResourcePageComponent>,

Alex ORLUC
committed
public functions: FunctionsService,
private contactService: ContactService,
public privilegeService: PrivilegeService,
public headerService: HeaderService

Alex ORLUC
committed
) { }
async ngOnInit(): Promise<void> {
Object.keys(this.emailAttachTool).forEach(element => {
if (element === 'document') {
this.emailAttach[element] = {

Alex ORLUC
committed
id: this.data.resId,
isLinked: false,
original: false
};
} else {
this.emailAttach[element] = [];
}
});

Alex ORLUC
committed
await this.getAttachElements();
if (this.data.emailId && this.data.emailType === 'email') {
await this.getEmailData(this.data.emailId);
} else if (this.data.emailId && this.data.emailType === 'acknowledgementReceipt') {
await this.getAcknowledgementReceiptData(this.data.emailId);
if (this.canManageMail()) {
this.initEmailModelsList();
this.initEmailsList();
this.initSignEmailModelsList();
await this.getResourceData();
await this.getUserEmails();
if (this.emailStatus !== 'DRAFT') {
this.setDefaultInfo();
}
}
setTimeout(() => {
this.initMce();
}, 0);

Alex ORLUC
committed
}
initMce() {
tinymce.init({
selector: "textarea#emailSignature",
base_url: '../../node_modules/tinymce/',
setup: (editor: any) => {
editor.on('init', (e: any) => {
this.loading = false;
});
},
readonly: this.emailStatus === 'SENT',

Alex ORLUC
committed
language: this.lang.langISO.replace('-', '_'),
language_url: `../../node_modules/tinymce-i18n/langs/${this.lang.langISO.replace('-', '_')}.js`,
menubar: false,
statusbar: false,
plugins: [

Alex ORLUC
committed
],
external_plugins: {
'maarch_b64image': "../../src/frontend/plugins/tinymce/maarch_b64image/plugin.min.js"
},
toolbar_sticky: true,
toolbar_drawer: 'floating',
toolbar: this.emailStatus !== 'SENT' ?

Alex ORLUC
committed
'undo redo | fontselect fontsizeselect | bold italic underline strikethrough forecolor | maarch_b64image | \
alignleft aligncenter alignright alignjustify \
bullist numlist outdent indent | removeformat' : false

Alex ORLUC
committed
});

Alex ORLUC
committed
}
add(event: MatChipInputEvent, type: string): void {

Alex ORLUC
committed
const input = event.input;
const value = event.value;
if ((value || '').trim()) {
label: value.trim(),
badFormat: this.isBadEmailFormat(value.trim())

Alex ORLUC
committed
}
if (input) {
input.value = '';
}
}
isBadEmailFormat(email: string) {
const regex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/g;
Guillaume Heurtier
committed
return email.trim().match(regex) === null;

Alex ORLUC
committed
closeModal(state: string = '') {
tinymce.remove();
this.dialogRef.close(state);
}
addEmail(item: any, type: string) {
this[type].splice(this[type].length - 1, 1);
if (item.type === 'contactGroup') {
this.http.get(`../../rest/contactsGroups/${item.id}`).pipe(
map((data: any) => {
data = data.contactsGroup.contacts.filter((contact: any) => !this.functions.empty(contact.email)).map((contact: any) => {
return {
label: contact.contact,
email: contact.email
}
});
return data;
}),
tap((data: any) => {
this[type] = this[type].concat(data);
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
} else {
this[type].push({
label: item.label,
email: item.email
});
}
}

Alex ORLUC
committed
mergeEmailTemplate(templateId: any) {
this.templateEmailListForm.reset();
this.http.post(`../../rest/templates/${templateId}/mergeEmail`, { data: { resId: this.data.resId } }).pipe(
tap((data: any) => {
var div = document.createElement('div');

Alex ORLUC
committed
div.innerHTML = tinymce.get('emailSignature').getContent();
if (div.getElementsByClassName('signature').length > 0) {
const signatureContent = div.getElementsByClassName('signature')[0].innerHTML;
div.getElementsByClassName('signature')[0].remove();

Alex ORLUC
committed
tinymce.get('emailSignature').setContent(`${div.innerHTML}${data.mergedDocument}<div class="signature">${signatureContent}</div>`);

Alex ORLUC
committed
tinymce.get('emailSignature').setContent(`${tinymce.get('emailSignature').getContent()}${data.mergedDocument}`);
if (!this.htmlMode) {
tinymce.get('emailSignature').setContent(tinymce.get('emailSignature').getContent({ format: 'text' }));
}
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}

Alex ORLUC
committed
mergeSignEmailTemplate(templateId: any) {
this.emailSignListForm.reset();
this.http.get(`../../rest/currentUser/emailSignatures/${templateId}`).pipe(
tap((data: any) => {
var div = document.createElement('div');

Alex ORLUC
committed
div.innerHTML = tinymce.get('emailSignature').getContent();
if (div.getElementsByClassName('signature').length > 0) {
div.getElementsByClassName('signature')[0].remove();

Alex ORLUC
committed
tinymce.get('emailSignature').setContent(`${div.innerHTML}<div class="signature">${data.emailSignature.content}</div>`);

Alex ORLUC
committed
tinymce.get('emailSignature').setContent(`${tinymce.get('emailSignature').getContent()}<div class="signature">${data.emailSignature.content}</div>`);
if (!this.htmlMode) {
tinymce.get('emailSignature').setContent(tinymce.get('emailSignature').getContent({ format: 'text' }));
}
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}

Alex ORLUC
committed
remove(item: any, type: string): void {
if (this.canManageMail()) {
const index = this[type].indexOf(item);

Alex ORLUC
committed
if (index >= 0) {
this[type].splice(index, 1);
}

Alex ORLUC
committed
}
}
getEmailData(emailId: number) {
this.http.get(`../../rest/emails/${emailId}`).pipe(
tap((data: any) => {
this.emailCreatorId = data.userId;
this.recipients = data.recipients.map((item: any) => {
return {
label: item,
badFormat: this.isBadEmailFormat(item)
}
});
this.copies = data.cc.map((item: any) => {
return {
label: item,
badFormat: this.isBadEmailFormat(item)
Guillaume Heurtier
committed
});
this.invisibleCopies = data.cci.map((item: any) => {
return {
label: item,
badFormat: this.isBadEmailFormat(item)
}
});
this.showCopies = this.copies.length > 0;
this.showInvisibleCopies = this.invisibleCopies.length > 0;
this.emailsubject = data.object;
this.emailStatus = data.status;
Guillaume Heurtier
committed
this.currentSender = {
entityId: data.sender.entityId,
Guillaume Heurtier
committed
label: data.sender.label,
email: data.sender.email
};
this.emailContent = data.body;
Object.keys(data.document).forEach(element => {
if (['id', 'isLinked', 'original'].indexOf(element) === -1) {
this.emailAttach[element] = [];
data.document[element].forEach((dataAttach: any) => {
const elem = this.emailAttachTool[element].list.filter((item: any) => item.id === dataAttach.id || item.id === dataAttach);
this.emailAttach[element] = this.emailAttach[element].concat(elem.map((item: any) => {
format: dataAttach.original || dataAttach.original === undefined ? item.format : 'pdf',
original: dataAttach.original,
size: dataAttach.original || dataAttach.original === undefined ? item.size : item.convertedDocument.size
}
});
} else if (element === 'isLinked' && data.document.isLinked === true) {
this.emailAttach.document.isLinked = true;
this.emailAttach.document.format = data.document.original || data.document.original === undefined ? this.emailAttachTool.document.list[0].format : 'pdf',
this.emailAttach.document.original = data.document.original;
this.emailAttach.document.size = this.emailAttach.document.original ? this.emailAttachTool.document.list[0].size : this.emailAttachTool.document.list[0].convertedDocument.size
resolve(true);
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
resolve(false);
return of(false);
})
).subscribe();
});
}
getAcknowledgementReceiptData(emailId: number) {
return new Promise((resolve) => {
this.http.get(`../../rest/acknowledgementReceipts/${emailId}`).pipe(
tap((data: any) => {
this.currentSender = {
label: data.acknowledgementReceipt.userLabel,
email: data.acknowledgementReceipt.userLabel
};
label: !this.functions.empty(data.acknowledgementReceipt.contact) ? this.contactService.formatContact(data.acknowledgementReceipt.contact) : this.lang.contactDeleted,
email: !this.functions.empty(data.acknowledgementReceipt.contact.email) ? data.acknowledgementReceipt.contact.email : this.lang.withoutEmail
}];
this.emailStatus = 'SENT';
}),
exhaustMap(() => this.http.get(`../../rest/acknowledgementReceipts/${emailId}/content`)),
tap((data: any) => {
this.pdfMode = data.format === 'pdf';
if (this.pdfMode) {
this.emailsubject = this.lang.ARPaper;
this.emailContent = data.encodedDocument;
} else {
this.emailsubject = this.lang.ARelectronic;
this.emailContent = this.b64DecodeUnicode(data.encodedDocument);
resolve(true);
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
resolve(false);
return of(false);
})
).subscribe();
});
}
this.http.get(`../../rest/resources/${this.data.resId}?light=true`).pipe(
tap((data: any) => {
this.resourceData = data;

Alex ORLUC
committed
this.emailAttach.document.chrono = this.resourceData.chrono;
this.emailAttach.document.label = this.resourceData.subject;

Alex ORLUC
committed
resolve(true);
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
resolve(false);
return of(false);
})
).subscribe();
});
setDefaultInfo() {
this.emailsubject = `[${this.resourceData.chrono}] ${this.resourceData.subject}`;
this.emailsubject = this.emailsubject.substring(0, 70);
this.currentSender = this.availableSenders[0];
if (!this.functions.empty(this.resourceData.senders)) {
this.resourceData.senders.forEach((sender: any) => {
this.setSender(sender.id);
});
}
}
setSender(id: number) {
this.http.get(`../../rest/contacts/${id}`).pipe(
tap((data: any) => {
if (!this.functions.empty(data.email)) {
this.recipients.push(
{

Alex ORLUC
committed
label: this.contactService.formatContact(data),
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
getUserEmails() {
this.http.get('../../rest/currentUser/availableEmails').pipe(
tap((data: any) => {
this.availableSenders = data.emails;
resolve(true);
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
resolve(false);
return of(false);
})
).subscribe();
});

Alex ORLUC
committed
this.http.get(`../../rest/resources/${this.data.resId}/emailsInitialization`).pipe(
tap((data: any) => {

Alex ORLUC
committed
Object.keys(data).forEach(element => {
if (element === 'resource') {
this.emailAttachTool.document.list = [];
if (!this.functions.empty(data[element])) {
this.emailAttachTool.document.list = [data[element]];
}

Alex ORLUC
committed
} else {
this.emailAttachTool[element].list = data[element].map((item: any) => {
if (item.attachInMail) {
this.toggleAttachMail(item, element, 'original');
}

Alex ORLUC
committed
return {
...item,
original: item.original !== undefined ? item.original : true,

Alex ORLUC
committed
title: item.chrono !== undefined ? `${item.chrono} - ${item.label} (${item.typeLabel})` : `${item.label} (${item.typeLabel})`
}
});
}
});
resolve(true);
}),
catchError((err: any) => {
this.notify.handleSoftErrors(err);
resolve(false);
return of(false);
})

Alex ORLUC
committed
).subscribe();

Alex ORLUC
committed
initEmailsList() {
this.recipientsInput.valueChanges.pipe(

Alex ORLUC
committed
debounceTime(300),
tap((value) => {
if (value.length === 0) {
this.filteredEmails = of([]);
}
}),

Alex ORLUC
committed
filter(value => value.length > 2),
switchMap(data => this.http.get('../../rest/autocomplete/correspondents', { params: { "search": data, "searchEmails": 'true' } })),

Alex ORLUC
committed
tap((data: any) => {
data = data.filter((contact: any) => !this.functions.empty(contact.email) || contact.type === 'contactGroup').map((contact: any) => {
let label = '';
if (contact.type === 'user' || contact.type === 'contact') {
if (!this.functions.empty(contact.firstname) || !this.functions.empty(contact.lastname)) {
label = contact.firstname + ' ' + contact.lastname;
} else {
label = contact.company;
}
} else if (contact.type === 'contactGroup') {
label = `${contact.firstname} ${contact.lastname}`;
} else {
label = `${contact.lastname}`;
}
return {
id: contact.id,
type: contact.type,
label: label,
email: contact.email
}
});
this.filteredEmails = of(data);

Alex ORLUC
committed
}),
catchError((err) => {
this.notify.handleSoftErrors(err);

Alex ORLUC
committed
return of(false);
})
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
).subscribe();
}
initEmailModelsList() {
this.http.get(`../../rest/resources/${this.data.resId}/emailTemplates`).pipe(
tap((data: any) => {
this.availableEmailModels = data.templates;
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
initSignEmailModelsList() {
this.http.get(`../../rest/currentUser/emailSignatures`).pipe(
tap((data: any) => {
this.availableSignEmailModels = data.emailSignatures;
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
resetAutocomplete() {
this.filteredEmails = of([]);
}

Alex ORLUC
committed
this.emailStatus = 'WAITING';
if (this.data.emailId === null) {
if (!this.isAllEmailRightFormat()) {
this.notify.error(this.lang.badEmailsFormat);

Alex ORLUC
committed
} else {
if (this.emailsubject === '') {
const dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.lang.confirm, msg: this.lang.warnEmptySubject } });
dialogRef.afterClosed().pipe(
filter((data: string) => data === 'ok'),
tap(() => {
this.createEmail(true);
})
).subscribe();
} else {
this.createEmail(true);

Alex ORLUC
committed
}
} else {
this.updateEmail(true);

Alex ORLUC
committed
}
}
createEmail(closeModal: boolean = true) {
this.http.post(`../../rest/emails`, this.formatEmail()).pipe(

Alex ORLUC
committed
if (this.emailStatus === 'DRAFT') {
// this.notify.success(this.lang.draftSaved);

Alex ORLUC
committed
} else {
this.notify.success(`${this.lang.sendingEmail}...`);

Alex ORLUC
committed
}
if (closeModal) {
this.closeModal('success');
}
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
const dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.lang.delete, msg: this.lang.confirmAction } });
dialogRef.afterClosed().pipe(
filter((data: string) => data === 'ok'),
exhaustMap(() => this.http.delete(`../../rest/emails/${this.data.emailId}`)),
tap(() => {
this.notify.success(this.lang.emailDeleted);
this.closeModal('success');
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
updateEmail(closeModal: boolean = true) {
this.http.put(`../../rest/emails/${this.data.emailId}`, this.formatEmail()).pipe(

Alex ORLUC
committed
if (this.emailStatus === 'DRAFT') {
// this.notify.success(this.lang.draftUpdated);

Alex ORLUC
committed
} else {
this.notify.success(`${this.lang.sendingEmail}...`);

Alex ORLUC
committed
}
if (closeModal) {
this.closeModal('success');
}
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})

Alex ORLUC
committed
saveDraft() {
if (this.canManageMail()) {
this.emailStatus = 'DRAFT';
if (this.data.emailId === null) {
if (!this.functions.empty(tinymce.get('emailSignature').getContent())) {
this.createEmail(true);
} else {
this.closeModal();
}
} else {
this.updateEmail(true);
}

Alex ORLUC
committed
} else {

Alex ORLUC
committed
}
}
drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer !== event.container) {
transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);
}

Alex ORLUC
committed
}
toggleAttachMail(item: any, type: string, mode: string) {
if (type === 'document') {
if (this.emailAttach.document.isLinked === false) {
this.emailAttach.document.isLinked = true;
this.emailAttach.document.format = mode !== 'pdf' ? item.format : 'pdf',
Guillaume Heurtier
committed
this.emailAttach.document.original = mode !== 'pdf';
this.emailAttach.document.size = mode === 'pdf' ? item.convertedDocument.size : item.size;
} else {
if (this.emailAttach[type].filter((attach: any) => attach.id === item.id).length === 0) {
this.emailAttach[type].push({

Alex ORLUC
committed
...item,
format: mode !== 'pdf' ? item.format : 'pdf',
Guillaume Heurtier
committed
original: mode !== 'pdf',
size: mode === 'pdf' ? item.convertedDocument.size : item.size
});
}
}
}
removeAttachMail(index: number, type: string) {
if (type === 'document') {
this.emailAttach.document.isLinked = false;
this.emailAttach.document.original = false;
} else {
this.emailAttach[type].splice(index, 1);
Guillaume Heurtier
committed
switchEditionMode() {
this.htmlMode = !this.htmlMode;
if (this.htmlMode) {
Guillaume Heurtier
committed
$j('.tox-editor-header').show();
tinymce.get('emailSignature').setContent(tinymce.get('emailSignature').getContent());
} else {
const dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.lang.switchInPlainText, msg: this.lang.confirmSwitchInPlanText } });
dialogRef.afterClosed().pipe(
tap((data: string) => {
if (data === 'ok') {
$j('.tox-editor-header').hide();
tinymce.get('emailSignature').setContent(tinymce.get('emailSignature').getContent({ format: 'text' }));
} else {
this.htmlMode = !this.htmlMode;
}
})
).subscribe();
}
}
formatEmail() {
Guillaume Heurtier
committed
let objAttach: any = {};
Object.keys(this.emailAttach).forEach(element => {

Alex ORLUC
committed
if (!this.functions.empty(this.emailAttach[element])) {
if (element === 'document') {
objAttach = {
id: this.emailAttach[element].id,
isLinked: this.emailAttach[element].isLinked,
original: this.emailAttach[element].original
}
} else if (element === 'notes') {
objAttach[element] = this.emailAttach[element].map((item: any) => item.id)
} else {
objAttach[element] = this.emailAttach[element].map((item: any) => {
return {
id: item.id,
original: item.original
}
})
}
let formatSender = {
email: this.currentSender.email,
entityId: !this.functions.empty(this.currentSender.entityId) ? this.currentSender.entityId : null
};

Alex ORLUC
committed
Guillaume Heurtier
committed
return {

Alex ORLUC
committed
document: objAttach,
sender: formatSender,
recipients: this.recipients.map(recipient => recipient.email),
cc: this.showCopies ? this.copies.map(copy => copy.email) : [],
cci: this.showInvisibleCopies ? this.invisibleCopies.map((invCopy => invCopy.email)) : [],
body: this.htmlMode ? tinymce.get('emailSignature').getContent() : tinymce.get('emailSignature').getContent({ format: 'text' }),

Alex ORLUC
committed
status: this.emailStatus
isSelectedAttachMail(item: any, type: string) {
if (type === 'document') {
return this.emailAttach.document.isLinked;
} else {
return this.emailAttach[type].filter((attach: any) => attach.id === item.id).length > 0;
}

Alex ORLUC
committed
canManageMail() {
if ((this.data.emailId === null) || (this.emailStatus !== 'SENT' && this.headerService.user.id === this.emailCreatorId)) {
this.recipientsInput.enable();
return true;
} else {
this.recipientsInput.disable();
return false;

Alex ORLUC
committed
}
}
isAllEmailRightFormat() {
const allEmail = this.recipients.concat(this.copies).concat(this.invisibleCopies);
allEmail.map(item => item.email).forEach(email => {
if (this.isBadEmailFormat(email)) {
state = false;
}
});
return state;
}
Guillaume Heurtier
committed
compareSenders(sender1: any, sender2: any) {
return (sender1.label === sender2.label || ((sender1.label === null || sender2.label === null) && (sender1.entityId === null || sender2.entityId === null))) && sender1.entityId === sender2.entityId && sender1.email === sender2.email;
}
b64DecodeUnicode(str: string) {
// Going backwards: from bytestream, to percent-encoding, to original string.
return decodeURIComponent(atob(str).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}