mirror of
https://github.com/ArenMg/aren.git
synced 2024-12-23 07:19:46 +00:00
FIrst version carto debat
This commit is contained in:
parent
bb0168d25d
commit
77f9c7a6a3
11 changed files with 713 additions and 593 deletions
|
@ -21,11 +21,11 @@ const router = new VueRouter({
|
||||||
{path: '/votemajoritaire', component: vueLoader('views/votemajoritaire')},
|
{path: '/votemajoritaire', component: vueLoader('views/votemajoritaire')},
|
||||||
{path: '/createVoteMajoritaire', component: vueLoader('views/createVoteMajoritaire')},
|
{path: '/createVoteMajoritaire', component: vueLoader('views/createVoteMajoritaire')},
|
||||||
{path: '/votemajoritairedetails', component: vueLoader('views/votemajoritairedetails')},
|
{path: '/votemajoritairedetails', component: vueLoader('views/votemajoritairedetails')},
|
||||||
{path: '/carto', component: vueLoader('views/cartoTest')},
|
|
||||||
{path: '/creation-debats-sur-cartes', component: vueLoader('views/createDebateSurCarte')},
|
{path: '/creation-debats-sur-cartes', component: vueLoader('views/createDebateSurCarte')},
|
||||||
{path: '/documents-debats-sur-cartes', component: vueLoader('views/documentsDebatesSurCarte')},
|
{path: '/documents-debats-sur-cartes', component: vueLoader('views/documentsDebatesSurCarte')},
|
||||||
{path: '/documents-debats-sur-cartes/:id', component: vueLoader('views/documentDebatesSurCarte')},
|
{path: '/documents-debats-sur-cartes/:id', component: vueLoader('views/documentDebatesSurCarte')},
|
||||||
{path: '/debats-sur-cartes', component: vueLoader('views/debatesSurCarte')},
|
{path: '/debats-sur-cartes', component: vueLoader('views/debatesSurCarte')},
|
||||||
|
{path: '/debats-sur-cartes/:id', component: vueLoader('views/debateSurCarte')},
|
||||||
{path: '*', component: vueLoader('views/404')},
|
{path: '*', component: vueLoader('views/404')},
|
||||||
]
|
]
|
||||||
});
|
});
|
|
@ -120,11 +120,13 @@ function Entity(obj = {}) {
|
||||||
|
|
||||||
function Category(obj = {}) {
|
function Category(obj = {}) {
|
||||||
Entity.call(this, obj);
|
Entity.call(this, obj);
|
||||||
|
this.type = DebateType.BASIC;
|
||||||
}
|
}
|
||||||
Category.prototype.attrs = {
|
Category.prototype.attrs = {
|
||||||
id: Number,
|
id: Number,
|
||||||
name: String,
|
name: String,
|
||||||
picture: String,
|
picture: String,
|
||||||
|
type: String,
|
||||||
debatesCount: Number,
|
debatesCount: Number,
|
||||||
lastCommentDate: Date,
|
lastCommentDate: Date,
|
||||||
};
|
};
|
||||||
|
@ -134,10 +136,15 @@ Category.prototype.oneToMany = {
|
||||||
Category.prototype.debates = function () {
|
Category.prototype.debates = function () {
|
||||||
return this.documents.map((document) => document.debates).flat();
|
return this.documents.map((document) => document.debates).flat();
|
||||||
};
|
};
|
||||||
|
Category.prototype.is = function (type) {
|
||||||
|
return DebateType._value[this.type] == DebateType._value[type];
|
||||||
|
}
|
||||||
|
|
||||||
function Document(obj = {}) {
|
function Document(obj = {}) {
|
||||||
Entity.call(this, obj);
|
Entity.call(this, obj);
|
||||||
this.type = DebateType.BASIC;
|
this.type = DebateType.BASIC;
|
||||||
|
this.meshLine = 1;
|
||||||
|
this.meshColumn = 1;
|
||||||
}
|
}
|
||||||
Document.prototype.attrs = {
|
Document.prototype.attrs = {
|
||||||
id: Number,
|
id: Number,
|
||||||
|
|
|
@ -102,7 +102,7 @@ const messages = {
|
||||||
guests: "Invité⋅e⋅s",
|
guests: "Invité⋅e⋅s",
|
||||||
debates: "Débats",
|
debates: "Débats",
|
||||||
new_debate: "Nouveau débat",
|
new_debate: "Nouveau débat",
|
||||||
new_map_debate: "Nouveau débat sur caarte",
|
new_map_debate: "Nouveau débat sur carte",
|
||||||
invite_in_debate: "Inviter au débat",
|
invite_in_debate: "Inviter au débat",
|
||||||
SUPERADMIN: "Super administrateur⋅rice",
|
SUPERADMIN: "Super administrateur⋅rice",
|
||||||
SUPERADMIN: "Root",
|
SUPERADMIN: "Root",
|
||||||
|
|
|
@ -1,569 +0,0 @@
|
||||||
<template>
|
|
||||||
<base-layout id="debate">
|
|
||||||
<template v-slot:title>
|
|
||||||
<h1>Debat sur carte</h1>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- ************************************************* -->
|
|
||||||
|
|
||||||
<div id="mainContainer">
|
|
||||||
<div id="documentContainer">
|
|
||||||
<div class="scroll-area" @scroll="hidePopup(); updateSpaghettis();" ref="documentContainer">
|
|
||||||
<div class="wrap">
|
|
||||||
<documented v-bind:value="$t('documentation.debate_document')">
|
|
||||||
<div id="mapContainer">
|
|
||||||
<img
|
|
||||||
id="mapImg"
|
|
||||||
src="assets/img/mapimg.png"
|
|
||||||
alt="map image"
|
|
||||||
/>
|
|
||||||
<table id="myTable">
|
|
||||||
<tr
|
|
||||||
v-for="(_, indexOfLine) in nbOfLines"
|
|
||||||
:key="indexOfLine"
|
|
||||||
>
|
|
||||||
<td
|
|
||||||
v-for="(_, indexOfColumn) in nbOfColumns"
|
|
||||||
:key="indexOfColumn"
|
|
||||||
@click="select(indexOfLine, indexOfColumn)"
|
|
||||||
:class="{
|
|
||||||
selected: arrayData[indexOfLine][indexOfColumn],
|
|
||||||
}"
|
|
||||||
></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</documented>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="commentsContainer" v-bind:class="{ loading: mountedChildren < debate.commentsCount }"/>
|
|
||||||
<div
|
|
||||||
class="scroll-area"
|
|
||||||
@scroll="
|
|
||||||
hidePopup();
|
|
||||||
updateSpaghettis();
|
|
||||||
"
|
|
||||||
ref="commentsContainer"
|
|
||||||
>
|
|
||||||
<comment-widget
|
|
||||||
v-for="comment in filteredComments"
|
|
||||||
v-show="displayableComments[comment.id]"
|
|
||||||
v-bind:displayable-comments="displayableComments"
|
|
||||||
v-bind:key="comment.id"
|
|
||||||
v-bind:comment="comment"
|
|
||||||
v-bind:search="search"
|
|
||||||
v-bind:hide-on-scroll="true"
|
|
||||||
@tag-edition="editTags($event)"
|
|
||||||
@selection-end="selectionHandler($event)"
|
|
||||||
@focus-me="selectComment($event); scrollToComment($event)"
|
|
||||||
@select-me="selectComment($event)"
|
|
||||||
@highlight-me="highlight($event)"
|
|
||||||
@mounted="mountChild($event)"
|
|
||||||
@report="reportComment($event)"
|
|
||||||
@collapse="collapseComment($event)">
|
|
||||||
</comment-widget>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-show="selectedRange"
|
|
||||||
class="selection_popup z-depth-2"
|
|
||||||
ref="arguePopup"
|
|
||||||
v-bind:style="'top: ' + popup.y + 'px; left: ' + popup.x + 'px;'"
|
|
||||||
@click="createComment()"
|
|
||||||
@mousedown.stop=""
|
|
||||||
@mouseup.stop=""
|
|
||||||
>
|
|
||||||
{{ $t("argue").toLowerCase() }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-slot:addons>
|
|
||||||
<comment-modal
|
|
||||||
id="commentModal"
|
|
||||||
ref="commentModal"
|
|
||||||
v-bind:comment="newComment">
|
|
||||||
</comment-modal>
|
|
||||||
|
|
||||||
<tags-modal ref="tagModal">
|
|
||||||
</tags-modal>
|
|
||||||
</template>
|
|
||||||
</base-layout>
|
|
||||||
</template>
|
|
||||||
<style scoped>
|
|
||||||
table,
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
#mapContainer {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
#mapImg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.selected {
|
|
||||||
background-color: rgb(0 0 0 / 26%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
module.exports = {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
nbOfLines: 5,
|
|
||||||
nbOfColumns: 9,
|
|
||||||
arrayData: false,
|
|
||||||
// ********************************
|
|
||||||
|
|
||||||
debate: false,
|
|
||||||
newComment: false,
|
|
||||||
sortByPosition: false,
|
|
||||||
popup: { x: -9999, y: -9999 },
|
|
||||||
displayableComments: {},
|
|
||||||
selectedRange: false,
|
|
||||||
search: "",
|
|
||||||
mountedChildren: 0,
|
|
||||||
leftDisplay: "document",
|
|
||||||
scrapsMagicNumber: 2,
|
|
||||||
spaghettiOver: "",
|
|
||||||
spaghettiCountDown: 0,
|
|
||||||
scraps: [],
|
|
||||||
themes: [],
|
|
||||||
themeInput: "",
|
|
||||||
themeHelper: false,
|
|
||||||
|
|
||||||
// ****************************************
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.initDataArray();
|
|
||||||
this.fetchData();
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
filteredComments() {
|
|
||||||
return this.debate.comments ? this.debate.comments.filter((comment) => !comment.parent) : []
|
|
||||||
},
|
|
||||||
documentAsDom() {
|
|
||||||
let div = document.createElement("div");
|
|
||||||
div.innerHTML = this.debate.document.content;
|
|
||||||
return div;
|
|
||||||
},
|
|
||||||
spaghettiData() {
|
|
||||||
if (this.leftDisplay === "theme") {
|
|
||||||
return this.themes;
|
|
||||||
} else {
|
|
||||||
return this.scraps;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route'() {
|
|
||||||
this.fetchData();
|
|
||||||
if (this.$route.query.comment) {
|
|
||||||
this.scrollToComment(this.$route.query.comment);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
debate() {
|
|
||||||
this.displayableComments = {};
|
|
||||||
//This is to make all the properties reactive
|
|
||||||
this.debate.comments.forEach((comment) => Vue.set(this.displayableComments, comment.id, true));
|
|
||||||
},
|
|
||||||
leftDisplay(val) {
|
|
||||||
if (val === 'scraps') {
|
|
||||||
this.fetchScraps();
|
|
||||||
}
|
|
||||||
if (val !== 'document') {
|
|
||||||
this.updateSpaghettis();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// search(val) {
|
|
||||||
// if (this.leftDisplay === 'scraps' || this.leftDisplay === 'theme') {
|
|
||||||
// this.updateSpaghettis();
|
|
||||||
// }
|
|
||||||
// this.updateDisplayableComments();
|
|
||||||
// },
|
|
||||||
sortByPosition(val) {
|
|
||||||
if (val) {
|
|
||||||
this.debate.deepSortComments((a, b) => a.compareBoundaryPoints(Range.START_TO_START, b));
|
|
||||||
} else {
|
|
||||||
this.debate.deepSortComments((a, b) => (a.created > b.created) ? 1 : -1);
|
|
||||||
}
|
|
||||||
if (this.leftDisplay !== 'document') {
|
|
||||||
// Sort by disply position
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.spaghettiData.forEach((s) => {
|
|
||||||
s.comments.sort((a, b) => {
|
|
||||||
let divA = document.getElementById("comment_" + a.id);
|
|
||||||
let divB = document.getElementById("comment_" + b.id);
|
|
||||||
return divA.offsetTop - divB.offsetTop
|
|
||||||
});
|
|
||||||
})
|
|
||||||
});
|
|
||||||
this.updateSpaghettis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/******************* */
|
|
||||||
fetchData() {
|
|
||||||
ArenService.Debates.get({
|
|
||||||
id: 3,
|
|
||||||
onSuccess: (debate) => {
|
|
||||||
this.debate = debate;
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
this.$router.push("/404");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fetchScraps() {
|
|
||||||
ArenService.Debates.getScraps({
|
|
||||||
id: this.debate.id,
|
|
||||||
onSuccess: (result) => {
|
|
||||||
if (result.length > 0) {
|
|
||||||
let arrLength = result.map(r => r.comments.length).sort();
|
|
||||||
let minLength = arrLength[arrLength.length - this.scrapsMagicNumber];
|
|
||||||
result = result.filter(s => s.comments.length > 1 && s.comments.length >= minLength);
|
|
||||||
result.forEach((s, i) => {
|
|
||||||
s.id = i;
|
|
||||||
s.comments = s.comments.map(cid => ArenService.Store.get(cid, Comment));
|
|
||||||
let range = new Range();
|
|
||||||
range.setPathStart(this.documentAsDom, s.startContainer, s.startOffset);
|
|
||||||
range.setPathEnd(this.documentAsDom, s.endContainer, s.endOffset);
|
|
||||||
s.selected = false;
|
|
||||||
s.html = range.getHtml();
|
|
||||||
s.spaghettis = [];
|
|
||||||
s.comments.sort((a, b) => {
|
|
||||||
let divA = document.getElementById("comment_" + a.id);
|
|
||||||
let divB = document.getElementById("comment_" + b.id);
|
|
||||||
return divA.offsetTop - divB.offsetTop
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.scraps = result;
|
|
||||||
this.updateSpaghettis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fetchTheme() {
|
|
||||||
if (this.themes.findIndex((t) => t.html === this.themeInput) === -1) {
|
|
||||||
ArenService.Debates.getTheme({
|
|
||||||
id: this.debate.id,
|
|
||||||
query: {theme: this.themeInput},
|
|
||||||
onSuccess: (result) => {
|
|
||||||
if (result.comments.length > 0) {
|
|
||||||
result.comments = result.comments.map(cid => ArenService.Store.get(cid, Comment)).filter((c) => c !== -1);
|
|
||||||
result.html = result.theme;
|
|
||||||
result.spaghettis = [];
|
|
||||||
result.selected = false;
|
|
||||||
result.comments.sort((a, b) => {
|
|
||||||
let divA = document.getElementById("comment_" + a.id);
|
|
||||||
let divB = document.getElementById("comment_" + b.id);
|
|
||||||
return divA.offsetTop - divB.offsetTop
|
|
||||||
});
|
|
||||||
this.themes.push(result);
|
|
||||||
this.updateSpaghettis();
|
|
||||||
this.themeInput = "";
|
|
||||||
} else {
|
|
||||||
this.themeHelper = this.$t('helper.theme_not_found');
|
|
||||||
setTimeout(() => this.themeHelper = false, 3500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.themeHelper = this.$t('helper.theme_already_set');
|
|
||||||
setTimeout(() => this.themeHelper = false, 3500);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateDisplayableComments() {
|
|
||||||
let selectedScrapComments = false;
|
|
||||||
this.debate.comments.forEach((comment) => this.displayableComments[comment.id] = !selectedScrapComments || selectedScrapComments.includes(comment));
|
|
||||||
},
|
|
||||||
mountChild(comment) {
|
|
||||||
this.mountedChildren++;
|
|
||||||
console.table("Mounted Child", this.mountedChildren);
|
|
||||||
// If there is a commentId in the url, scroll to its position
|
|
||||||
if (this.$route.query.comment && this.mountedChildren >= this.debate.commentsCount) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.scrollToComment(this.$route.query.comment);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// When a new comment is created, scrool to its position
|
|
||||||
if (this.newComment && comment.owner.id === this.$root.user.id) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.scrollToComment(comment);
|
|
||||||
this.newComment = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hidePopup() {
|
|
||||||
this.selectedRange = false;
|
|
||||||
},
|
|
||||||
clearSelection( ) {
|
|
||||||
if (this.$route.query.comment) {
|
|
||||||
this.$router.replace({'query': null});
|
|
||||||
}
|
|
||||||
clearSelection( );
|
|
||||||
},
|
|
||||||
editTags(comment) {
|
|
||||||
this.$refs.tagModal.comment = comment;
|
|
||||||
let copy = [...comment.proposedTags];
|
|
||||||
this.$refs.tagModal.open((returnComment) => {
|
|
||||||
if (returnComment) {
|
|
||||||
ArenService.Comments.edit({
|
|
||||||
data: returnComment,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
comment.proposedTags = copy;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
mailleSelectionHandler(comment,text){
|
|
||||||
if (this.newComment === false) {
|
|
||||||
this.newComment = new Comment( );
|
|
||||||
}
|
|
||||||
if (!this.$refs.commentModal.isOpen()) {
|
|
||||||
this.newComment.debate = this.debate;
|
|
||||||
this.newComment.hypostases = [];
|
|
||||||
this.newComment.tags = [];
|
|
||||||
this.newComment.parent = comment;
|
|
||||||
this.newComment.selection = text;
|
|
||||||
this.newComment.startContainer = 0;
|
|
||||||
this.newComment.endContainer = 0;
|
|
||||||
this.newComment.startOffset = 0;
|
|
||||||
this.newComment.endOffset = 0;
|
|
||||||
} else {
|
|
||||||
this.hidePopup();
|
|
||||||
}
|
|
||||||
this.createComment();
|
|
||||||
},
|
|
||||||
selectionHandler(comment) {
|
|
||||||
let selection = getSelection( );
|
|
||||||
let position = selection.anchorNode.compareDocumentPosition(selection.focusNode);
|
|
||||||
let backward = false;
|
|
||||||
if (!position && selection.anchorOffset > selection.focusOffset ||
|
|
||||||
position === Node.DOCUMENT_POSITION_PRECEDING)
|
|
||||||
backward = true;
|
|
||||||
if (!selection.isCollapsed) {
|
|
||||||
this.selectedRange = selection.getRangeAt(0);
|
|
||||||
this.selectedRange.affineToWord();
|
|
||||||
if (this.newComment === false) {
|
|
||||||
this.newComment = new Comment( );
|
|
||||||
}
|
|
||||||
if (!this.$refs.commentModal.isOpen()) {
|
|
||||||
this.newComment.debate = this.debate;
|
|
||||||
this.newComment.hypostases = [];
|
|
||||||
this.newComment.tags = [];
|
|
||||||
this.newComment.parent = comment;
|
|
||||||
this.newComment.selection = this.selectedRange.getHtml();
|
|
||||||
let container = this.getCommentContainer(this.newComment);
|
|
||||||
this.newComment.startContainer = container.getChildPathTo(this.selectedRange.startContainer);
|
|
||||||
this.newComment.endContainer = container.getChildPathTo(this.selectedRange.endContainer);
|
|
||||||
this.newComment.startOffset = this.selectedRange.startOffset;
|
|
||||||
this.newComment.endOffset = this.selectedRange.endOffset;
|
|
||||||
|
|
||||||
this.popupPositionFromRange(backward);
|
|
||||||
} else {
|
|
||||||
this.hidePopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
popupPositionFromRange(backward) {
|
|
||||||
if (this.selectedRange) {
|
|
||||||
backward = backward === undefined ? this.$refs.arguePopup.classList.contains('top') : backward;
|
|
||||||
let top = mainContainer.offsetTop + 28;
|
|
||||||
let bottom = mainContainer.offsetHeight - window.scrollY + top - 58;
|
|
||||||
let rects = this.selectedRange.getClientRects( );
|
|
||||||
let start = rects[0];
|
|
||||||
let end = rects[rects.length - 1];
|
|
||||||
if (backward && start.y > top || (end.y + end.height) > bottom) {
|
|
||||||
this.popup.x = start.x + window.scrollX;
|
|
||||||
this.popup.y = start.y + window.scrollY;
|
|
||||||
this.$refs.arguePopup.classList.remove('bottom');
|
|
||||||
this.$refs.arguePopup.classList.add('top');
|
|
||||||
} else {
|
|
||||||
this.popup.x = end.x + end.width + window.scrollX;
|
|
||||||
this.popup.y = end.y + end.height + window.scrollY;
|
|
||||||
this.$refs.arguePopup.classList.remove('top');
|
|
||||||
this.$refs.arguePopup.classList.add('bottom');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getCommentContainer(comment) {
|
|
||||||
return comment.parent
|
|
||||||
? document.querySelector("#comment_" + comment.parent.id + " .argumentation")
|
|
||||||
: this.$refs.documentDisplay;
|
|
||||||
},
|
|
||||||
createComment( ) {
|
|
||||||
if (!this.$root.user.is('USER')) {
|
|
||||||
this.$confirm({
|
|
||||||
title: this.$t('not_connected'),
|
|
||||||
message: this.$t('helper.not_connected'),
|
|
||||||
isInfo: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$refs.commentModal.open((returnValue) => {
|
|
||||||
if (returnValue) {
|
|
||||||
ArenService.Debates.addComment({
|
|
||||||
id: this.newComment.debate.id,
|
|
||||||
data: this.newComment,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.hidePopup( );
|
|
||||||
// For the selected text to stay
|
|
||||||
this.highlight(this.newComment);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectComment(comment) {
|
|
||||||
let range = new Range( );
|
|
||||||
let container = comment.parent
|
|
||||||
? document.querySelector("#comment_" + comment.parent.id + " .argumentation")
|
|
||||||
: this.$refs.documentDisplay;
|
|
||||||
range.setPathStart(container, comment.startContainer, comment.startOffset);
|
|
||||||
range.setPathEnd(container, comment.endContainer, comment.endOffset);
|
|
||||||
clearSelection( );
|
|
||||||
getSelection( ).addRange(range);
|
|
||||||
return range;
|
|
||||||
},
|
|
||||||
highlight(comment) {
|
|
||||||
if (!comment.parent) {
|
|
||||||
let range = this.selectComment(comment);
|
|
||||||
if (!inScrollView(this.$refs.documentContainer, range)) {
|
|
||||||
scrollCenter(this.$refs.documentContainer, range);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Select after scroll is done because of lazy loading
|
|
||||||
this.scrollToComment(comment.parent, false, () => {
|
|
||||||
this.selectComment(comment);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollToComment(comment, focus = true, callback = () => {}) {
|
|
||||||
let commentDiv = document.getElementById("comment_" + (comment.id ? comment.id : comment));
|
|
||||||
let previousScroll = this.$refs.commentsContainer.scrollTop;
|
|
||||||
if (focus) {
|
|
||||||
commentDiv.querySelector(".focuser").focus( );
|
|
||||||
}
|
|
||||||
this.$refs.commentsContainer.scrollTop = previousScroll;
|
|
||||||
if (!inScrollView(this.$refs.commentsContainer, commentDiv)) {
|
|
||||||
scrollCenter(this.$refs.commentsContainer, commentDiv, callback);
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollToScrap(scrapId, callback) {
|
|
||||||
let scrapDiv = document.getElementById("scrap_" + scrapId);
|
|
||||||
if (!inScrollView(this.$refs.documentContainer, scrapDiv)) {
|
|
||||||
scrollCenter(this.$refs.documentContainer, scrapDiv, callback);
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
spaghetti(scrap, scrapIndex, comment, commentIndex) {
|
|
||||||
let scrapId = "scrap_" + scrapIndex;
|
|
||||||
let commentId = "comment_" + comment.id;
|
|
||||||
let scrapDiv = document.getElementById(scrapId);
|
|
||||||
let commentDiv = document.getElementById(commentId);
|
|
||||||
// Check if a parent is collapsed, if so remove spaghettis
|
|
||||||
let answersDiv = commentDiv.closest(".answers-container");
|
|
||||||
while (answersDiv && commentDiv) {
|
|
||||||
let commentParentDiv = answersDiv.previousElementSibling;
|
|
||||||
if (commentDiv.offsetTop - commentParentDiv.offsetTop < (commentParentDiv.offsetHeight / 2)) {
|
|
||||||
commentDiv = false;
|
|
||||||
} else {
|
|
||||||
answersDiv = answersDiv.parentElement.closest(".answers-container");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// Only if the div exists and is visible
|
|
||||||
if (commentDiv && commentDiv.offsetParent !== null) {
|
|
||||||
let offsetStart = this.$refs.documentContainer.scrollTop;
|
|
||||||
let offsetEnd = this.$refs.commentsContainer.scrollTop;
|
|
||||||
let heightStart = Math.min(scrapDiv.offsetHeight * 0.5, scrap.comments.length * 2);
|
|
||||||
let deltaY = heightStart / scrap.comments.length;
|
|
||||||
|
|
||||||
return d3.linkHorizontal().x(d => d.x).y(d => d.y)({
|
|
||||||
source: {
|
|
||||||
x: (scrapDiv.offsetLeft + scrapDiv.offsetWidth),
|
|
||||||
y: (scrapDiv.offsetTop + commentIndex * deltaY + (scrapDiv.offsetHeight - heightStart) / 2) - offsetStart
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
x: commentDiv.offsetLeft,
|
|
||||||
y: (commentDiv.offsetTop + commentDiv.offsetHeight / 2) - offsetEnd
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
updateSpaghettis() {
|
|
||||||
if (this.leftDisplay !== 'document') {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.spaghettiData.forEach((s, scrapIndex) => {
|
|
||||||
s.spaghettis = s.comments.map((c, comIndex) => this.spaghetti(s, scrapIndex, c, comIndex));
|
|
||||||
s.color = d3.interpolateRainbow(scrapIndex / this.spaghettiData.length);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clickSpaghetti() {
|
|
||||||
let arrIds = this.spaghettiOver.split('_');
|
|
||||||
this.spaghettiCountDown = 2;
|
|
||||||
this.scrollToScrap(arrIds[1], () => this.spaghettiCountDown--);
|
|
||||||
this.scrollToComment(arrIds[2], true, () => this.spaghettiCountDown--);
|
|
||||||
},
|
|
||||||
collapseComment(duration) {
|
|
||||||
let start = Date.now();
|
|
||||||
duration = duration + 500;
|
|
||||||
let interval = setInterval(() => {
|
|
||||||
this.updateSpaghettis();
|
|
||||||
if (Date.now() - start >= duration) {
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 1000 / 60);
|
|
||||||
},
|
|
||||||
reportComment(comment) {
|
|
||||||
this.$confirm({
|
|
||||||
title: this.$t('report'),
|
|
||||||
message: this.$t('helper.report_details')
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/********************* */
|
|
||||||
|
|
||||||
initDataArray() {
|
|
||||||
let arr = [];
|
|
||||||
let col = Array(this.nbOfColumns).fill(false);
|
|
||||||
for (let i = 0; i < this.nbOfLines; i++) {
|
|
||||||
arr.push(col);
|
|
||||||
}
|
|
||||||
this.arrayData = arr;
|
|
||||||
},
|
|
||||||
select(l, c) {
|
|
||||||
let row = this.arrayData[l].slice();
|
|
||||||
row[c] = true;
|
|
||||||
this.arrayData.splice(l, 1, row);
|
|
||||||
this.mailleSelectionHandler(null,`Maille ligne ${l+1}, colonne ${c+1}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
"tags-modal": vueLoader("components/modals/tagsModal"),
|
|
||||||
"comment-modal": vueLoader("components/modals/commentModal"),
|
|
||||||
|
|
||||||
"comment-widget": vueLoader("components/widgets/comment"),
|
|
||||||
"bullets-container": vueLoader("components/widgets/bulletsContainer"),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -196,7 +196,7 @@
|
||||||
data( ) {
|
data( ) {
|
||||||
return {
|
return {
|
||||||
debate: new Debate( ),
|
debate: new Debate( ),
|
||||||
categories: ArenService.Store.Category,
|
categories: ArenService.Store.Category.filter(cat=>cat.is('CARTO')),
|
||||||
institution: new Institution( ),
|
institution: new Institution( ),
|
||||||
search: "",
|
search: "",
|
||||||
step: 0,
|
step: 0,
|
||||||
|
|
651
src/main/webapp/assets/js/views/debateSurCarte.vue
Normal file
651
src/main/webapp/assets/js/views/debateSurCarte.vue
Normal file
|
@ -0,0 +1,651 @@
|
||||||
|
<template>
|
||||||
|
<base-layout id="debate">
|
||||||
|
<template v-slot:title>
|
||||||
|
<h1>Debat sur carte</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- ************************************************* -->
|
||||||
|
|
||||||
|
<div id="mainContainer">
|
||||||
|
<div id="documentContainer">
|
||||||
|
<div
|
||||||
|
class="scroll-area"
|
||||||
|
@scroll="
|
||||||
|
hidePopup();
|
||||||
|
updateSpaghettis();
|
||||||
|
"
|
||||||
|
ref="documentContainer"
|
||||||
|
>
|
||||||
|
<div class="wrap">
|
||||||
|
<documented v-bind:value="$t('documentation.debate_document')">
|
||||||
|
<div id="mapContainer">
|
||||||
|
<img
|
||||||
|
id="mapImg"
|
||||||
|
:src="debate.document.mapLink"
|
||||||
|
alt="map image"
|
||||||
|
/>
|
||||||
|
<table id="myTable" v-if="debate">
|
||||||
|
<tr
|
||||||
|
v-for="(_, indexOfLine) in debate.document.meshLine"
|
||||||
|
:key="indexOfLine"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
v-for="(_, indexOfColumn) in debate.document.meshColumn"
|
||||||
|
:key="indexOfColumn"
|
||||||
|
@click="select(indexOfLine, indexOfColumn)"
|
||||||
|
:class="{
|
||||||
|
selected: arrayData[indexOfLine][indexOfColumn],
|
||||||
|
}"
|
||||||
|
></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</documented>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="commentsContainer"
|
||||||
|
v-bind:class="{ loading: mountedChildren < debate.commentsCount }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="scroll-area"
|
||||||
|
@scroll="
|
||||||
|
hidePopup();
|
||||||
|
updateSpaghettis();
|
||||||
|
"
|
||||||
|
ref="commentsContainer"
|
||||||
|
>
|
||||||
|
<comment-widget
|
||||||
|
v-for="comment in filteredComments"
|
||||||
|
v-show="displayableComments[comment.id]"
|
||||||
|
v-bind:displayable-comments="displayableComments"
|
||||||
|
v-bind:key="comment.id"
|
||||||
|
v-bind:comment="comment"
|
||||||
|
v-bind:search="search"
|
||||||
|
v-bind:hide-on-scroll="true"
|
||||||
|
@tag-edition="editTags($event)"
|
||||||
|
@selection-end="selectionHandler($event)"
|
||||||
|
@focus-me="
|
||||||
|
selectComment($event);
|
||||||
|
scrollToComment($event);
|
||||||
|
"
|
||||||
|
@select-me="selectComment($event)"
|
||||||
|
@highlight-me="highlight($event)"
|
||||||
|
@mounted="mountChild($event)"
|
||||||
|
@report="reportComment($event)"
|
||||||
|
@collapse="collapseComment($event)"
|
||||||
|
>
|
||||||
|
</comment-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-show="selectedRange"
|
||||||
|
class="selection_popup z-depth-2"
|
||||||
|
ref="arguePopup"
|
||||||
|
v-bind:style="'top: ' + popup.y + 'px; left: ' + popup.x + 'px;'"
|
||||||
|
@click="createComment()"
|
||||||
|
@mousedown.stop=""
|
||||||
|
@mouseup.stop=""
|
||||||
|
>
|
||||||
|
{{ $t("argue").toLowerCase() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-slot:addons>
|
||||||
|
<comment-modal
|
||||||
|
id="commentModal"
|
||||||
|
ref="commentModal"
|
||||||
|
v-bind:comment="newComment"
|
||||||
|
>
|
||||||
|
</comment-modal>
|
||||||
|
|
||||||
|
<tags-modal ref="tagModal"> </tags-modal>
|
||||||
|
</template>
|
||||||
|
</base-layout>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
table,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#mapContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#mapImg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: rgb(0 0 0 / 26%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
module.exports = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
//nbOfLines: 5,
|
||||||
|
//nbOfColumns: 9,
|
||||||
|
arrayData: false,
|
||||||
|
// ********************************
|
||||||
|
|
||||||
|
debate: false,
|
||||||
|
newComment: false,
|
||||||
|
sortByPosition: false,
|
||||||
|
popup: { x: -9999, y: -9999 },
|
||||||
|
displayableComments: {},
|
||||||
|
selectedRange: false,
|
||||||
|
search: "",
|
||||||
|
mountedChildren: 0,
|
||||||
|
leftDisplay: "document",
|
||||||
|
scrapsMagicNumber: 2,
|
||||||
|
spaghettiOver: "",
|
||||||
|
spaghettiCountDown: 0,
|
||||||
|
scraps: [],
|
||||||
|
themes: [],
|
||||||
|
themeInput: "",
|
||||||
|
themeHelper: false,
|
||||||
|
|
||||||
|
// ****************************************
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
ArenService.CommentListener.listen({
|
||||||
|
id: this.$route.params.id,
|
||||||
|
onMessage: (comment) => {
|
||||||
|
Vue.set(this.displayableComments, comment.id, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filteredComments() {
|
||||||
|
return this.debate.comments
|
||||||
|
? this.debate.comments.filter((comment) => !comment.parent)
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
documentAsDom() {
|
||||||
|
let div = document.createElement("div");
|
||||||
|
div.innerHTML = this.debate.document.content;
|
||||||
|
return div;
|
||||||
|
},
|
||||||
|
spaghettiData() {
|
||||||
|
if (this.leftDisplay === "theme") {
|
||||||
|
return this.themes;
|
||||||
|
} else {
|
||||||
|
return this.scraps;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.fetchData();
|
||||||
|
if (this.$route.query.comment) {
|
||||||
|
this.scrollToComment(this.$route.query.comment);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
debate() {
|
||||||
|
this.displayableComments = {};
|
||||||
|
//This is to make all the properties reactive
|
||||||
|
this.debate.comments.forEach((comment) =>
|
||||||
|
Vue.set(this.displayableComments, comment.id, true)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
leftDisplay(val) {
|
||||||
|
if (val === "scraps") {
|
||||||
|
this.fetchScraps();
|
||||||
|
}
|
||||||
|
if (val !== "document") {
|
||||||
|
this.updateSpaghettis();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// search(val) {
|
||||||
|
// if (this.leftDisplay === 'scraps' || this.leftDisplay === 'theme') {
|
||||||
|
// this.updateSpaghettis();
|
||||||
|
// }
|
||||||
|
// this.updateDisplayableComments();
|
||||||
|
// },
|
||||||
|
sortByPosition(val) {
|
||||||
|
if (val) {
|
||||||
|
this.debate.deepSortComments((a, b) =>
|
||||||
|
a.compareBoundaryPoints(Range.START_TO_START, b)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.debate.deepSortComments((a, b) =>
|
||||||
|
a.created > b.created ? 1 : -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.leftDisplay !== "document") {
|
||||||
|
// Sort by disply position
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.spaghettiData.forEach((s) => {
|
||||||
|
s.comments.sort((a, b) => {
|
||||||
|
let divA = document.getElementById("comment_" + a.id);
|
||||||
|
let divB = document.getElementById("comment_" + b.id);
|
||||||
|
return divA.offsetTop - divB.offsetTop;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.updateSpaghettis();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/******************* */
|
||||||
|
fetchData() {
|
||||||
|
ArenService.Debates.get({
|
||||||
|
id: this.$route.params.id,
|
||||||
|
onSuccess: (debate) => {
|
||||||
|
this.debate = debate;
|
||||||
|
let arr = [];
|
||||||
|
let col = Array(debate.document.meshColumn).fill(false);
|
||||||
|
for (let i = 0; i < debate.document.meshLine; i++) {
|
||||||
|
arr.push(col);
|
||||||
|
}
|
||||||
|
this.arrayData = arr;
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
this.$router.push("/404");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchScraps() {
|
||||||
|
ArenService.Debates.getScraps({
|
||||||
|
id: this.debate.id,
|
||||||
|
onSuccess: (result) => {
|
||||||
|
if (result.length > 0) {
|
||||||
|
let arrLength = result.map((r) => r.comments.length).sort();
|
||||||
|
let minLength =
|
||||||
|
arrLength[arrLength.length - this.scrapsMagicNumber];
|
||||||
|
result = result.filter(
|
||||||
|
(s) => s.comments.length > 1 && s.comments.length >= minLength
|
||||||
|
);
|
||||||
|
result.forEach((s, i) => {
|
||||||
|
s.id = i;
|
||||||
|
s.comments = s.comments.map((cid) =>
|
||||||
|
ArenService.Store.get(cid, Comment)
|
||||||
|
);
|
||||||
|
let range = new Range();
|
||||||
|
range.setPathStart(
|
||||||
|
this.documentAsDom,
|
||||||
|
s.startContainer,
|
||||||
|
s.startOffset
|
||||||
|
);
|
||||||
|
range.setPathEnd(this.documentAsDom, s.endContainer, s.endOffset);
|
||||||
|
s.selected = false;
|
||||||
|
s.html = range.getHtml();
|
||||||
|
s.spaghettis = [];
|
||||||
|
s.comments.sort((a, b) => {
|
||||||
|
let divA = document.getElementById("comment_" + a.id);
|
||||||
|
let divB = document.getElementById("comment_" + b.id);
|
||||||
|
return divA.offsetTop - divB.offsetTop;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.scraps = result;
|
||||||
|
this.updateSpaghettis();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchTheme() {
|
||||||
|
if (this.themes.findIndex((t) => t.html === this.themeInput) === -1) {
|
||||||
|
ArenService.Debates.getTheme({
|
||||||
|
id: this.debate.id,
|
||||||
|
query: { theme: this.themeInput },
|
||||||
|
onSuccess: (result) => {
|
||||||
|
if (result.comments.length > 0) {
|
||||||
|
result.comments = result.comments
|
||||||
|
.map((cid) => ArenService.Store.get(cid, Comment))
|
||||||
|
.filter((c) => c !== -1);
|
||||||
|
result.html = result.theme;
|
||||||
|
result.spaghettis = [];
|
||||||
|
result.selected = false;
|
||||||
|
result.comments.sort((a, b) => {
|
||||||
|
let divA = document.getElementById("comment_" + a.id);
|
||||||
|
let divB = document.getElementById("comment_" + b.id);
|
||||||
|
return divA.offsetTop - divB.offsetTop;
|
||||||
|
});
|
||||||
|
this.themes.push(result);
|
||||||
|
this.updateSpaghettis();
|
||||||
|
this.themeInput = "";
|
||||||
|
} else {
|
||||||
|
this.themeHelper = this.$t("helper.theme_not_found");
|
||||||
|
setTimeout(() => (this.themeHelper = false), 3500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.themeHelper = this.$t("helper.theme_already_set");
|
||||||
|
setTimeout(() => (this.themeHelper = false), 3500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateDisplayableComments() {
|
||||||
|
let selectedScrapComments = false;
|
||||||
|
this.debate.comments.forEach(
|
||||||
|
(comment) =>
|
||||||
|
(this.displayableComments[comment.id] =
|
||||||
|
!selectedScrapComments || selectedScrapComments.includes(comment))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
mountChild(comment) {
|
||||||
|
this.mountedChildren++;
|
||||||
|
console.table("Mounted Child", this.mountedChildren);
|
||||||
|
// If there is a commentId in the url, scroll to its position
|
||||||
|
if (
|
||||||
|
this.$route.query.comment &&
|
||||||
|
this.mountedChildren >= this.debate.commentsCount
|
||||||
|
) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollToComment(this.$route.query.comment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// When a new comment is created, scrool to its position
|
||||||
|
if (this.newComment && comment.owner.id === this.$root.user.id) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollToComment(comment);
|
||||||
|
this.newComment = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hidePopup() {
|
||||||
|
this.selectedRange = false;
|
||||||
|
},
|
||||||
|
clearSelection() {
|
||||||
|
if (this.$route.query.comment) {
|
||||||
|
this.$router.replace({ query: null });
|
||||||
|
}
|
||||||
|
clearSelection();
|
||||||
|
},
|
||||||
|
editTags(comment) {
|
||||||
|
this.$refs.tagModal.comment = comment;
|
||||||
|
let copy = [...comment.proposedTags];
|
||||||
|
this.$refs.tagModal.open((returnComment) => {
|
||||||
|
if (returnComment) {
|
||||||
|
ArenService.Comments.edit({
|
||||||
|
data: returnComment,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
comment.proposedTags = copy;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mailleSelectionHandler(comment, text) {
|
||||||
|
if (this.newComment === false) {
|
||||||
|
this.newComment = new Comment();
|
||||||
|
}
|
||||||
|
if (!this.$refs.commentModal.isOpen()) {
|
||||||
|
this.newComment.debate = this.debate;
|
||||||
|
this.newComment.hypostases = [];
|
||||||
|
this.newComment.tags = [];
|
||||||
|
this.newComment.parent = comment;
|
||||||
|
this.newComment.selection = text;
|
||||||
|
this.newComment.startContainer = 0;
|
||||||
|
this.newComment.endContainer = 0;
|
||||||
|
this.newComment.startOffset = 0;
|
||||||
|
this.newComment.endOffset = 0;
|
||||||
|
} else {
|
||||||
|
this.hidePopup();
|
||||||
|
}
|
||||||
|
this.createComment();
|
||||||
|
},
|
||||||
|
selectionHandler(comment) {
|
||||||
|
let selection = getSelection();
|
||||||
|
let position = selection.anchorNode.compareDocumentPosition(
|
||||||
|
selection.focusNode
|
||||||
|
);
|
||||||
|
let backward = false;
|
||||||
|
if (
|
||||||
|
(!position && selection.anchorOffset > selection.focusOffset) ||
|
||||||
|
position === Node.DOCUMENT_POSITION_PRECEDING
|
||||||
|
)
|
||||||
|
backward = true;
|
||||||
|
if (!selection.isCollapsed) {
|
||||||
|
this.selectedRange = selection.getRangeAt(0);
|
||||||
|
this.selectedRange.affineToWord();
|
||||||
|
if (this.newComment === false) {
|
||||||
|
this.newComment = new Comment();
|
||||||
|
}
|
||||||
|
if (!this.$refs.commentModal.isOpen()) {
|
||||||
|
this.newComment.debate = this.debate;
|
||||||
|
this.newComment.hypostases = [];
|
||||||
|
this.newComment.tags = [];
|
||||||
|
this.newComment.parent = comment;
|
||||||
|
this.newComment.selection = this.selectedRange.getHtml();
|
||||||
|
let container = this.getCommentContainer(this.newComment);
|
||||||
|
this.newComment.startContainer = container.getChildPathTo(
|
||||||
|
this.selectedRange.startContainer
|
||||||
|
);
|
||||||
|
this.newComment.endContainer = container.getChildPathTo(
|
||||||
|
this.selectedRange.endContainer
|
||||||
|
);
|
||||||
|
this.newComment.startOffset = this.selectedRange.startOffset;
|
||||||
|
this.newComment.endOffset = this.selectedRange.endOffset;
|
||||||
|
|
||||||
|
this.popupPositionFromRange(backward);
|
||||||
|
} else {
|
||||||
|
this.hidePopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
popupPositionFromRange(backward) {
|
||||||
|
if (this.selectedRange) {
|
||||||
|
backward =
|
||||||
|
backward === undefined
|
||||||
|
? this.$refs.arguePopup.classList.contains("top")
|
||||||
|
: backward;
|
||||||
|
let top = mainContainer.offsetTop + 28;
|
||||||
|
let bottom = mainContainer.offsetHeight - window.scrollY + top - 58;
|
||||||
|
let rects = this.selectedRange.getClientRects();
|
||||||
|
let start = rects[0];
|
||||||
|
let end = rects[rects.length - 1];
|
||||||
|
if ((backward && start.y > top) || end.y + end.height > bottom) {
|
||||||
|
this.popup.x = start.x + window.scrollX;
|
||||||
|
this.popup.y = start.y + window.scrollY;
|
||||||
|
this.$refs.arguePopup.classList.remove("bottom");
|
||||||
|
this.$refs.arguePopup.classList.add("top");
|
||||||
|
} else {
|
||||||
|
this.popup.x = end.x + end.width + window.scrollX;
|
||||||
|
this.popup.y = end.y + end.height + window.scrollY;
|
||||||
|
this.$refs.arguePopup.classList.remove("top");
|
||||||
|
this.$refs.arguePopup.classList.add("bottom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCommentContainer(comment) {
|
||||||
|
return comment.parent
|
||||||
|
? document.querySelector(
|
||||||
|
"#comment_" + comment.parent.id + " .argumentation"
|
||||||
|
)
|
||||||
|
: this.$refs.documentDisplay;
|
||||||
|
},
|
||||||
|
createComment() {
|
||||||
|
if (!this.$root.user.is("USER")) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t("not_connected"),
|
||||||
|
message: this.$t("helper.not_connected"),
|
||||||
|
isInfo: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$refs.commentModal.open((returnValue) => {
|
||||||
|
if (returnValue) {
|
||||||
|
ArenService.Debates.addComment({
|
||||||
|
id: this.newComment.debate.id,
|
||||||
|
data: this.newComment,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.hidePopup();
|
||||||
|
// For the selected text to stay
|
||||||
|
this.highlight(this.newComment);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectComment(comment) {
|
||||||
|
let range = new Range();
|
||||||
|
let container = comment.parent
|
||||||
|
? document.querySelector(
|
||||||
|
"#comment_" + comment.parent.id + " .argumentation"
|
||||||
|
)
|
||||||
|
: this.$refs.documentDisplay;
|
||||||
|
range.setPathStart(
|
||||||
|
container,
|
||||||
|
comment.startContainer,
|
||||||
|
comment.startOffset
|
||||||
|
);
|
||||||
|
range.setPathEnd(container, comment.endContainer, comment.endOffset);
|
||||||
|
clearSelection();
|
||||||
|
getSelection().addRange(range);
|
||||||
|
return range;
|
||||||
|
},
|
||||||
|
highlight(comment) {
|
||||||
|
if (!comment.parent) {
|
||||||
|
let range = this.selectComment(comment);
|
||||||
|
if (!inScrollView(this.$refs.documentContainer, range)) {
|
||||||
|
scrollCenter(this.$refs.documentContainer, range);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Select after scroll is done because of lazy loading
|
||||||
|
this.scrollToComment(comment.parent, false, () => {
|
||||||
|
this.selectComment(comment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollToComment(comment, focus = true, callback = () => {}) {
|
||||||
|
let commentDiv = document.getElementById(
|
||||||
|
"comment_" + (comment.id ? comment.id : comment)
|
||||||
|
);
|
||||||
|
let previousScroll = this.$refs.commentsContainer.scrollTop;
|
||||||
|
if (focus) {
|
||||||
|
commentDiv.querySelector(".focuser").focus();
|
||||||
|
}
|
||||||
|
this.$refs.commentsContainer.scrollTop = previousScroll;
|
||||||
|
if (!inScrollView(this.$refs.commentsContainer, commentDiv)) {
|
||||||
|
scrollCenter(this.$refs.commentsContainer, commentDiv, callback);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollToScrap(scrapId, callback) {
|
||||||
|
let scrapDiv = document.getElementById("scrap_" + scrapId);
|
||||||
|
if (!inScrollView(this.$refs.documentContainer, scrapDiv)) {
|
||||||
|
scrollCenter(this.$refs.documentContainer, scrapDiv, callback);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
spaghetti(scrap, scrapIndex, comment, commentIndex) {
|
||||||
|
let scrapId = "scrap_" + scrapIndex;
|
||||||
|
let commentId = "comment_" + comment.id;
|
||||||
|
let scrapDiv = document.getElementById(scrapId);
|
||||||
|
let commentDiv = document.getElementById(commentId);
|
||||||
|
// Check if a parent is collapsed, if so remove spaghettis
|
||||||
|
let answersDiv = commentDiv.closest(".answers-container");
|
||||||
|
while (answersDiv && commentDiv) {
|
||||||
|
let commentParentDiv = answersDiv.previousElementSibling;
|
||||||
|
if (
|
||||||
|
commentDiv.offsetTop - commentParentDiv.offsetTop <
|
||||||
|
commentParentDiv.offsetHeight / 2
|
||||||
|
) {
|
||||||
|
commentDiv = false;
|
||||||
|
} else {
|
||||||
|
answersDiv = answersDiv.parentElement.closest(".answers-container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only if the div exists and is visible
|
||||||
|
if (commentDiv && commentDiv.offsetParent !== null) {
|
||||||
|
let offsetStart = this.$refs.documentContainer.scrollTop;
|
||||||
|
let offsetEnd = this.$refs.commentsContainer.scrollTop;
|
||||||
|
let heightStart = Math.min(
|
||||||
|
scrapDiv.offsetHeight * 0.5,
|
||||||
|
scrap.comments.length * 2
|
||||||
|
);
|
||||||
|
let deltaY = heightStart / scrap.comments.length;
|
||||||
|
|
||||||
|
return d3
|
||||||
|
.linkHorizontal()
|
||||||
|
.x((d) => d.x)
|
||||||
|
.y((d) => d.y)({
|
||||||
|
source: {
|
||||||
|
x: scrapDiv.offsetLeft + scrapDiv.offsetWidth,
|
||||||
|
y:
|
||||||
|
scrapDiv.offsetTop +
|
||||||
|
commentIndex * deltaY +
|
||||||
|
(scrapDiv.offsetHeight - heightStart) / 2 -
|
||||||
|
offsetStart,
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
x: commentDiv.offsetLeft,
|
||||||
|
y: commentDiv.offsetTop + commentDiv.offsetHeight / 2 - offsetEnd,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
updateSpaghettis() {
|
||||||
|
if (this.leftDisplay !== "document") {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.spaghettiData.forEach((s, scrapIndex) => {
|
||||||
|
s.spaghettis = s.comments.map((c, comIndex) =>
|
||||||
|
this.spaghetti(s, scrapIndex, c, comIndex)
|
||||||
|
);
|
||||||
|
s.color = d3.interpolateRainbow(
|
||||||
|
scrapIndex / this.spaghettiData.length
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickSpaghetti() {
|
||||||
|
let arrIds = this.spaghettiOver.split("_");
|
||||||
|
this.spaghettiCountDown = 2;
|
||||||
|
this.scrollToScrap(arrIds[1], () => this.spaghettiCountDown--);
|
||||||
|
this.scrollToComment(arrIds[2], true, () => this.spaghettiCountDown--);
|
||||||
|
},
|
||||||
|
collapseComment(duration) {
|
||||||
|
let start = Date.now();
|
||||||
|
duration = duration + 500;
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
this.updateSpaghettis();
|
||||||
|
if (Date.now() - start >= duration) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, 1000 / 60);
|
||||||
|
},
|
||||||
|
reportComment(comment) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t("report"),
|
||||||
|
message: this.$t("helper.report_details"),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/********************* */
|
||||||
|
select(l, c) {
|
||||||
|
let row = this.arrayData[l].slice();
|
||||||
|
row[c] = true;
|
||||||
|
this.arrayData.splice(l, 1, row);
|
||||||
|
this.mailleSelectionHandler(
|
||||||
|
null,
|
||||||
|
`Maille ligne ${l + 1}, colonne ${c + 1}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
"tags-modal": vueLoader("components/modals/tagsModal"),
|
||||||
|
"comment-modal": vueLoader("components/modals/commentModal"),
|
||||||
|
|
||||||
|
"comment-widget": vueLoader("components/widgets/comment"),
|
||||||
|
"bullets-container": vueLoader("components/widgets/bulletsContainer"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -60,7 +60,9 @@
|
||||||
query: categoryId ? {category: categoryId, overview: true} : {overview: true},
|
query: categoryId ? {category: categoryId, overview: true} : {overview: true},
|
||||||
onSuccess: debates => debates.forEach(d => {
|
onSuccess: debates => debates.forEach(d => {
|
||||||
if (!this.categories.includes(d.document.category)) {
|
if (!this.categories.includes(d.document.category)) {
|
||||||
this.categories.push(d.document.category)
|
if(d.document.category.is("CARTO")){
|
||||||
|
this.categories.push(d.document.category);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
<template v-slot:right>
|
<template v-slot:right>
|
||||||
<span
|
<span
|
||||||
|
:disabled="document.mapLink.length<=0"
|
||||||
v-if="editionMode"
|
v-if="editionMode"
|
||||||
class="waves-effect waves-light btn"
|
class="waves-effect waves-light btn"
|
||||||
@click="saveDocument()"
|
@click="saveDocument()"
|
||||||
|
@ -45,13 +46,12 @@
|
||||||
class="waves-effect waves-light btn"
|
class="waves-effect waves-light btn"
|
||||||
@click="
|
@click="
|
||||||
fetchData();
|
fetchData();
|
||||||
editionMode = true;
|
editionMode = true;"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ $t("modify") }}
|
{{ $t("modify") }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<div id="cartoDetails">
|
<div id="cartoDetails" v-if="editionMode">
|
||||||
<div>
|
<div>
|
||||||
<label for="loadCarto" class="btn">Charger la carte</label>
|
<label for="loadCarto" class="btn">Charger la carte</label>
|
||||||
<input
|
<input
|
||||||
|
@ -64,21 +64,21 @@
|
||||||
<div class="cartoInformation-bloc">
|
<div class="cartoInformation-bloc">
|
||||||
<div class="mailleInfo">
|
<div class="mailleInfo">
|
||||||
<label>Nombre de ligne </label>
|
<label>Nombre de ligne </label>
|
||||||
<input type="text" maxlength="2" v-model.number="nbOfLines" />
|
<input type="text" maxlength="2" v-model.number="document.meshLine" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mailleInfo">
|
<div class="mailleInfo">
|
||||||
<label>Nombre de colonne </label>
|
<label>Nombre de colonne </label>
|
||||||
<input type="text" maxlength="2" v-model.number="nbOfColumns" />
|
<input type="text" maxlength="2" v-model.number="document.meshColumn" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="mapContainer" v-if="imgUrl">
|
<div id="mapContainer" v-if="imgUrl">
|
||||||
<img id="mapImg" :src="imgUrl" alt="map image" />
|
<img id="mapImg" :src="imgUrl" alt="map image" />
|
||||||
<table>
|
<table>
|
||||||
<tr v-for="(_, indexOfLine) in nbOfLines" :key="indexOfLine">
|
<tr v-for="(_, indexOfLine) in document.meshLine" :key="indexOfLine">
|
||||||
<td
|
<td
|
||||||
v-for="(_, indexOfColumn) in nbOfColumns"
|
v-for="(_, indexOfColumn) in document.meshColumn"
|
||||||
:key="indexOfColumn"
|
:key="indexOfColumn"
|
||||||
></td>
|
></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -142,8 +142,8 @@ module.exports = {
|
||||||
editionMode: false,
|
editionMode: false,
|
||||||
selectedCarto: null,
|
selectedCarto: null,
|
||||||
imgUrl: null,
|
imgUrl: null,
|
||||||
nbOfLines: 0,
|
// nbOfLines: 0,
|
||||||
nbOfColumns: 0,
|
// nbOfColumns: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -163,12 +163,16 @@ module.exports = {
|
||||||
onSuccess: (document) => {
|
onSuccess: (document) => {
|
||||||
this.document = document;
|
this.document = document;
|
||||||
this.copyDocument(this.document, this.unmodifiedDocument);
|
this.copyDocument(this.document, this.unmodifiedDocument);
|
||||||
|
this.imgUrl = document.mapLink;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.document = new Document();
|
this.document = new Document();
|
||||||
|
this.document.type = "CARTO";
|
||||||
|
this.document.mapLink="";
|
||||||
this.document.category = new Category();
|
this.document.category = new Category();
|
||||||
this.document.category.id = this.$route.query.category;
|
this.document.category.id = this.$route.query.category;
|
||||||
|
this.document.category.type = "CARTO";
|
||||||
this.editionMode = true;
|
this.editionMode = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -181,6 +185,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
saveDocument() {
|
saveDocument() {
|
||||||
this.editionMode = false;
|
this.editionMode = false;
|
||||||
|
//this.document.mapLink = this.getBase64Image();
|
||||||
ArenService.Documents.createOrUpdate({
|
ArenService.Documents.createOrUpdate({
|
||||||
data: this.document,
|
data: this.document,
|
||||||
onSuccess: (document) => {
|
onSuccess: (document) => {
|
||||||
|
@ -199,7 +204,28 @@ module.exports = {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
this.selectedCarto = file;
|
this.selectedCarto = file;
|
||||||
this.imgUrl = URL.createObjectURL(file);
|
this.imgUrl = URL.createObjectURL(file);
|
||||||
|
this.document.mapLink = this.getBase64Image();
|
||||||
},
|
},
|
||||||
|
getBase64Image() {
|
||||||
|
// Create an empty canvas element
|
||||||
|
let img = document.getElementById("mapImg");
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
|
||||||
|
// Copy the image contents to the canvas
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
// Get the data-URL formatted image
|
||||||
|
// Firefox supports PNG and JPEG. You could check img.src to
|
||||||
|
// guess the original format, but be aware the using "image/jpg"
|
||||||
|
// will re-encode the image.
|
||||||
|
var dataURL = canvas.toDataURL("image/png");
|
||||||
|
//let dt = dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
|
||||||
|
console.log(dataURL);
|
||||||
|
return dataURL;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
"wysiwyg-editor": vueLoader("components/widgets/wysiwygEditor"),
|
"wysiwyg-editor": vueLoader("components/widgets/wysiwygEditor"),
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data( ) {
|
data( ) {
|
||||||
return {
|
return {
|
||||||
categories: ArenService.Store.Category
|
categories: ArenService.Store.Category.filter(cat=>!cat.is('CARTO'))
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created( ) {
|
created( ) {
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data( ) {
|
data( ) {
|
||||||
return {
|
return {
|
||||||
categories: ArenService.Store.Category
|
categories: ArenService.Store.Category.filter(cat=>cat.is('CARTO'))
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created( ) {
|
created( ) {
|
||||||
|
@ -84,14 +84,17 @@
|
||||||
name: document.name + " - copie",
|
name: document.name + " - copie",
|
||||||
author: document.author,
|
author: document.author,
|
||||||
content: document.content,
|
content: document.content,
|
||||||
category: {id: document.category.id}
|
category: {id: document.category.id, type: "CARTO"}
|
||||||
});
|
});
|
||||||
ArenService.Documents.duplicate({
|
ArenService.Documents.duplicate({
|
||||||
id: document.id
|
id: document.id
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
createOrUpdateCategory(category = {}) {
|
createOrUpdateCategory(category = {}) {
|
||||||
this.$refs.categoryModal.category = new Category(category);
|
let catg = new Category(category);
|
||||||
|
catg.type = "CARTO"
|
||||||
|
this.$refs.categoryModal.category = catg;
|
||||||
|
console.log(this.$refs.categoryModal.category);
|
||||||
this.$refs.categoryModal.open((updatedCat) => {
|
this.$refs.categoryModal.open((updatedCat) => {
|
||||||
if (updatedCat) {
|
if (updatedCat) {
|
||||||
ArenService.Categories.createOrUpdate({
|
ArenService.Categories.createOrUpdate({
|
||||||
|
|
|
@ -111,14 +111,14 @@
|
||||||
<li v-if="user.is('USER')">
|
<li v-if="user.is('USER')">
|
||||||
<router-link to="/votemajoritaire" v-bind:class="{ active: $route.path === '/votemajoritaire' || $route.path === '/createVoteMajoritaire' || $route.path === '/votemajoritairedetails'}">Votes majoritaires</router-link>
|
<router-link to="/votemajoritaire" v-bind:class="{ active: $route.path === '/votemajoritaire' || $route.path === '/createVoteMajoritaire' || $route.path === '/votemajoritairedetails'}">Votes majoritaires</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-if="user.is('USER')">
|
||||||
<router-link to="/creation-debats-sur-cartes" v-bind:class="{ active: $route.path === '/creation-debats-sur-cartes' }">Ouvrir un débat sur carte</router-link>
|
<router-link to="/creation-debats-sur-cartes" v-bind:class="{ active: $route.path === '/creation-debats-sur-cartes' }">Ouvrir dc</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-if="user.is('USER')">
|
||||||
<router-link to="/documents-debats-sur-cartes" v-bind:class="{ active: $route.path === '/documents-debats-sur-cartes' }">Mes documents de débat sur carte</router-link>
|
<router-link to="/documents-debats-sur-cartes" v-bind:class="{ active: $route.path === '/documents-debats-sur-cartes' }">Mes documents dc</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-if="user.is('USER')">
|
||||||
<router-link to="/debats-sur-cartes" v-bind:class="{ active: $route.path === '/debats-sur-cartes' }">Mes débats sur carte</router-link>
|
<router-link to="/debats-sur-cartes" v-bind:class="{ active: $route.path === '/debats-sur-cartes' }">Débats sur carte</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="right">
|
<ul class="right">
|
||||||
|
|
Loading…
Reference in a new issue