Merge branch 'titles' of github.com:xwiki-labs/cryptpad into title
This commit is contained in:
commit
11f63c861c
@ -158,9 +158,11 @@ define([
|
|||||||
|
|
||||||
// append the userlist to the hyperjson structure
|
// append the userlist to the hyperjson structure
|
||||||
obj.metadata = {
|
obj.metadata = {
|
||||||
users: userList,
|
users: userList
|
||||||
title: document.title
|
|
||||||
};
|
};
|
||||||
|
if (!isDefaultTitle()) {
|
||||||
|
obj.metadata.title = document.title;
|
||||||
|
}
|
||||||
|
|
||||||
// set mode too...
|
// set mode too...
|
||||||
obj.highlightMode = module.highlightMode;
|
obj.highlightMode = module.highlightMode;
|
||||||
@ -239,11 +241,15 @@ define([
|
|||||||
return text.trim();
|
return text.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isDefaultTitle = function () {
|
||||||
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
return Cryptpad.isDefaultName(parsed, document.title);
|
||||||
|
};
|
||||||
var suggestName = function () {
|
var suggestName = function () {
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
var name = Cryptpad.getDefaultName(parsed, []);
|
var name = Cryptpad.getDefaultName(parsed, []);
|
||||||
|
|
||||||
if (document.title.slice(0, name.length) === name) {
|
if (Cryptpad.isDefaultName(parsed, document.title)) {
|
||||||
return getHeadingText() || document.title;
|
return getHeadingText() || document.title;
|
||||||
} else {
|
} else {
|
||||||
return document.title || getHeadingText() || name;
|
return document.title || getHeadingText() || name;
|
||||||
@ -328,42 +334,42 @@ define([
|
|||||||
onLocal();
|
onLocal();
|
||||||
}));
|
}));
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
}
|
|
||||||
|
|
||||||
/* add a rename button */
|
/* add a rename button */
|
||||||
var $setTitle = Cryptpad.createButton('rename', true)
|
var $setTitle = Cryptpad.createButton('rename', true)
|
||||||
.click(function () {
|
.click(function () {
|
||||||
var suggestion = suggestName();
|
var suggestion = suggestName();
|
||||||
|
|
||||||
Cryptpad.prompt(Messages.renamePrompt,
|
Cryptpad.prompt(Messages.renamePrompt,
|
||||||
suggestion, function (title, ev) {
|
suggestion, function (title, ev) {
|
||||||
if (title === null) { return; }
|
if (title === null) { return; }
|
||||||
|
|
||||||
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
|
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
|
||||||
if (err) {
|
|
||||||
console.log("Unable to determine if name caused a conflict");
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conflicts) {
|
|
||||||
Cryptpad.alert(Messages.renameConflict);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.setPadTitle(title, function (err, data) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("unable to set pad title");
|
console.log("Unable to determine if name caused a conflict");
|
||||||
console.log(err);
|
console.error(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.title = title;
|
|
||||||
onLocal();
|
if (conflicts) {
|
||||||
|
Cryptpad.alert(Messages.renameConflict);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log("unable to set pad title");
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.title = title;
|
||||||
|
onLocal();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
$rightside.append($setTitle);
|
||||||
$rightside.append($setTitle);
|
}
|
||||||
|
|
||||||
/* add a forget button */
|
/* add a forget button */
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true)
|
var $forgetPad = Cryptpad.createButton('forget', true)
|
||||||
@ -470,7 +476,7 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.title = title || info.channel.slice(0, 8);
|
document.title = title || info.channel.slice(0, 8);
|
||||||
Cryptpad.rememberPad(title, function (err, data) {
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("Unable to set pad title");
|
console.log("Unable to set pad title");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -657,11 +663,13 @@ define([
|
|||||||
var hjson2 = {
|
var hjson2 = {
|
||||||
content: localDoc,
|
content: localDoc,
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userList,
|
users: userList
|
||||||
title: document.title
|
|
||||||
},
|
},
|
||||||
highlightMode: highlightMode,
|
highlightMode: highlightMode,
|
||||||
};
|
};
|
||||||
|
if (!isDefaultTitle()) {
|
||||||
|
hjson2.metadata.title = document.title;
|
||||||
|
}
|
||||||
var shjson2 = stringify(hjson2);
|
var shjson2 = stringify(hjson2);
|
||||||
if (shjson2 !== shjson) {
|
if (shjson2 !== shjson) {
|
||||||
console.error("shjson2 !== shjson");
|
console.error("shjson2 !== shjson");
|
||||||
|
|||||||
@ -137,6 +137,26 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var parseHash = common.parseHash = function (hash) {
|
||||||
|
var parsed = {};
|
||||||
|
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
|
||||||
|
// Old hash
|
||||||
|
parsed.channel = hash.slice(0, 32);
|
||||||
|
parsed.key = hash.slice(32);
|
||||||
|
parsed.version = 0;
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
var hashArr = hash.split('/');
|
||||||
|
if (hashArr[1] && hashArr[1] === '1') {
|
||||||
|
parsed.version = 1;
|
||||||
|
parsed.mode = hashArr[2];
|
||||||
|
parsed.channel = hashArr[3];
|
||||||
|
parsed.key = hashArr[4];
|
||||||
|
parsed.present = hashArr[5] && hashArr[5] === 'present';
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
var getEditHashFromKeys = common.getEditHashFromKeys = function (chanKey, keys) {
|
var getEditHashFromKeys = common.getEditHashFromKeys = function (chanKey, keys) {
|
||||||
if (typeof keys === 'string') {
|
if (typeof keys === 'string') {
|
||||||
return chanKey + keys;
|
return chanKey + keys;
|
||||||
@ -295,6 +315,10 @@ define([
|
|||||||
while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; }
|
while (!isNameAvailable(name + ' - ' + untitledIndex, parsed, recentPads)) { untitledIndex++; }
|
||||||
return name + ' - ' + untitledIndex;
|
return name + ' - ' + untitledIndex;
|
||||||
};
|
};
|
||||||
|
var isDefaultName = common.isDefaultName = function (parsed, title) {
|
||||||
|
var name = getDefaultName(parsed, []);
|
||||||
|
return title.slice(0, name.length) === name;
|
||||||
|
};
|
||||||
|
|
||||||
var makePad = function (href, title) {
|
var makePad = function (href, title) {
|
||||||
var now = ''+new Date();
|
var now = ''+new Date();
|
||||||
@ -397,51 +421,10 @@ define([
|
|||||||
}, legacy);
|
}, legacy);
|
||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE
|
|
||||||
var rememberPad = common.rememberPad = window.rememberPad = function (title, cb) {
|
|
||||||
// bail out early
|
|
||||||
if (!/#/.test(window.location.hash)) { return; }
|
|
||||||
|
|
||||||
getRecentPads(function (err, pads) {
|
|
||||||
if (err) {
|
|
||||||
cb(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = ''+new Date();
|
|
||||||
var href = window.location.href;
|
|
||||||
|
|
||||||
var parsed = parsePadUrl(window.location.href);
|
|
||||||
var isUpdate = false;
|
|
||||||
|
|
||||||
var out = pads.map(function (pad) {
|
|
||||||
var p = parsePadUrl(pad.href);
|
|
||||||
if (p.hash === parsed.hash && p.type === parsed.type) {
|
|
||||||
isUpdate = true;
|
|
||||||
// bump the atime
|
|
||||||
pad.atime = now;
|
|
||||||
|
|
||||||
pad.title = title;
|
|
||||||
pad.href = href;
|
|
||||||
}
|
|
||||||
return pad;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isUpdate) {
|
|
||||||
// href, ctime, atime, title
|
|
||||||
out.push(makePad(href, title));
|
|
||||||
}
|
|
||||||
setRecentPads(out, function (err, data) {
|
|
||||||
cb(err, data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// STORAGE
|
// STORAGE
|
||||||
var setPadTitle = common.setPadTitle = function (name, cb) {
|
var setPadTitle = common.setPadTitle = function (name, cb) {
|
||||||
var href = window.location.href;
|
var href = window.location.href;
|
||||||
var parsed = parsePadUrl(href);
|
var parsed = parsePadUrl(href);
|
||||||
|
|
||||||
getRecentPads(function (err, recent) {
|
getRecentPads(function (err, recent) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
@ -452,7 +435,26 @@ define([
|
|||||||
|
|
||||||
var renamed = recent.map(function (pad) {
|
var renamed = recent.map(function (pad) {
|
||||||
var p = parsePadUrl(pad.href);
|
var p = parsePadUrl(pad.href);
|
||||||
if (p.hash === parsed.hash && p.type === parsed.type) {
|
|
||||||
|
if (p.type !== parsed.type) { return pad; }
|
||||||
|
|
||||||
|
// Version 1 : we have up to 4 differents hash for 1 pad, keep the strongest :
|
||||||
|
// Edit > Edit (present) > View > View (present)
|
||||||
|
var bypass = false;
|
||||||
|
var pHash = parseHash(p.hash);
|
||||||
|
var parsedHash = parseHash(parsed.hash);
|
||||||
|
if (pHash.version === 1 && parsedHash.version === 1 && pHash.channel === parsedHash.channel) {
|
||||||
|
if (pHash.mode === 'view' && parsedHash.mode === 'edit') { bypass = true; }
|
||||||
|
else if (pHash.mode === parsedHash.mode && pHash.present) { bypass = true; }
|
||||||
|
else {
|
||||||
|
// Editing a "weaker" version of a stored hash : update the date and do not push the current hash
|
||||||
|
pad.atime = new Date().toISOString();
|
||||||
|
contains = true;
|
||||||
|
return pad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.hash === parsed.hash || bypass) {
|
||||||
contains = true;
|
contains = true;
|
||||||
// update the atime
|
// update the atime
|
||||||
pad.atime = new Date().toISOString();
|
pad.atime = new Date().toISOString();
|
||||||
|
|||||||
@ -294,6 +294,21 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isDefaultTitle = function () {
|
||||||
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
return Cryptpad.isDefaultName(parsed, document.title);
|
||||||
|
};
|
||||||
|
var suggestName = function () {
|
||||||
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
var name = Cryptpad.getDefaultName(parsed, []);
|
||||||
|
|
||||||
|
if (Cryptpad.isDefaultName(parsed, document.title)) {
|
||||||
|
return getHeadingText() || document.title;
|
||||||
|
} else {
|
||||||
|
return document.title || getHeadingText() || name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var DD = new DiffDom(diffOptions);
|
var DD = new DiffDom(diffOptions);
|
||||||
|
|
||||||
// apply patches, and try not to lose the cursor in the process!
|
// apply patches, and try not to lose the cursor in the process!
|
||||||
@ -312,9 +327,11 @@ define([
|
|||||||
hjson[3] = {
|
hjson[3] = {
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userList,
|
users: userList,
|
||||||
title: document.title
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (!isDefaultTitle()) {
|
||||||
|
hjson[3].metadata.title = document.title;
|
||||||
|
}
|
||||||
return stringify(hjson);
|
return stringify(hjson);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -472,17 +489,6 @@ define([
|
|||||||
})) { return text; }
|
})) { return text; }
|
||||||
};
|
};
|
||||||
|
|
||||||
var suggestName = module.suggestName = function () {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
var name = Cryptpad.getDefaultName(parsed, []);
|
|
||||||
|
|
||||||
if (document.title.slice(0, name.length) === name) {
|
|
||||||
return getHeadingText() || document.title;
|
|
||||||
} else {
|
|
||||||
return document.title || getHeadingText() || name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var exportFile = function () {
|
var exportFile = function () {
|
||||||
var html = getHTML();
|
var html = getHTML();
|
||||||
var suggestion = suggestName();
|
var suggestion = suggestName();
|
||||||
@ -536,34 +542,34 @@ define([
|
|||||||
realtimeOptions.onLocal();
|
realtimeOptions.onLocal();
|
||||||
}));
|
}));
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
}
|
|
||||||
|
|
||||||
/* add a rename button */
|
/* add a rename button */
|
||||||
var $rename = Cryptpad.createButton('rename', true)
|
var $rename = Cryptpad.createButton('rename', true)
|
||||||
.click(function () {
|
.click(function () {
|
||||||
var suggestion = suggestName();
|
var suggestion = suggestName();
|
||||||
|
|
||||||
Cryptpad.prompt(Messages.renamePrompt, suggestion, function (title) {
|
Cryptpad.prompt(Messages.renamePrompt, suggestion, function (title) {
|
||||||
if (title === null) { return; }
|
if (title === null) { return; }
|
||||||
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
|
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
|
||||||
if (conflicts) {
|
if (conflicts) {
|
||||||
Cryptpad.alert(Messages.renameConflict);
|
Cryptpad.alert(Messages.renameConflict);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.setPadTitle(title, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Couldn't set pad title");
|
|
||||||
console.error(err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.title = title;
|
|
||||||
editor.fire('change');
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Couldn't set pad title");
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.title = title;
|
||||||
|
editor.fire('change');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
$rightside.append($rename);
|
||||||
$rightside.append($rename);
|
}
|
||||||
|
|
||||||
/* add a forget button */
|
/* add a forget button */
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true)
|
var $forgetPad = Cryptpad.createButton('forget', true)
|
||||||
@ -602,7 +608,7 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.title = title || info.channel.slice(0, 8);
|
document.title = title || info.channel.slice(0, 8);
|
||||||
Cryptpad.rememberPad(title, function (err, data) {
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("Couldn't remember pad");
|
console.log("Couldn't remember pad");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@ -923,7 +923,7 @@ define([
|
|||||||
Cryptpad.getPadTitle(function (err, title) {
|
Cryptpad.getPadTitle(function (err, title) {
|
||||||
title = document.title = title || info.channel.slice(0, 8);
|
title = document.title = title || info.channel.slice(0, 8);
|
||||||
|
|
||||||
Cryptpad.rememberPad(title, function (err, data) {
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("unable to remember pad");
|
console.log("unable to remember pad");
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|||||||
@ -193,10 +193,11 @@ define([
|
|||||||
|
|
||||||
// append the userlist to the hyperjson structure
|
// append the userlist to the hyperjson structure
|
||||||
obj.metadata = {
|
obj.metadata = {
|
||||||
users: userList,
|
users: userList
|
||||||
title: APP.title
|
|
||||||
};
|
};
|
||||||
|
if (!isDefaultTitle()) {
|
||||||
|
obj.metadata.title = APP.title;
|
||||||
|
}
|
||||||
// stringify the json and send it into chainpad
|
// stringify the json and send it into chainpad
|
||||||
var shjson = stringify(obj);
|
var shjson = stringify(obj);
|
||||||
|
|
||||||
@ -254,11 +255,15 @@ define([
|
|||||||
return text.trim();
|
return text.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isDefaultTitle = function () {
|
||||||
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
|
return Cryptpad.isDefaultName(parsed, APP.title);
|
||||||
|
};
|
||||||
var suggestName = function () {
|
var suggestName = function () {
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
||||||
var name = Cryptpad.getDefaultName(parsed, []);
|
var name = Cryptpad.getDefaultName(parsed, []);
|
||||||
|
|
||||||
if (APP.title.slice(0, name.length) === name) {
|
if (Cryptpad.isDefaultName(parsed, APP.title)) {
|
||||||
return getHeadingText() || APP.title;
|
return getHeadingText() || APP.title;
|
||||||
} else {
|
} else {
|
||||||
return APP.title || getHeadingText() || name;
|
return APP.title || getHeadingText() || name;
|
||||||
@ -343,43 +348,43 @@ define([
|
|||||||
onLocal();
|
onLocal();
|
||||||
}));
|
}));
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
}
|
|
||||||
|
|
||||||
/* add a rename button */
|
/* add a rename button */
|
||||||
var $setTitle = Cryptpad.createButton('rename', true)
|
var $setTitle = Cryptpad.createButton('rename', true)
|
||||||
.click(function () {
|
.click(function () {
|
||||||
var suggestion = suggestName();
|
var suggestion = suggestName();
|
||||||
|
|
||||||
Cryptpad.prompt(Messages.renamePrompt,
|
Cryptpad.prompt(Messages.renamePrompt,
|
||||||
suggestion, function (title, ev) {
|
suggestion, function (title, ev) {
|
||||||
if (title === null) { return; }
|
if (title === null) { return; }
|
||||||
|
|
||||||
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
|
Cryptpad.causesNamingConflict(title, function (err, conflicts) {
|
||||||
if (err) {
|
|
||||||
console.log("Unable to determine if name caused a conflict");
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conflicts) {
|
|
||||||
Cryptpad.alert(Messages.renameConflict);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.setPadTitle(title, function (err, data) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("unable to set pad title");
|
console.log("Unable to determine if name caused a conflict");
|
||||||
console.log(err);
|
console.error(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
APP.title = title;
|
|
||||||
setTabTitle();
|
if (conflicts) {
|
||||||
onLocal();
|
Cryptpad.alert(Messages.renameConflict);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log("unable to set pad title");
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
APP.title = title;
|
||||||
|
setTabTitle();
|
||||||
|
onLocal();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
$rightside.append($setTitle);
|
||||||
$rightside.append($setTitle);
|
}
|
||||||
|
|
||||||
/* add a forget button */
|
/* add a forget button */
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true)
|
var $forgetPad = Cryptpad.createButton('forget', true)
|
||||||
@ -485,7 +490,7 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.title = APP.title = title || info.channel.slice(0, 8);
|
document.title = APP.title = title || info.channel.slice(0, 8);
|
||||||
Cryptpad.rememberPad(title, function (err, data) {
|
Cryptpad.setPadTitle(title, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("Unable to set pad title");
|
console.log("Unable to set pad title");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -684,11 +689,13 @@ define([
|
|||||||
var hjson2 = {
|
var hjson2 = {
|
||||||
content: localDoc,
|
content: localDoc,
|
||||||
metadata: {
|
metadata: {
|
||||||
users: userList,
|
users: userList
|
||||||
title: APP.title
|
|
||||||
},
|
},
|
||||||
highlightMode: highlightMode,
|
highlightMode: highlightMode,
|
||||||
};
|
};
|
||||||
|
if (!isDefaultTitle()) {
|
||||||
|
hjson2.metadata.title = APP.title;
|
||||||
|
}
|
||||||
var shjson2 = stringify(hjson2);
|
var shjson2 = stringify(hjson2);
|
||||||
if (shjson2 !== shjson) {
|
if (shjson2 !== shjson) {
|
||||||
console.error("shjson2 !== shjson");
|
console.error("shjson2 !== shjson");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user