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 = `${text} ORG×`; 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 = /\/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)); }, }, };