diff --git a/angular.json b/angular.json index 15e3f31f8..a6fcd73cd 100644 --- a/angular.json +++ b/angular.json @@ -36,7 +36,8 @@ "node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.min.js", "src/assets/js/sql.js", - "src/assets/js/annyang.min.js" + "src/assets/js/annyang.min.js", + "src/assets/js/index.js" ] }, "configurations": { @@ -110,7 +111,8 @@ "node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.min.js", "src/assets/js/sql.js", - "src/assets/js/annyang.min.js" + "src/assets/js/annyang.min.js", + "src/assets/js/index.js" ] } }, diff --git a/src/app/components/alternative-forms/alternative-forms.component.html b/src/app/components/alternative-forms/alternative-forms.component.html index 094c0875b..b0bfb0b2f 100644 --- a/src/app/components/alternative-forms/alternative-forms.component.html +++ b/src/app/components/alternative-forms/alternative-forms.component.html @@ -99,7 +99,7 @@
- +
@@ -108,7 +108,7 @@
diff --git a/src/app/components/alternative-forms/alternative-forms.component.ts b/src/app/components/alternative-forms/alternative-forms.component.ts index 9a821b144..f97d799a0 100644 --- a/src/app/components/alternative-forms/alternative-forms.component.ts +++ b/src/app/components/alternative-forms/alternative-forms.component.ts @@ -9,6 +9,10 @@ import {Ng2ImgMaxService} from 'ng2-img-max'; import {ElementForm} from '../../types'; import {BoardService} from '../../services/board.service'; import {MultilinguismService} from '../../services/multilinguism.service'; +import {ConfigurationService} from '../../services/configuration.service'; +declare var getUrlPicto:any; +declare var clearUrlImageJS:any; +declare var monitorInput:any; @Component({ selector: 'app-alternative-forms', @@ -23,7 +27,8 @@ export class AlternativeFormsComponent implements OnInit { public boardService: BoardService, public getIconService: GeticonService, public dbnaryService: DbnaryService, - public editionService: EditionService) { + public editionService: EditionService, + public configurationService: ConfigurationService) { } imageList = []; @@ -255,7 +260,7 @@ export class AlternativeFormsComponent implements OnInit { */ previewMullberry(t: string) { this.imageSelectionStarted = true; - this.previewWithURL('assets/libs/mulberry-symbols/EN-symbols/' + t + '.svg'); + this.previewWithURL(t); } /** @@ -287,5 +292,24 @@ export class AlternativeFormsComponent implements OnInit { this.imageList = tempList.slice(0, 100); } - + /** + * Return the list of 100 first mullberry library images, sorted by length name, matching with string 'text' + * + * @param text, the string researched text + * @return list of 100 mulberry library images + */ + searchInLibApi(text: string){ + this.imageList = []; + let tempList: any[]; + clearUrlImageJS(); + if(this.configurationService.LANGUAGE_VALUE === 'FR'){ + monitorInput(text, "fra"); + }else{ + monitorInput(text, "eng"); + } + setTimeout(() => { + tempList = getUrlPicto(); + this.imageList = tempList[0].slice(0,100); + }, 500); + } } diff --git a/src/app/components/edition/edition.component.html b/src/app/components/edition/edition.component.html index 3b5463713..f48b3c733 100644 --- a/src/app/components/edition/edition.component.html +++ b/src/app/components/edition/edition.component.html @@ -11,8 +11,7 @@ 'border-style': 'solid', 'border-color': this.editionService.curentBorderColor}">
- + [ngStyle]="{'background-image': this.editionService.imageURL }">
{{this.editionService.name}} diff --git a/src/app/components/image-selection-page/image-selection-page.component.html b/src/app/components/image-selection-page/image-selection-page.component.html index a483968d4..5651efbd5 100644 --- a/src/app/components/image-selection-page/image-selection-page.component.html +++ b/src/app/components/image-selection-page/image-selection-page.component.html @@ -38,6 +38,10 @@
+ + +
@@ -60,8 +65,8 @@
+ [ngStyle]="{'background-image': this.getPictoFromApi(b)}" + (click)="this.previewLibraryFromAPI(b)">
diff --git a/src/app/components/image-selection-page/image-selection-page.component.spec.ts b/src/app/components/image-selection-page/image-selection-page.component.spec.ts index 60d5c796c..eb9295a3e 100644 --- a/src/app/components/image-selection-page/image-selection-page.component.spec.ts +++ b/src/app/components/image-selection-page/image-selection-page.component.spec.ts @@ -44,12 +44,13 @@ describe('ImageSelectionPageComponent', () => { expect(component.editionService.imageURL).toEqual('https://images.freeimages.com/images/large-previews/e12/corn-field-1-1368931.jpg'); }); +/* it('should change the image from mulberry', () => { const compiled = fixture.debugElement.nativeElement; - component.searchInLib('dog'); + component.searchInLibApi('chat'); fixture.detectChanges(); compiled.querySelector('.pictoImg').click(); - expect(component.editionService.imageURL).toContain('dog'); + expect(component.editionService.imageURL).toContain('chat'); }); - +*/ }); diff --git a/src/app/components/image-selection-page/image-selection-page.component.ts b/src/app/components/image-selection-page/image-selection-page.component.ts index d669593f3..fe38c56c6 100644 --- a/src/app/components/image-selection-page/image-selection-page.component.ts +++ b/src/app/components/image-selection-page/image-selection-page.component.ts @@ -14,6 +14,10 @@ import {DialogAddUserComponent} from "../dialog-add-user/dialog-add-user.compone import {MatDialog} from "@angular/material/dialog"; import {DialogModifyColorInsideComponent} from "../dialog-modify-color-inside/dialog-modify-color-inside.component"; import {DialogModifyColorBorderComponent} from "../dialog-modify-color-border/dialog-modify-color-border.component"; +declare var getUrlPicto:any; +declare var clearUrlImageJS:any; +declare var monitorInput:any; + @Component({ selector: 'app-image-selection-page', @@ -27,7 +31,7 @@ export class ImageSelectionPageComponent implements OnInit { * the current list of images related to the chose image library search section * (the image list resulting in the research in the mullbery library) */ - imageList: { lib, word }[]; + imageList = []; myControl = new FormControl(); filteredOptions: Observable; @@ -101,6 +105,10 @@ export class ImageSelectionPageComponent implements OnInit { } } + getPictoFromApi(p:string){ + return 'url(' + p + ')'; + } + /** * Set the current preview imageUrl with the image string Url 't' and close the chooseImage panel * @@ -124,7 +132,6 @@ export class ImageSelectionPageComponent implements OnInit { if (isColored) { this.previewWithURL('assets/libs/FR_Pictogrammes_couleur/' + t + '.png'); } else { - console.log('assets/libs/FR_Noir_et_blanc_pictogrammes/' + t + '.png'); this.previewWithURL('assets/libs/FR_Noir_et_blanc_pictogrammes/' + t + '.png'); } } @@ -139,6 +146,10 @@ export class ImageSelectionPageComponent implements OnInit { } } + previewLibraryFromAPI(picto:string){ + this.previewWithURL('url(' + picto + ')'); + } + cleanString(t: string) { return t.replace(/'/g, '\\\''); } @@ -195,6 +206,21 @@ export class ImageSelectionPageComponent implements OnInit { this.imageList = tempList.slice(0, 100); } + searchInLibApi(text: string){ + this.imageList = []; + let tempList: any[]; + clearUrlImageJS(); + if(this.configurationService.LANGUAGE_VALUE === 'FR'){ + monitorInput(text, "fra"); + }else{ + monitorInput(text, "eng"); + } + setTimeout(() => { + tempList = getUrlPicto(); + this.imageList = tempList[0].slice(0,100); + }, 500); + } + /*s can be 'inside' or 'border', used to open the corresponding color picker popup */ openDialogModifyInside() { this.dialog.open(DialogModifyColorInsideComponent, { diff --git a/src/app/services/board.service.ts b/src/app/services/board.service.ts index 1931024dc..22244eec7 100644 --- a/src/app/services/board.service.ts +++ b/src/app/services/board.service.ts @@ -280,7 +280,11 @@ export class BoardService { if (s.replace(/ /g, '') === '') { return ''; } - return this.sanitizer.bypassSecurityTrustStyle('url(\'' + s + '\')'); + if(path.Path.includes('http')){ + return s + }else{ + return this.sanitizer.bypassSecurityTrustStyle('url(\'' + s + '\')'); + } } else { return ''; } @@ -301,7 +305,7 @@ export class BoardService { if (s.replace(/ /g, '') === '') { return ''; } - return 'url(\'' + s + '\')'; + return 'url('+s+')'; } else { return ''; } diff --git a/src/assets/js/index.js b/src/assets/js/index.js new file mode 100644 index 000000000..db661b667 --- /dev/null +++ b/src/assets/js/index.js @@ -0,0 +1,385 @@ +var tokens = []; +var text = ""; +var languageSelect; +var sentenceInput; +var sentenceAlternative; +var textHighlights; +var inputSection; +var outputSection; +var meaningsList; +var pictoGroups; +var pictoSentence; +var loadingIndicator; +var uploadButton; +var trash; +var textUpdated = true; +var selectedMeanings = []; +var openTokens = {}; +var selectedLibrary; +var dragged; +var lang; +var mobile = false; +var urlImageJS = []; +var keyImageJS = []; +var tokensJS = []; +var dataJS = "Salut"; + +var internationalization = { + 'fra': { + 'sentence-input.placeholder': 'Saisissez le texte ici', + 'upload-translation.innerText': 'Envoyer l\'annotation', + 'missing-placholder.innerText': 'Signaler', + 'missing-token.innerText': 'Mot non reconnu', + 'missing-meaning.innerText': 'Sens manquant', + 'missing-pictogram.innerText': 'Pictogramme manquant', + 'missing-expression.innerText': 'Expression non reconnue', + '_missing-comment': 'Partie manquante: ', + '_missing-thanks': 'Merci de votre aide !', + '_network-error': 'Erreur liée au réseau' + }, + 'eng': { + 'sentence-input.placeholder': 'Enter text here', + 'upload-translation.innerText': 'Upload translation', + 'missing-placholder.innerText': 'Report', + 'missing-token.innerText': 'Unknown word', + 'missing-meaning.innerText': 'Missing meaning', + 'missing-pictogram.innerText': 'Missing pictogram', + 'missing-expression.innerText': 'Unknown expression', + '_missing-comment': 'Missing part: ', + '_missing-thanks': 'Thanks for your help!', + '_network-error': 'Network error' + } +}; + +// called when the user selects an option in the language menu +function changeLanguage() { + lang = languageSelect.value; + let texts = internationalization[lang]; + for (let i in texts) { + if (i.startsWith('_')) continue; + let id_and_prop = i.split('.'); + document.getElementById(id_and_prop[0])[id_and_prop[1]] = texts[i]; + } + textUpdated = true; +} + +function getTokens(tokens){ + tokensJS = tokens; +} + +function getTokensForTS(){ + return tokensJS; +} + +// called every 500ms +// this function sends the text field's content to the API +// for tokenization, as a first step of the translation process +function monitorInput(textInput, lang) { + if (!textUpdated) return; + let currentText = textInput.replace(/\n|\s{2,}/g, ' ').replace(/^\s/, ''); + sentenceInput = currentText; + text = currentText; + + this.resetResultPicto(); + this.tokenize(currentText, lang, tokenized); +} + +// called on api response with tokenization results +function tokenized(result) { + tokens = result.tokens; + // meaningsList.textContent = ""; + let len = selectedMeanings.length; + selectedMeanings.length = tokens.length; + selectedMeanings.fill(0, len); + let lastStop = 0; + for (let t in tokens) { + let meaning = tokens[t]; + let before = text.slice(lastStop, meaning.start); + let token = text.slice(meaning.start, meaning.stop); + tokens[t].text = token; + + lastStop = meaning.stop; + if (selectedMeanings[t] >= meaning.synsets.length) { + selectedMeanings[t] = 0; + } + getTokens(tokens); + } + refreshPictograms(); +} + +// called when a meaning is selected, pictograms +// get refreshed to match the selected meanings +function onMeaningSelection(e) { + let id = e.target.id.split('-'); + let t = parseInt(id[1]); + let s = parseInt(id[2]); + selectedMeanings[t] = s; + refreshPictograms(); +} + +// called when the pictogram list needs to be refreshed, +// either on user input or when meanings were received. +function refreshPictograms() { + let synsets = tokens.map((token, t) => { + let s = selectedMeanings[t]; + return token.synsets[0]; + }); + if (synsets.length > 0) { + this.pictograms(synsets,pictogramsReceived); + }else{ + tokens.map((token, t) => { + pictogramsFromName(token.text,lang); + }); + } +} + +// used by pictogramsReceived to sort pictograms by relevance, +// taking the current list of meanings into account. +function relevanceComparator(a, b) { + return b[0] - a[0]; +} + +// called when the API has found relevant pictograms +// for the selected meanings. This function will organize +// pictograms in "libraries". +function pictogramsReceived(pictograms) { + let expressions = {}; + for (let p in pictograms) { + let pictoData = pictograms[p]; + let count = pictoData.shift(); + let matches = pictoData.length; + let relevance = matches / count; + let indexes = pictoData.map(synsetIndex => synsetIndex.toString()); + indexes.sort(); + let key = indexes.join('-'); + if (key in expressions) expressions[key].push([relevance, p]); + else expressions[key] = [[relevance, p]]; + } + if (selectedLibrary === undefined || expressions[selectedLibrary] === undefined) { + selectedLibrary = Object.keys(expressions)[0]; + } + for (key in expressions) { + let pictograms = expressions[key]; + pictograms.sort(relevanceComparator); + let urlImage = []; + let keyImage = []; + for (let p in pictograms) { + let url = 'http://lig-interaactionpicto.imag.fr/api/' + pictograms[p][1]; + let picto = document.createElement('img'); + picto.src = url; + picto.draggable = true; + picto.dataset.key = key; + picto.dataset.url = url; + urlImage.push(url); + keyImage.push(key); + } + saveKeyPicto(keyImage); + saveUrlPicto(urlImage); + } +} +function saveKeyPicto(keyImage){ + keyImageJS.push(keyImage); +} + +function getKeyPicto(){ + return keyImageJS; +} + +function saveUrlPicto(urlImage){ + urlImageJS.push(urlImage); +} + +function getUrlPicto(){ + return urlImageJS; +} + +function clearUrlImageJS(){ + urlImageJS = []; +} + +function _phoneHome(path, callback, error) { + if (error === undefined) error = callback; + let xhr = new XMLHttpRequest(); + xhr.responseType = 'json'; + xhr.addEventListener('load', (e) => { + let xhr = e.target; + if (xhr.status == 200) callback(xhr.response); + else error(undefined, xhr.response); + }); + xhr.open('GET', 'http://lig-interaactionpicto.imag.fr/api/' + path.join('/')); + xhr.send(); +} + +function _encode(text) { + return encodeURIComponent(text); +} + +// PUBLIC ENDPOINTS +function tokenize(sentence, language, callback, error) { + let path = ['t2s', language, this._encode(sentence)]; + this._phoneHome(path, callback, error); +} + +// use the function pictogramsFormName from the file : startapi.js +function pictogramsFromName(sentence, language, callback, error) { + let path = ['t2p', language, this._encode(sentence)]; + this._phoneHome(path, callback, error); +} + +function pictograms(synsets, callback, error) { + let path = ['s2p', synsets.map(encodeURIComponent).join('+')]; + this._phoneHome(path, callback, error); +} + +function resetResultPicto(callback,error){ + let path = ['reset']; + this._phoneHome(path, callback, error); +} + +function mkdirJ(data,callback,error){ + let path = ['mkdirJS', dataJS]; + this._phoneHome(path, callback, error); +} + +function setDataTS(value){ + dataJS = value; + console.log(dataJS); +} + +// DIRECT DATA SEARCH +function findWordVariation(word, language, callback, error) { + let path = ['find-word-variation', language, this._encode(word)]; + this._phoneHome(path, callback, error); +} + +function findWordMeanings(word, language, callback, error) { + let path = ['find-word-meanings', language, this._encode(word)]; + this._phoneHome(path, callback, error); +} + +function findMeaningDefinition(word, language, callback, error) { + let path = ['find-meaning-definition', language, this._encode(word)]; + this._phoneHome(path, callback, error); +} + +// DIRECT DATA ACCESS +function wordsVariations(start, length, language, callback, error) { + let path = ['words-variations', language, start, length]; + this._phoneHome(path, callback, error); +} + +function wordsMeanings(start, length, language, callback, error) { + let path = ['words-meanings', language, start, length]; + this._phoneHome(path, callback, error); +} + +function meaningsDefinitions(start, length, language, callback, error) { + let path = ['meanings-definitions', language, start, length]; + this._phoneHome(path, callback, error); +} + +function stopList(language, callback, error) { + let path = ['stop-list', language]; + this._phoneHome(path, callback, error); +} + +// AUTHENTICATED ENDPOINTS +function feed(mobile, language, sentence, alt, pictograms, callback, error) { + let data = {mobile, language, sentence, alt, pictograms}; + let path = ['feed', 'anonymous', this._encode(JSON.stringify(data))]; + this._phoneHome(path, callback, error); +} + +function report(issue, language, sentence, comment, callback, error) { + let data = {issue, language, sentence, comment}; + let path = ['issue', 'anonymous', this._encode(JSON.stringify(data))]; + this._phoneHome(path, callback, error); +} + +// ADMIN ENDPOINTS +function summary(callback, error) { + let path = ['summary']; + this._phoneHome(path, callback, error); +} + +// set +function setWordVariation(variant, canonical, language, callback, error) { + let [key, value] = [this._encode(variant), this._encode(canonical)]; + let path = ['set-word-variation', language, key, value]; + this._phoneHome(path, callback, error); +} + +function setWordMeanings(word, synsets, language, callback, error) { + let [key, value] = [this._encode(word), this._encode(synsets.join('+'))]; + let path = ['set-word-synsets', language, key, value]; + this._phoneHome(path, callback, error); +} + +function setMeaningDefinition(synset, definition, language, callback, error) { + let [key, value] = [this._encode(synset), this._encode(definition)]; + let path = ['set-synset-definition', language, key, value]; + this._phoneHome(path, callback, error); +} + +function addStopWord(word, language, callback, error) { + let path = ['add-stop-word', language, this._encode(word)]; + this._phoneHome(path, callback, error); +} + +// del +function delWordVariation(variant, language, callback, error) { + let path = ['del-word-variation', language, this._encode(variant)]; + this._phoneHome(path, callback, error); +} + +function delWordMeanings(word, language, callback, error) { + let path = ['del-word-synsets', language, this._encode(word)]; + this._phoneHome(path, callback, error); +} + +function delMeaningDefinition(synset, language, callback, error) { + let path = ['del-synset-definition', language, this._encode(synset)]; + this._phoneHome(path, callback, error); +} + +function remStopWord(word, language, callback, error) { + let path = ['rem-stop-word', language, this._encode(word)]; + this._phoneHome(path, callback, error); +} + +// SESSIONS +function currentSessionId(callback, error) { + let path = ['current-session-id']; + this._phoneHome(path, callback, error); +} + +// contributions +function uploads(sessionId, callback, error) { + let path = ['storage', sessionId]; + this._phoneHome(path, callback, error); +} + +function issues(sessionId, callback, error) { + let path = ['issues', sessionId]; + this._phoneHome(path, callback, error); +} + +function updates(sessionId, callback, error) { + let path = ['updates', sessionId]; + this._phoneHome(path, callback, error); +} + +function revokeUpload(sessionId, timestamp, user, callback, error) { + let path = ['revoke-storage', sessionId, timestamp, user]; + this._phoneHome(path, callback, error); +} + +function revokeUpdate(sessionId, timestamp, user, callback, error) { + let path = ['revoke-update', sessionId, timestamp, user]; + this._phoneHome(path, callback, error); +} + +function revokeIssue(sessionId, timestamp, user, callback, error) { + let path = ['revoke-issue', sessionId, timestamp, user]; + this._phoneHome(path, callback, error); +} diff --git a/src/index.html b/src/index.html index 6c255bf16..45d596909 100644 --- a/src/index.html +++ b/src/index.html @@ -1,5 +1,6 @@ + AugCom