research_ui/mixins/research/selectTextMixin.js
2025-02-04 16:10:58 +03:30

442 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import apis from "~/apis/tahrirApi";
// import tinyTahrir from "assets/tahrir/vendors/tinymce-files/tinytahrir";
export default {
created() {
this.httpService = useNuxtApp()["$http"];
},
// beforeMount() {
// this.commentHttpService = new HttpService(
// import.meta.env.VITE_TAHRIR_BASE_URL
// );
// },
destroyed() {
// window.removeEventListener("scroll", this.handleScroll);
},
data() {
return {
id: "",
comments: [],
newComment: false,
selectedText: null,
globalGuid: undefined,
paragraphId: undefined,
paragraphParentId: undefined,
commentHttpService: undefined,
};
},
methods: {
/*
open comment form when user select a text
*/
onMouseUp($event) {
// Get selected text and encode it
const selection = encodeURIComponent(
this.getSelected().toString()
).replace(/[!'()*]/g);
if (selection) {
this.selectedText = selection;
this.setParagraphId($event);
// this.createPopup($event);
this.saveSelectedTextComment($event);
}
},
/*
extract user selected text
*/
getSelected() {
if (window.getSelection) {
return window.getSelection();
} else if (document.getSelection) {
return document.getSelection();
} else {
var selection = document.selection && document.selection.createRange();
if (selection.text) {
return selection.text;
}
return false;
}
},
setParagraphId(ev) {
this.paragraphId = ev.target.id;
this.paragraphParentId = ev.target.parentNode?.id;
},
/*
create comment popup and place it on top fo the selected text;
*/
createPopup(event) {
// this.openCommentForm();
// Get cursor position
// const posX = event.clientX - 130;
// const posY = event.clientY - 200;
// const posX = event.clientX;
// const posY = event.clientY;
// try {
// this.$refs.popupMenu.$el.style.top = posY + "px";
// this.$refs.popupMenu.$el.style.left = posX + "px";
// this.$refs.popupMenu.$el.style.opacity = 1;
// }
// catch(err) {
// setTimeout(() => {
// this.$refs.popupMenu.$el.style.top = posY + "px";
// this.$refs.popupMenu.$el.style.left = posX + "px";
// this.$refs.popupMenu.$el.style.opacity = 1;
// }, 700);
// }
},
openCommentForm() {
this.newComment = true;
},
/*
summary: save selected text
description: replace selected text with the new span.tcomment tag and then save it.
send a new request to api for saving the comment.
then update/replace paragraph content with newely created content.
then crawle all paragraphs for .tcomment ids.
then request api for paragraphs comments.
@fires when user clicked on comment popup save button.
@param {userComment} Html user comment.
@return void.
*/
saveSelectedTextComment(userComment) {
const url = tahrirUrl()+ apis.comments.addCommentToSelectedParag;
const replacedContent = this.replaceSelectedText();
const payload = {
content: replacedContent,
guid: this.paragraphId,
};
this.commentHttpService
.formDataRequest(url, payload)
.then((res) => {
this.newComment = false;
this.addComment(
{
text: userComment,
pid: this.globalGuid,
},
replacedContent
);
})
.catch((err) => {
mySwalToast({
html: err?.message,
});
})
.finally(() => (this.loading = false));
},
/*
summary: save comment
description:
send a new request to api for saving the comment.
then update/replace paragraph content with newely created content.
then crawle all paragraphs for .tcomment ids.
then request api for paragraphs comments.
@fires when saveSelectedTextComment method completed...
@param {text} String user comment.
@param {pid} String globalGuid.
@param {replacedContent} Html user comment.
@return void.
*/
addComment({ text, pid }, replacedContent) {
const payload = { text: JSON.stringify(text) };
const url = tahrirUrl()+ apis.comments.add + "/" + pid;
this.commentHttpService.formDataRequest(url, payload).then((res) => {
this.updateParagraphContentProperty(replacedContent);
this.crawlParagsForCommentId(this.localParagraphs).then((guidList) => {
this.getComments(guidList);
});
mySwalToast({
html: res.message,
});
});
},
/*
summary: update paragraph content property
description: update paragraph old content property with updated content.
@fires when addComment method completed...
@param {replacedContent} Html user comment.
@return void.
*/
updateParagraphContentProperty(replacedContent) {
this.localParagraphs.forEach((pars) => {
if (pars.id == this.paragraphParentId.slice(7))
pars.content = replacedContent;
});
},
/*
summary: update new comment form position
description: update position of new comment form when scrolling.
@fires when user scroll
@param {event} event window scroll event.
@return void.
*/
updateCommentFormTopPosition(event) {
// Find out how much (if any) user has scrolled
var scrollTop =
window.screenY !== undefined
? window.screenY
: (
document.documentElement ||
document.body.parentNode ||
document.body
).scrollTop;
const posY = event.clientY - 200 + scrollTop;
this.$refs.popupMenu._vnode.elm.style.top = posY + "px";
},
/*
summary: comment list
description: getting list of comments from the api.
@fires after page crawl for tcomment complete.
@param {array} guidList paragraph's comment ids.
@return void.
*/
getComments(guidList) {
const payload = {
pids: guidList,
};
this.commentHttpService
.formDataRequest(tahrirUrl()+apis.comments.documentComment, payload)
.then((res) => {
if (res.data) this.attachCommentsToParags(res.data);
});
},
/*
summary: attach comments to paragraphs object
description: loop over paragraphs and find paragraphs that
its content property math the comment id catched from the api.
@fires after getComment method called.
@param {object}
@return void.
*/
attachCommentsToParags(comments) {
try {
this.localParagraphs.forEach((parag) => {
const commentList = [];
Object.keys(comments).forEach((value) => {
if (parag.content.includes(value))
commentList.push(comments[value]);
});
this.$set(parag, "comments", commentList);
});
} catch (err) {
this.comments = comments;
}
this.fetchingData = false;
},
/*
summary: replace selected text with new span.
description: find the parent paragraph with its refs and
then replace the selected text with new span.
@fires when saveSelectedTextComment method called.
@return Html.
*/
replaceSelectedText() {
const text = decodeURIComponent(this.selectedText);
this.globalGuid = this.newGuid();
const replacementTag = `<mark id="${this.globalGuid}" class="c01206 c01172 c01173"><span class="c01186" id="19">${text} </span> <span class="c01210 c01209">ORG<span class="c01174">×</span></span></mark>`;
const refContent = this.$refs[this.paragraphParentId];
refContent.innerHTML = refContent.innerHTML.replace(text, replacementTag);
return refContent.innerHTML;
},
closeCommentForm() {
this.newComment = false;
},
/*
summary: crawl paragraphs for .tcomment.
description: crawl paragraph fot .tcomment tags and getting its id.
@fires when addComment method called.
@return Html.
*/
async crawlParagsForCommentId(pars) {
return await pars
.map((par) => {
if (par?.content.includes("tcomment"))
return this.geCommenttIds(par.content);
})
.filter((item) => item)
.join(",");
},
/*
summary: create unique id.
@fires when crawlParagsForCommentId method called.
@return string.
*/
newGuid() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
/[xy]/g,
function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
}
);
},
/*
summary: separate spans id property..
@fires when crawlParagsForCommentId method called.
@return string.
*/
geCommenttIds(content) {
var ids = "";
var regex =
/\<span id=\\{0,1}\"([a-z0-9-]*?)\\{0,1}\" class=\\{0,1}\"tcomment\\{0,1}\".*?\>/g;
var m = regex.exec(content);
while (m !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if (ids != "") ids += ",";
ids += m[1];
m = regex.exec(content);
}
return ids;
},
handleScroll(event) {
// Any code to be executed when the window is scrolled
},
/*
summary: hide/show other paragraphs comments.
description: when user clicked on comment, hide other
comments and add active class to .tcomment tag in the paragraph
@fires when user open/close the comments.
@param {object} paragraph
@param {Number} commentIndex
@return void.
*/
hideOtherComments({ paragraph, commentIndex }) {
const _this = this;
_this.localParagraphs.forEach((par) => {
// parse other paragraphs
if (par.id != paragraph.id) {
if (par.comments?.length) {
par.comments.forEach((com, index) => {
const res = com.isShow == undefined ? false : !com.isShow;
_this.$set(par.comments[index], "isShow", res);
});
}
}
// parse selected paragraph
else {
if (par.comments?.length) {
par.comments.forEach((com, index) => {
// hide sibling comments in the active paragraph.
if (index != commentIndex) {
const res = com.isShow == undefined ? false : !com.isShow;
_this.$set(par.comments[index], "isShow", res);
}
// toggle paragraph .tcomment class
else {
Array.from(
_this.$refs["parent-" + par.id][0].children[0].children
).forEach((child) => {
if (child.id == com[0].pid) {
child.classList.toggle("active");
}
});
}
});
}
}
});
},
removSpan(comment) {
const spanTag = document.getElementsById(comment.pid);
const rawText = spanTag.innerHTML;
this.paragraph.content = this.paragraph.content.replace(spanTag, rawText);
return this.paragraph.content;
},
saveChangeSelectedTextComment(userComment) {
const url = apis.comments.addCommentToSelectedParag;
const replacedContent = this.removSpan();
const payload = {
content: replacedContent,
guid: this.paragraphId,
};
ApiService.formData(url, payload)
.then((res) => {
this.newComment = false;
this.addComment(
{
text: userComment,
pid: this.globalGuid,
},
replacedContent
);
})
.catch((err) => {
mySwalToast({
html: err?.message,
});
})
.finally(() => (this.loading = false));
},
},
};