Commit 4cd56bed authored by dindigala's avatar dindigala
Browse files

chore: remove annotation mixin

parent 6df91e92
Pipeline #268609 passed with stages
in 3 minutes and 39 seconds
......@@ -44,7 +44,7 @@
</template>
<script>
import Annotation from '@/mixins/annotation';
import * as AnnotationUtils from '@/utils';
import { colors } from 'quasar';
import treestore from '@/stores/treestore.js';
import Header from '@/components/header.vue';
......@@ -57,7 +57,6 @@ export default {
Header,
},
mixins: [
Annotation,
Panels,
],
data() {
......@@ -169,7 +168,7 @@ export default {
);
if (current.annotationPage.items.length) {
this.$store.dispatch('annotations/annotationLoaded', current.annotationPage.items.map((x) => ({ ...x, targetId: this.stripTargetId(x, true) })));
this.$store.dispatch('annotations/annotationLoaded', current.annotationPage.items.map((x) => ({ ...x, targetId: AnnotationUtils.stripTargetId(x, true) })));
} else {
this.$store.dispatch('annotations/annotationLoaded', []);
}
......
......@@ -58,7 +58,6 @@
<script>
import * as Icons from '@quasar/extras/fontawesome-v5';
import Annotation from '@/mixins/annotation';
import AnnotationToggles from '@/components/annotations/toggles.vue';
import AnnotationList from '@/components/annotations/list.vue';
import AnnotationOptions from '@/components/annotations/options.vue';
......@@ -69,6 +68,7 @@ import Notification from '@/components/notification.vue';
import {
getAnnotationTabs, createTooltip, backTrackNestedAnnotations, isAnnotationSelected, getAnnotationIcon, onlyIf,
} from '@/utils';
import * as AnnotationUtils from '@/utils';
import DomMixin from '@/mixins/dom';
export default {
......@@ -80,7 +80,7 @@ export default {
Loading,
Notification,
},
mixins: [Annotation, DomMixin],
mixins: [DomMixin],
props: {
config: {
type: Object,
......@@ -175,7 +175,7 @@ export default {
watch: {
currentTab: {
handler() {
this.highlightActiveContent(this.filteredAnnotations);
AnnotationUtils.highlightActiveContent(this.filteredAnnotations);
this.handleTooltip();
},
},
......@@ -215,7 +215,7 @@ export default {
addAnnotation(annotation) {
this.$store.dispatch('annotations/addActiveAnnotation', annotation);
let selector = this.stripTargetId(annotation, false);
let selector = AnnotationUtils.stripTargetId(annotation, false);
if (selector.startsWith('.')) {
selector = selector.replace(/\./g, '');
......@@ -223,7 +223,7 @@ export default {
const el = document.getElementById(selector) || document.querySelector(`.${selector}`);
this.updateHighlightState(selector, 'INC');
AnnotationUtils.updateHighlightState(selector, 'INC');
if (el) {
this.addIcon(el, annotation);
}
......@@ -250,7 +250,7 @@ export default {
const svg = getAnnotationIcon(contentType, this.config.annotations.types);
svg.setAttribute(
'data-annotation-icon',
this.stripTargetId(annotation),
AnnotationUtils.stripTargetId(annotation),
);
element.prepend(svg);
} catch (err) {
......@@ -270,7 +270,7 @@ export default {
handleTooltip() {
const annotationIds = this.filteredAnnotations.reduce((prev, curr) => {
let id = this.stripTargetId(curr, false);
let id = AnnotationUtils.stripTargetId(curr, false);
if (id.startsWith('.')) {
id = id.replace('.', '');
}
......@@ -318,7 +318,7 @@ export default {
return;
}
this.highlightActiveContent(this.filteredAnnotations);
AnnotationUtils.highlightActiveContent(this.filteredAnnotations);
this.handleTooltip();
} catch (err) {
......@@ -352,7 +352,7 @@ export default {
},
removeIcon(annotation) {
const stripeId = this.stripTargetId(annotation);
const stripeId = AnnotationUtils.stripTargetId(annotation);
const el = document
.querySelector(`svg[data-annotation-icon='${stripeId}']`);
......@@ -366,13 +366,13 @@ export default {
return;
}
this.$store.dispatch('annotations/removeActiveAnnotation', annotation);
let selector = this.stripTargetId(annotation, false);
let selector = AnnotationUtils.stripTargetId(annotation, false);
if (selector.startsWith('.')) {
selector = selector.replace(/\./g, '');
}
this.updateHighlightState(selector, 'DEC', level);
AnnotationUtils.updateHighlightState(selector, 'DEC', level);
this.removeIcon(annotation);
},
......
......@@ -78,7 +78,6 @@
<script>
import { fasSearchPlus, fasSearchMinus } from '@quasar/extras/fontawesome-v5';
import Annotation from '@/mixins/annotation';
import DomMixin from '@/mixins/dom';
import Loading from '@/components/loading.vue';
import Notification from '@/components/notification.vue';
......@@ -92,7 +91,7 @@ export default {
Loading,
Notification,
},
mixins: [Annotation, DomMixin],
mixins: [DomMixin],
props: {
config: {
type: Object,
......
export default {
computed: {
tidoConfig() {
return JSON.parse(document.getElementById('tido-config').text);
},
},
methods: {
getAllElementsFromSelector(selector, arr = []) {
const el = document.getElementById(selector);
if (el) {
// https://www.geeksforgeeks.org/queue-data-structure/
const queue = [];
queue.push(el);
while (queue.length) {
const popped = queue.pop();
arr.push(popped);
[...popped.children].forEach((child) => {
queue.push(child);
});
}
return arr;
}
return [...document.querySelectorAll(`.${selector}`)];
},
getElementById(id) {
if (!id) {
return null;
}
return document.getElementById(id.replace(/#/g, ''));
},
getNewLevel(element, operation) {
const currentLevel = parseInt(
element.getAttribute('data-annotation-level'),
10,
);
if (operation === 'INC') {
return currentLevel + 1;
}
if (currentLevel !== 0) {
return currentLevel - 1;
}
return currentLevel;
},
addHighlightToTargetIds(selector, root) {
const element = root.getElementById(selector);
if (!element) {
return;
}
function recursiveAddClass(children) {
[...children].forEach((child) => {
child.setAttribute('data-annotation', true);
child.classList.add(selector);
recursiveAddClass(child.children);
});
}
if (!element.children.length) {
element.setAttribute('data-annotation', true);
element.classList.add(selector);
}
recursiveAddClass(element.children);
},
highlightActiveContent(annotations) {
annotations.forEach((el) => {
if (el.target.id) {
this.highlightActiveId(this.getElementById(this.stripTargetId(el, false)));
} else {
[...document.querySelectorAll(`.${this.stripTargetId(el)}`)].map((x) => x.setAttribute('data-annotation-level', 0));
}
});
},
highlightActiveId(element) {
if (!element) {
return;
}
element.setAttribute('data-annotation-level', 0);
[...element.children].forEach((child) => {
child.setAttribute('data-annotation-level', 0);
this.highlightActiveId(child);
});
},
replaceSelectorWithSpan(selector, root) {
const start = root.querySelector(`[data-target="${selector}_start"]`);
const end = root.querySelector(`[data-target="${selector}_end"]`);
let started = false;
let ended = false;
function replaceRecursive(element) {
if (!element.childNodes) return;
[...element.childNodes].forEach((childNode) => {
if (childNode === start) started = true;
if (childNode === end) ended = true;
if (ended) return;
if (childNode.nodeName === 'SPAN' && childNode.getAttribute('data-annotation') && started) {
childNode.classList.add(selector);
}
if (childNode.nodeName === '#text') {
if (started) {
if (childNode.textContent && childNode.textContent.trim()) {
const span = document.createElement('span');
span.setAttribute('class', selector);
span.setAttribute('data-annotation', true);
span.innerHTML = childNode.textContent;
childNode.replaceWith(span);
}
}
} else {
replaceRecursive(childNode);
}
});
}
replaceRecursive(root);
return root;
},
/**
* get the annotation id of the current item
*
* @param string url
* @return string
*/
stripAnnotationId(url) {
if (!url) {
return '';
}
return url.split('/').pop();
},
stripId(val) {
if (!val) {
return '';
}
return val.replace(/-/g, '.').replace(/[^.0-9]/g, '');
},
stripSelector(annotation) {
return `.${annotation.target.selector.startSelector.value
.replace("span[data-target='", '')
.replace("'", '')
.replace('_start]', '')
.replace('_end]', '')}`;
},
stripTargetId(annotation, removeDot = true) {
let output = '';
if (annotation.target.selector) {
output = this.stripSelector(annotation);
} else {
output = this.stripAnnotationId(annotation.target.id);
}
return removeDot ? output.replace(/\./g, '') : output;
},
toggleAnnotationSelector(annotation, text = 'toggle') {
const id = annotation.target.selector.startSelector.value
.replace("span[data-target='", '')
.replace("_start']", '');
const isOverlap = id.includes('Overlap');
[...document.querySelectorAll(`.${id}`)].forEach((el) => this.updateTextContentClass(
el,
text,
isOverlap ? 'annotation-disabled-overlap' : 'annotation-disabled',
));
},
updateHighlightState(selector, operation, level) {
this.getAllElementsFromSelector(selector).forEach((el) => el.setAttribute(
'data-annotation-level',
level || this.getNewLevel(el, operation),
));
},
updateTextContentClass(element, task = 'add', ...className) {
if (!element) {
return;
}
element.classList[task](...className);
},
},
};
......@@ -15,7 +15,7 @@ Vue.use(Vuex);
*/
export default function TidoStore(/* { ssrContext } */) {
const Store = new Vuex.Store({
const store = new Vuex.Store({
modules: {
annotations,
},
......@@ -25,5 +25,5 @@ export default function TidoStore(/* { ssrContext } */) {
strict: process.env.DEBUGGING,
});
return Store;
return store;
}
......@@ -2,13 +2,75 @@ import * as Icons from '@quasar/extras/fontawesome-v5';
// utility functions that we can use as generic way for perform tranformation on annotations.
export function addHighlightToTargetIds(selector, root) {
const element = root.getElementById(selector);
if (!element) {
return;
}
function recursiveAddClass(children) {
[...children].forEach((child) => {
child.setAttribute('data-annotation', true);
child.classList.add(selector);
recursiveAddClass(child.children);
});
}
if (!element.children.length) {
element.setAttribute('data-annotation', true);
element.classList.add(selector);
}
recursiveAddClass(element.children);
}
export function replaceSelectorWithSpan(selector, root) {
const start = root.querySelector(`[data-target="${selector}_start"]`);
const end = root.querySelector(`[data-target="${selector}_end"]`);
let started = false;
let ended = false;
function replaceRecursive(element) {
if (!element.childNodes) return;
[...element.childNodes].forEach((childNode) => {
if (childNode === start) started = true;
if (childNode === end) ended = true;
if (ended) return;
if (childNode.nodeName === 'SPAN' && childNode.getAttribute('data-annotation') && started) {
childNode.classList.add(selector);
}
if (childNode.nodeName === '#text') {
if (started) {
if (childNode.textContent && childNode.textContent.trim()) {
const span = document.createElement('span');
span.setAttribute('class', selector);
span.setAttribute('data-annotation', true);
span.innerHTML = childNode.textContent;
childNode.replaceWith(span);
}
}
} else {
replaceRecursive(childNode);
}
});
}
replaceRecursive(root);
return root;
}
export function addHighlighterAttributes(dom) {
this.mapUniqueElements(
this.findDomElements('[data-target]:not([value=""])', dom),
(x) => x.getAttribute('data-target').replace('_start', '').replace('_end', ''),
).forEach((selector) => this.replaceSelectorWithSpan(selector, dom));
).forEach((selector) => replaceSelectorWithSpan(selector, dom));
this.mapElements(this.findDomElements('[id]', dom), (x) => x.getAttribute('id')).forEach((selector) => this.addHighlightToTargetIds(selector, dom));
this.mapElements(this.findDomElements('[id]', dom), (x) => x.getAttribute('id')).forEach((selector) => addHighlightToTargetIds(selector, dom));
}
export function getAnnotationContentIds(dom) {
......@@ -126,6 +188,131 @@ export function createTooltip(element, annotationClasses, config) {
setTimeout(() => tooltipEl.classList.add('annotation-animated-tooltip'), 10);
}
export function getElementById(id) {
if (!id) {
return null;
}
return document.getElementById(id.replace(/#/g, ''));
}
export function getNewLevel(element, operation) {
const currentLevel = parseInt(
element.getAttribute('data-annotation-level'),
10,
);
if (operation === 'INC') {
return currentLevel + 1;
}
if (currentLevel !== 0) {
return currentLevel - 1;
}
return currentLevel;
}
export function highlightActiveId(element) {
if (!element) {
return;
}
element.setAttribute('data-annotation-level', 0);
[...element.children].forEach((child) => {
child.setAttribute('data-annotation-level', 0);
highlightActiveId(child);
});
}
export function stripId(val) {
if (!val) {
return '';
}
return val.replace(/-/g, '.').replace(/[^.0-9]/g, '');
}
export function stripSelector(annotation) {
return `.${annotation.target.selector.startSelector.value
.replace("span[data-target='", '')
.replace("'", '')
.replace('_start]', '')
.replace('_end]', '')}`;
}
export function stripAnnotationId(url) {
if (!url) {
return '';
}
return url.split('/').pop();
}
export function stripTargetId(annotation, removeDot = true) {
let output = '';
if (annotation.target.selector) {
output = stripSelector(annotation);
} else {
output = stripAnnotationId(annotation.target.id);
}
return removeDot ? output.replace(/\./g, '') : output;
}
export function updateTextContentClass(element, task = 'add', ...className) {
if (!element) {
return;
}
element.classList[task](...className);
}
export function toggleAnnotationSelector(annotation, text = 'toggle') {
const id = annotation.target.selector.startSelector.value
.replace("span[data-target='", '')
.replace("_start']", '');
const isOverlap = id.includes('Overlap');
[...document.querySelectorAll(`.${id}`)].forEach((el) => updateTextContentClass(
el,
text,
isOverlap ? 'annotation-disabled-overlap' : 'annotation-disabled',
));
}
export function highlightActiveContent(annotations) {
annotations.forEach((el) => {
if (el.target.id) {
highlightActiveId(getElementById(stripTargetId(el, false)));
} else {
[...document.querySelectorAll(`.${stripTargetId(el)}`)].map((x) => x.setAttribute('data-annotation-level', 0));
}
});
}
export function getAllElementsFromSelector(selector, arr = []) {
const el = document.getElementById(selector);
if (el) {
// https://www.geeksforgeeks.org/queue-data-structure/
const queue = [];
queue.push(el);
while (queue.length) {
const popped = queue.pop();
arr.push(popped);
[...popped.children].forEach((child) => {
queue.push(child);
});
}
return arr;
}
return [...document.querySelectorAll(`.${selector}`)];
}
export function updateHighlightState(selector, operation, level) {
this.getAllElementsFromSelector(selector).forEach((el) => el.setAttribute(
'data-annotation-level',
level || getNewLevel(el, operation),
));
}
export const backTrackNestedAnnotations = (el, classNames = []) => {
let current = el;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment