--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="200"
+ height="200"
+ viewBox="-100 -100 200 200"
+ version="1.1"
+ id="svg4"
+ sodipodi:docname="circle.svg"
+ inkscape:version="0.92.4 5da689c313, 2019-01-14">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="960"
+ inkscape:window-height="1060"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="1.18"
+ inkscape:cx="101.69492"
+ inkscape:cy="100"
+ inkscape:window-x="0"
+ inkscape:window-y="20"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg4" />
+ <circle
+ r="91.875"
+ id="circle2"
+ cx="0"
+ cy="0"
+ style="fill:none;stroke:#5f0e78;stroke-width:15.75px" />
+</svg>
-<?xml version="1.0" standalone="yes"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Mark possible moves - vchess" viewbox="0 0 599 599" width="600" height="600">
- <circle id="mark_circle" fill="#90c" cx="300" cy="300" r="200" />
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Mark possible moves - vchess"
+ viewbox="0 0 599 599"
+ width="600"
+ height="600"
+ sodipodi:docname="mark.svg"
+ inkscape:version="0.92.4 5da689c313, 2019-01-14">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="960"
+ inkscape:window-height="1060"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="0.39333333"
+ inkscape:cx="305.08475"
+ inkscape:cy="300"
+ inkscape:window-x="0"
+ inkscape:window-y="20"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Mark possible moves - vchess" />
+ <circle
+ id="mark_circle"
+ cx="300"
+ cy="300"
+ r="180"
+ style="fill:#9900cc;stroke-width:0.89999998" />
</svg>
img.piece
width: 100%
-img.piece, img.mark-square
+img.piece, img.mark-square, img.circle-square
max-width: 100%
height: auto
display: block
img.mark-square
- opacity: 0.6
+ opacity: .7
width: 76%
position: absolute
top: 12%
left: 12%
- opacity: .7
+
+img.circle-square
+ opacity: 0.7
+ width: 100%
+ position: absolute
+ top: 0
+ left: 0
.in-shadow
filter: brightness(50%)
@showrules="showRules"
@analyze="analyzePosition"
@goto-move="gotoMove"
+ @reset-arrows="resetArrows"
)
.clearer
</template>
if (e.deltaY < 0) this.undo();
else if (e.deltaY > 0) this.play();
},
+ resetArrows: function() {
+ // TODO: make arrows scale with board, and remove this
+ this.$refs["board"].cancelResetArrows();
+ },
showRules: function() {
//this.$router.push("/variants/" + this.game.vname);
window.open("#/variants/" + this.game.vname, "_blank"); //better
this.game.vname +
"/?fen=" +
this.vr.getFen().replace(/ /g, "_");
- if (this.game.mycolor)
- newUrl += "&side=" + this.game.mycolor;
- // Open in same tab in live games (against cheating)
- if (this.game.type == "live") this.$router.push(newUrl);
- else window.open("#" + newUrl);
+ if (!!this.game.mycolor) newUrl += "&side=" + this.game.mycolor;
+ window.open("#" + newUrl);
},
download: function() {
const content = this.getPgn();
pgn += '\n';
for (let i = 0; i < this.moves.length; i += 2) {
if (i > 0) pgn += " ";
- pgn += (i/2+1) + "." + getFullNotation(this.moves[i]);
+ // Adjust dots notation for a better display:
+ let fullNotation = getFullNotation(this.moves[i]);
+ if (fullNotation == "...") fullNotation = "..";
+ pgn += (i/2+1) + "." + fullNotation;
if (i+1 < this.moves.length)
pgn += " " + getFullNotation(this.moves[i+1]);
}
pgn += "\n\n";
for (let i = 0; i < this.moves.length; i += 2) {
- pgn += getFullNotation(this.moves[i], "unambiguous") + "\n";
- if (i+1 < this.moves.length)
- pgn += getFullNotation(this.moves[i+1], "unambiguous") + "\n";
+ const moveNumber = i / 2 + 1;
+ pgn += moveNumber + "." + i + " " +
+ getFullNotation(this.moves[i], "unambiguous") + "\n";
+ if (i+1 < this.moves.length) {
+ pgn += moveNumber + "." + (i+1) + " " +
+ getFullNotation(this.moves[i+1], "unambiguous") + "\n";
+ }
}
return pgn;
},
choices: [], //promotion pieces, or checkered captures... (as moves)
selectedPiece: null, //moving piece (or clicked piece)
start: null, //pixels coordinates + id of starting square (click or drag)
+ startArrow: null,
+ movingArrow: { x: -1, y: -1 },
+ arrows: [], //object of {start: x,y / end: x,y}
+ circles: {}, //object of squares' ID --> true (TODO: use a set?)
click: "",
clickTime: 0,
settings: store.state.settings
},
[...Array(sizeY).keys()].map(j => {
const cj = orientation == "w" ? j : sizeY - j - 1;
+ const squareId = "sq-" + ci + "-" + cj;
let elems = [];
if (showPiece(ci, cj)) {
elems.push(
piece: true,
ghost:
!!this.selectedPiece &&
- this.selectedPiece.parentNode.id == "sq-" + ci + "-" + cj
+ this.selectedPiece.parentNode.id == squareId
},
attrs: {
src:
})
);
}
+ if (!!this.circles[squareId]) {
+ elems.push(
+ h("img", {
+ "class": {
+ "circle-square": true
+ },
+ attrs: {
+ src: "/images/circle.svg"
+ }
+ })
+ );
+ }
const lightSquare = (ci + cj) % 2 == lightSquareMod;
return h(
"div",
);
elementArray.unshift(choices);
}
+ if (
+ !this.mobileBrowser &&
+ (this.arrows.length > 0 || this.movingArrow.x >= 0)
+ ) {
+ let svgArrows = [];
+ this.arrows.forEach(a => {
+ svgArrows.push(
+ h(
+ "path",
+ {
+ "class": { "svg-arrow": true },
+ attrs: {
+ d: (
+ "M" + a.start.x + "," + a.start.y + " " +
+ "L" + a.end.x + "," + a.end.y
+ )
+ }
+ }
+ )
+ );
+ });
+ if (this.movingArrow.x >= 0) {
+ svgArrows.push(
+ h(
+ "path",
+ {
+ "class": { "svg-arrow": true },
+ attrs: {
+ d: (
+ "M" + this.startArrow.x + "," + this.startArrow.y + " " +
+ "L" + this.movingArrow.x + "," + this.movingArrow.y
+ )
+ }
+ }
+ )
+ );
+ }
+ // Add SVG element for drawing arrows
+ elementArray.push(
+ h(
+ "svg",
+ {
+ attrs: {
+ id: "arrowCanvas",
+ stroke: "none"
+ }
+ },
+ [
+ h(
+ "defs",
+ {},
+ [
+ h(
+ "marker",
+ {
+ attrs: {
+ id: "arrow",
+ markerWidth: "2",
+ markerHeight: "2",
+ markerUnits: "strokeWidth",
+ refX: "0",
+ refY: "1",
+ orient: "auto"
+ }
+ },
+ [
+ h(
+ "path",
+ {
+ attrs: {
+ d: "M0,0 L0,2 L2,1 z",
+ style: "fill: blue"
+ }
+ }
+ )
+ ]
+ )
+ ]
+ )
+ ].concat(svgArrows)
+ )
+ );
+ }
let onEvents = {};
// NOTE: click = mousedown + mouseup
if (this.mobileBrowser) {
on: {
mousedown: this.mousedown,
mousemove: this.mousemove,
- mouseup: this.mouseup
+ mouseup: this.mouseup,
+ contextmenu: this.blockContextMenu
}
};
}
return h("div", onEvents, elementArray);
},
methods: {
+ blockContextMenu: function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ },
+ cancelResetArrows: function() {
+ this.startArrow = null;
+ this.arrows = [];
+ this.circles = {};
+ },
mousedown: function(e) {
+ if (!([1, 3].includes(e.which))) return;
e.preventDefault();
- if (!this.start) {
- // NOTE: classList[0] is enough: 'piece' is the first assigned class
- const withPiece = e.target.classList[0] == "piece";
- // Emit the click event which could be used by some variants
- this.$emit(
- "click-square",
- getSquareFromId(withPiece ? e.target.parentNode.id : e.target.id)
- );
- // Start square must contain a piece.
- if (!withPiece) return;
- let parent = e.target.parentNode; //surrounding square
- // Show possible moves if current player allowed to play
- const startSquare = getSquareFromId(parent.id);
- this.possibleMoves = [];
- const color = this.analyze ? this.vr.turn : this.userColor;
- if (this.vr.canIplay(color, startSquare))
- this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
- // For potential drag'n drop, remember start coordinates
- // (to center the piece on mouse cursor)
- let rect = parent.getBoundingClientRect();
- this.start = {
+ if (e.which != 3)
+ // Cancel current drawing and circles, if any
+ this.cancelResetArrows();
+ if (e.which == 1 || this.mobileBrowser) {
+ // Mouse left button
+ if (!this.start) {
+ // NOTE: classList[0] is enough: 'piece' is the first assigned class
+ const withPiece = (e.target.classList[0] == "piece");
+ // Emit the click event which could be used by some variants
+ this.$emit(
+ "click-square",
+ getSquareFromId(withPiece ? e.target.parentNode.id : e.target.id)
+ );
+ // Start square must contain a piece.
+ if (!withPiece) return;
+ let parent = e.target.parentNode; //surrounding square
+ // Show possible moves if current player allowed to play
+ const startSquare = getSquareFromId(parent.id);
+ this.possibleMoves = [];
+ const color = this.analyze ? this.vr.turn : this.userColor;
+ if (this.vr.canIplay(color, startSquare))
+ this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
+ // For potential drag'n drop, remember start coordinates
+ // (to center the piece on mouse cursor)
+ const rect = parent.getBoundingClientRect();
+ this.start = {
+ x: rect.x + rect.width / 2,
+ y: rect.y + rect.width / 2,
+ id: parent.id
+ };
+ // Add the moving piece to the board, just after current image
+ this.selectedPiece = e.target.cloneNode();
+ Object.assign(
+ this.selectedPiece.style,
+ {
+ position: "absolute",
+ top: 0,
+ display: "inline-block",
+ zIndex: 3000
+ }
+ );
+ parent.insertBefore(this.selectedPiece, e.target.nextSibling);
+ } else {
+ this.processMoveAttempt(e);
+ }
+ } else {
+ // e.which == 3 : mouse right button
+ let elem = e.target;
+ // Next loop because of potential marks
+ while (elem.tagName == "IMG") elem = elem.parentNode;
+ // To center the arrow in square:
+ const rect = elem.getBoundingClientRect();
+ this.startArrow = {
x: rect.x + rect.width / 2,
y: rect.y + rect.width / 2,
- id: parent.id
+ id: elem.id
};
- // Add the moving piece to the board, just after current image
- this.selectedPiece = e.target.cloneNode();
+ }
+ },
+ mousemove: function(e) {
+ if (!this.selectedPiece && !this.startArrow) return;
+ e.preventDefault();
+ if (!!this.selectedPiece) {
+ // There is an active element: move it around
+ const [offsetX, offsetY] =
+ this.mobileBrowser
+ ? [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
+ : [e.clientX, e.clientY];
Object.assign(
this.selectedPiece.style,
{
- position: "absolute",
- top: 0,
- display: "inline-block",
- zIndex: 3000
+ left: offsetX - this.start.x + "px",
+ top: offsetY - this.start.y + "px"
}
);
- parent.insertBefore(this.selectedPiece, e.target.nextSibling);
- } else {
- this.processMoveAttempt(e);
}
- },
- mousemove: function(e) {
- if (!this.selectedPiece) return;
- e.preventDefault();
- // There is an active element: move it around
- const [offsetX, offsetY] =
- this.mobileBrowser
- ? [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
- : [e.clientX, e.clientY];
- Object.assign(
- this.selectedPiece.style,
- {
- left: offsetX - this.start.x + "px",
- top: offsetY - this.start.y + "px"
+ else {
+ let elem = e.target;
+ // Next loop because of potential marks
+ while (elem.tagName == "IMG") elem = elem.parentNode;
+ // To center the arrow in square:
+ if (elem.id != this.startArrow.id) {
+ const rect = elem.getBoundingClientRect();
+ this.movingArrow = {
+ x: rect.x + rect.width / 2,
+ y: rect.y + rect.width / 2
+ };
}
- );
+ }
},
mouseup: function(e) {
- if (!this.selectedPiece) return;
+ if (!([1, 3].includes(e.which))) return;
e.preventDefault();
- // Drag'n drop. Selected piece is no longer needed:
- this.selectedPiece.parentNode.removeChild(this.selectedPiece);
- delete this.selectedPiece;
- this.selectedPiece = null;
- this.processMoveAttempt(e);
+ if (e.which == 1) {
+ if (!this.selectedPiece) return;
+ // Drag'n drop. Selected piece is no longer needed:
+ this.selectedPiece.parentNode.removeChild(this.selectedPiece);
+ delete this.selectedPiece;
+ this.selectedPiece = null;
+ this.processMoveAttempt(e);
+ } else {
+ // Mouse right button (e.which == 3)
+ this.movingArrow = { x: -1, y: -1 };
+ this.processArrowAttempt(e);
+ }
},
processMoveAttempt: function(e) {
// Obtain the move from start and end squares
} else if (moves.length == 1) this.play(moves[0]);
// else: forbidden move attempt
},
+ processArrowAttempt: function(e) {
+ // Obtain the arrow from start and end squares
+ const [offsetX, offsetY] = [e.clientX, e.clientY];
+ let landing = document.elementFromPoint(offsetX, offsetY);
+ // Next condition: classList.contains(piece) fails because of marks
+ while (landing.tagName == "IMG") landing = landing.parentNode;
+ if (this.startArrow.id == landing.id)
+ // Draw (or erase) a circle
+ this.$set(this.circles, landing.id, !this.circles[landing.id]);
+ else {
+ // OK: add arrow, landing is a new square
+ const rect = landing.getBoundingClientRect();
+ this.arrows.push({
+ start: {
+ x: this.startArrow.x,
+ y: this.startArrow.y
+ },
+ end: {
+ x: rect.x + rect.width / 2,
+ y: rect.y + rect.width / 2
+ }
+ });
+ }
+ this.startArrow = null;
+ },
findMatchingMoves: function(endSquare) {
// Run through moves list and return the matching set (if promotions...)
return (
opacity: 0.5
top: 0
+#arrowCanvas
+ pointer-events: none
+ position: absolute
+ top: 0
+ left: 0
+ width: 100%
+ height: 100%
+
+.svg-arrow
+ opacity: 0.65
+ stroke: #5f0e78
+ stroke-width: 10px
+ fill: none
+ marker-end: url(#arrow)
+
.incheck-light
background-color: rgba(204, 51, 0, 0.7) !important
.incheck-dark
adjustBoard: function() {
const boardContainer = document.getElementById("boardContainer");
if (!boardContainer) return; //no board on page
+ let arrows = document.getElementById("arrowCanvas");
+ // TODO: arrows on board don't scale
+ if (!!arrows) this.$emit("reset-arrows");
const k = document.getElementById("boardSize").value;
const movesWidth = window.innerWidth >= 768 ? 280 : 0;
const minBoardWidth = 240; //TODO: these 240 and 280 are arbitrary...
)
img(src="/images/icons/rematch.svg")
#playersInfo
- p
+ p(v-if="largeScreen")
span.name(:class="{connected: isConnected(0)}")
| {{ game.players[0].name || "@nonymous" }}
span.time(
span.time-separator(v-if="!!virtualClocks[1][1]") :
span.time-right(v-if="!!virtualClocks[1][1]")
| {{ virtualClocks[1][1] }}
+ p(v-else)
+ span.name(:class="{connected: isConnected(0)}")
+ | {{ game.players[0].name || "@nonymous" }}
+ span.split-names -
+ span.name(:class="{connected: isConnected(1)}")
+ | {{ game.players[1].name || "@nonymous" }}
+ br
+ span.time(
+ v-if="game.score=='*'"
+ :class="{yourturn: !!vr && vr.turn == 'w'}"
+ )
+ span.time-left {{ virtualClocks[0][0] }}
+ span.time-separator(v-if="!!virtualClocks[0][1]") :
+ span.time-right(v-if="!!virtualClocks[0][1]")
+ | {{ virtualClocks[0][1] }}
+ span.time(
+ v-if="game.score=='*'"
+ :class="{yourturn: !!vr && vr.turn == 'b'}"
+ )
+ span.time-left {{ virtualClocks[1][0] }}
+ span.time-separator(v-if="!!virtualClocks[1][1]") :
+ span.time-right(v-if="!!virtualClocks[1][1]")
+ | {{ virtualClocks[1][1] }}
BaseGame(
ref="basegame"
:game="game"
retrySendmove: null,
clockUpdate: null,
// Related to (killing of) self multi-connects:
- newConnect: {},
- killed: {}
+ newConnect: {}
};
},
watch: {
this.retrySendmove = null;
this.clockUpdate = null;
this.newConnect = {};
- this.killed = {};
// 1] Initialize connection
this.connexionString =
params.socketUrl +
}
}
);
- this.newConnect[data.from] = true; //for self multi-connects tests
+ // For self multi-connects tests:
+ this.newConnect[data.from[0]] = true;
this.send("askidentity", { target: data.from[0] });
} else {
this.people[data.from[0]].tmpIds[data.from[1]] = { focus: true };
}
break;
}
- case "killed":
- // I logged in elsewhere:
- this.conn.removeEventListener("message", this.socketMessageListener);
- this.conn.removeEventListener("close", this.socketCloseListener);
- this.conn = null;
- alert(this.st.tr["New connexion detected: tab now offline"]);
- break;
case "askidentity": {
// Request for identification
const me = {
this.$forceUpdate(); //TODO: shouldn't be required
// If I multi-connect, kill current connexion if no mark (I'm older)
if (this.newConnect[user.sid]) {
+ delete this.newConnect[user.sid];
if (
user.id > 0 &&
user.id == this.st.user.id &&
- user.sid != this.st.user.sid &&
- !this.killed[this.st.user.sid]
+ user.sid != this.st.user.sid
) {
- this.send("killme", { sid: this.st.user.sid });
- this.killed[this.st.user.sid] = true;
+ this.cleanBeforeDestroy();
+ alert(this.st.tr["New connexion detected: tab now offline"]);
+ break;
}
- delete this.newConnect[user.sid];
}
- if (!this.killed[this.st.user.sid]) {
- // Ask potentially missed last state, if opponent and I play
- if (
- !this.gotLastate &&
- !!this.game.mycolor &&
- this.game.type == "live" &&
- this.game.score == "*" &&
- this.game.players.some(p => p.sid == user.sid)
- ) {
- this.send("asklastate", { target: user.sid });
- let counter = 1;
- this.askLastate = setInterval(
- () => {
- // Ask at most 3 times:
- // if no reply after that there should be a network issue.
- if (
- counter < 3 &&
- !this.gotLastate &&
- !!this.people[user.sid]
- ) {
- this.send("asklastate", { target: user.sid });
- counter++;
- } else {
- clearInterval(this.askLastate);
- }
- },
- 1500
- );
- }
+ // Ask potentially missed last state, if opponent and I play
+ if (
+ !this.gotLastate &&
+ !!this.game.mycolor &&
+ this.game.type == "live" &&
+ this.game.score == "*" &&
+ this.game.players.some(p => p.sid == user.sid)
+ ) {
+ this.send("asklastate", { target: user.sid });
+ let counter = 1;
+ this.askLastate = setInterval(
+ () => {
+ // Ask at most 3 times:
+ // if no reply after that there should be a network issue.
+ if (
+ counter < 3 &&
+ !this.gotLastate &&
+ !!this.people[user.sid]
+ ) {
+ this.send("asklastate", { target: user.sid });
+ counter++;
+ } else {
+ clearInterval(this.askLastate);
+ }
+ },
+ 1500
+ );
}
break;
}
connexionString: "",
socketCloseListener: 0,
// Related to (killing of) self multi-connects:
- newConnect: {},
- killed: {}
+ newConnect: {}
};
},
watch: {
const page = data.page || "/";
if (data.code == "connect") {
// Ask challenges only on first connexion:
- if (!this.people[data.from])
+ if (!this.people[data.from[0]])
this.send("askchallenges", { target: data.from[0] });
}
// Ask game only if live:
else if (!page.match(/\/[0-9]+$/))
this.send("askgame", { target: data.from[0], page: page });
- if (!this.people[data.from]) {
+ if (!this.people[data.from[0]]) {
this.$set(
this.people,
data.from[0],
}
}
);
- this.newConnect[data.from] = true; //for self multi-connects tests
+ // For self multi-connects tests:
+ this.newConnect[data.from[0]] = true;
this.send("askidentity", { target: data.from[0], page: page });
} else {
this.people[data.from[0]].tmpIds[data.from[1]] =
}
break;
}
- case "killed":
- // I logged in elsewhere:
- this.conn.removeEventListener("message", this.socketMessageListener);
- this.conn.removeEventListener("close", this.socketCloseListener);
- this.conn = null;
- alert(this.st.tr["New connexion detected: tab now offline"]);
- break;
case "askidentity": {
// Request for identification
const me = {
this.$forceUpdate();
// If I multi-connect, kill current connexion if no mark (I'm older)
if (this.newConnect[user.sid]) {
+ delete this.newConnect[user.sid];
if (
user.id > 0 &&
user.id == this.st.user.id &&
- user.sid != this.st.user.sid &&
- !this.killed[this.st.user.sid]
+ user.sid != this.st.user.sid
) {
- this.send("killme", { sid: this.st.user.sid });
- this.killed[this.st.user.sid] = true;
+ // I logged in elsewhere:
+ this.cleanBeforeDestroy();
+ alert(this.st.tr["New connexion detected: tab now offline"]);
}
- delete this.newConnect[user.sid];
}
break;
}
// When page changes:
doDisconnect();
break;
- case "killme": {
- // Self multi-connect: manual removal + disconnect
- const doKill = (pg) => {
- Object.keys(clients[pg][obj.sid]).forEach(x => {
- send(clients[pg][obj.sid][x].socket, { code: "killed" });
- });
- delete clients[pg][obj.sid];
- };
- const disconnectFromOtherConnexion = (pg,code,o={}) => {
- Object.keys(clients[pg]).forEach(k => {
- if (k != obj.sid) {
- Object.keys(clients[pg][k]).forEach(x => {
- send(
- clients[pg][k][x].socket,
- Object.assign({ code: code, from: obj.sid }, o)
- );
- });
- }
- });
- };
- Object.keys(clients).forEach(pg => {
- if (clients[pg][obj.sid]) {
- doKill(pg);
- disconnectFromOtherConnexion(pg, "disconnect");
- if (pg.indexOf("/game/") >= 0 && clients["/"])
- disconnectFromOtherConnexion("/", "gdisconnect", { page: pg });
- }
- });
- break;
- }
case "pollclients": {
// From Game
let sockIds = {};
case "getfocus":
case "losefocus":
- clients[page][sid][tmpId].focus = (obj.code == "getfocus");
+ if (
+ !!clients[page] &&
+ !!clients[page][sid] &&
+ !!clients[page][sid][tmpId]
+ ) {
+ clients[page][sid][tmpId].focus = (obj.code == "getfocus");
+ }
if (page == "/") notifyRoom("/", obj.code, { page: "/" }, [sid]);
else {
// Notify game room + Hall: