Improved save/load
This commit is contained in:
parent
47151452b4
commit
06968a57d2
@ -1,14 +1,14 @@
|
|||||||
body { background:#fff; padding: 5px; font-family: 'input_mono_regular'; -webkit-user-select: none; overflow: hidden;}
|
body { padding: 5px; font-family: 'input_mono_regular'; -webkit-user-select: none; overflow: hidden;}
|
||||||
#app { display: flex; flex-direction: column; align-items: center;}
|
#app { display: flex; flex-direction: column; align-items: center;}
|
||||||
|
|
||||||
#wrapper { padding: 25px; padding-bottom: 15px; background: inherit;-webkit-app-region: drag;}
|
#wrapper { padding: 25px; padding-bottom: 15px; -webkit-app-region: drag;}
|
||||||
#dotgrid { margin:0px auto; position:relative; overflow: hidden; padding:10px;-webkit-app-region: no-drag; width:310px; height:310px; }
|
#dotgrid { margin:0px auto; position:relative; overflow: hidden; padding:10px;-webkit-app-region: no-drag; width:310px; height:310px; }
|
||||||
#cursor { opacity: 1; transition: all 50ms; width:8px; height:8px; position:absolute; z-index:25; border-radius:5px; border:1px solid black;}
|
#cursor { opacity: 1; transition: all 50ms; width:8px; height:8px; position:absolute; z-index:25; border-radius:5px; border:1px solid black;}
|
||||||
#cursor_coord { font-size:10px; z-index: 10000; margin-left:15px; margin-top:-2px;}
|
#cursor_coord { font-size:10px; z-index: 10000; margin-left:15px; margin-top:-2px;}
|
||||||
#cursor_coord.left { margin-left:-110px; text-align: right; width:100px; }
|
#cursor_coord.left { margin-left:-110px; text-align: right; width:100px; }
|
||||||
#cursor_from { width:4px; height:4px; background:white; margin-top:2px; margin-left:2px; position:absolute; z-index:2500; border-radius:10px; left:-100px;border:1px solid black;}
|
#cursor_from { width:4px; height:4px; margin-top:2px; margin-left:2px; position:absolute; z-index:2500; border-radius:10px; left:-100px;border:1px solid black;}
|
||||||
#cursor_to { width:4px; height:4px; background:white; margin-top:2px; margin-left:2px; position:absolute; z-index:2500; border-radius:10px; left:-100px; border:1px solid black;}
|
#cursor_to { width:4px; height:4px; margin-top:2px; margin-left:2px; position:absolute; z-index:2500; border-radius:10px; left:-100px; border:1px solid black;}
|
||||||
#cursor_end { width:4px; height:4px; background:white; margin-top:2px; margin-left:2px; position:absolute; z-index:2500; border-radius:10px; left:-100px; border:1px solid black;}
|
#cursor_end { width:4px; height:4px; margin-top:2px; margin-left:2px; position:absolute; z-index:2500; border-radius:10px; left:-100px; border:1px solid black;}
|
||||||
|
|
||||||
#guide,#widgets { position: absolute;width: 300px;height: 300px; margin-left: -5px; margin-top: -5px;}
|
#guide,#widgets { position: absolute;width: 300px;height: 300px; margin-left: -5px; margin-top: -5px;}
|
||||||
#widgets { z-index: 9000; margin-left: 0; margin-top: 0; }
|
#widgets { z-index: 9000; margin-left: 0; margin-top: 0; }
|
||||||
@ -33,3 +33,26 @@ svg.vector { z-index: 1000;position: relative; left:10px; top:10px; width:300px;
|
|||||||
#interface.visible { bottom:20px; opacity: 1 }
|
#interface.visible { bottom:20px; opacity: 1 }
|
||||||
|
|
||||||
#preview { position: absolute; top:20px; left:20px; stroke-dasharray: 4,4; }
|
#preview { position: absolute; top:20px; left:20px; stroke-dasharray: 4,4; }
|
||||||
|
|
||||||
|
/* Theme Defaults */
|
||||||
|
|
||||||
|
:root { --background: "#222"; --f_high: "#fff";--f_med: "#777";--f_low: "#444";--f_inv: "#000";--b_high: "#000";--b_med: "#affec7";--b_low: "#000";--b_inv: "#affec7"; }
|
||||||
|
|
||||||
|
body { background:var(--background) !important; }
|
||||||
|
.fh { color:var(--f_high) !important; stroke:var(--f_high) !important; }
|
||||||
|
.fm { color:var(--f_med) !important ; stroke:var(--f_med) !important; }
|
||||||
|
.fl { color:var(--f_low) !important ; stroke:var(--f_low) !important; }
|
||||||
|
.f_inv { color:var(--f_inv) !important ; stroke:var(--f_inv) !important; }
|
||||||
|
.f_inv { color:var(--f_inv) !important ; stroke:var(--f_inv) !important; }
|
||||||
|
.bh { background:var(--b_high) !important; }
|
||||||
|
.bm { background:var(--b_med) !important ; }
|
||||||
|
.bl { background:var(--b_low) !important ; }
|
||||||
|
.b_inv { background:var(--b_inv) !important ; ; }
|
||||||
|
.icon { color:var(--f_high) !important; stroke:var(--f_high) !important; }
|
||||||
|
#dotgrid svg.vector { stroke:var(--f_high) !important; }
|
||||||
|
#dotgrid #preview { stroke:var(--f_high) !important; }
|
||||||
|
#dotgrid #cursor { border-color:var(--f_med); }
|
||||||
|
#dotgrid #cursor_from { background:var(--f_low); border-color:var(--f_low) !important; }
|
||||||
|
#dotgrid #cursor_to { background:var(--f_low); border-color:var(--f_low) !important; }
|
||||||
|
#dotgrid #cursor_end { background:var(--f_low); border-color:var(--f_low) !important; }
|
||||||
|
|
@ -52,8 +52,6 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
|
|
||||||
this.install = function()
|
this.install = function()
|
||||||
{
|
{
|
||||||
document.body.appendChild(this.theme.el);
|
|
||||||
|
|
||||||
document.getElementById("app").appendChild(this.wrapper);
|
document.getElementById("app").appendChild(this.wrapper);
|
||||||
this.wrapper.appendChild(this.element);
|
this.wrapper.appendChild(this.element);
|
||||||
this.element.appendChild(this.guide.el);
|
this.element.appendChild(this.guide.el);
|
||||||
@ -88,7 +86,7 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
|
|
||||||
// Vector
|
// Vector
|
||||||
this.svg_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
this.svg_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||||
this.svg_el.setAttribute("class","vector fh");
|
this.svg_el.setAttribute("class","vector");
|
||||||
this.svg_el.setAttribute("width",this.width+"px");
|
this.svg_el.setAttribute("width",this.width+"px");
|
||||||
this.svg_el.setAttribute("height",this.height+"px");
|
this.svg_el.setAttribute("height",this.height+"px");
|
||||||
this.svg_el.setAttribute("xmlns","http://www.w3.org/2000/svg");
|
this.svg_el.setAttribute("xmlns","http://www.w3.org/2000/svg");
|
||||||
@ -104,7 +102,7 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
// Preview
|
// Preview
|
||||||
this.preview_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
this.preview_el = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||||
this.preview_el.id = "preview"
|
this.preview_el.id = "preview"
|
||||||
this.preview_el.setAttribute("class","vector fh");
|
this.preview_el.setAttribute("class","vector");
|
||||||
this.preview_el.setAttribute("width",this.width+"px");
|
this.preview_el.setAttribute("width",this.width+"px");
|
||||||
this.preview_el.setAttribute("height",this.height+"px");
|
this.preview_el.setAttribute("height",this.height+"px");
|
||||||
this.preview_el.setAttribute("xmlns","http://www.w3.org/2000/svg");
|
this.preview_el.setAttribute("xmlns","http://www.w3.org/2000/svg");
|
||||||
@ -127,7 +125,9 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
this.guide.start();
|
this.guide.start();
|
||||||
this.interface.start();
|
this.interface.start();
|
||||||
|
|
||||||
dotgrid.set_size({width:300,height:300})
|
window.addEventListener('drop', dotgrid.drag);
|
||||||
|
|
||||||
|
dotgrid.set_size({width:300,height:300});
|
||||||
this.draw();
|
this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,50 +534,49 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
{
|
{
|
||||||
if(this.segments.length == 0){ return; }
|
if(this.segments.length == 0){ return; }
|
||||||
this.scale = 1
|
this.scale = 1
|
||||||
this.draw()
|
this.draw();
|
||||||
|
|
||||||
// Override fill color
|
if(dotgrid.fill){ dotgrid.svg_el.style.fill = "black"; dotgrid.render.draw(); }
|
||||||
if(dotgrid.fill){ dotgrid.svg_el.style.fill = "black" }
|
|
||||||
|
|
||||||
var svg = this.svg_el.outerHTML
|
var svg = dotgrid.svg_el.outerHTML;
|
||||||
|
|
||||||
dialog.showSaveDialog((fileName) => {
|
dialog.showSaveDialog((fileName) => {
|
||||||
if (fileName === undefined){ return; }
|
if (fileName === undefined){ return; }
|
||||||
fs.writeFile(fileName+".svg", svg, (err) => {
|
fs.writeFile(fileName+".svg", svg);
|
||||||
if(err){ alert("An error ocurred creating the file "+ err.message); return; }
|
|
||||||
});
|
|
||||||
fs.writeFile(fileName+'.png', dotgrid.render.buffer());
|
fs.writeFile(fileName+'.png', dotgrid.render.buffer());
|
||||||
fs.writeFile(fileName+'.dot', JSON.stringify(dotgrid.serializer.serialize()));
|
fs.writeFile(fileName+'.dot', JSON.stringify(dotgrid.serializer.serialize()));
|
||||||
dotgrid.draw()
|
dotgrid.draw()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.load = function()
|
this.open = function()
|
||||||
{
|
{
|
||||||
this.scale = 1;
|
var paths = dialog.showOpenDialog({properties: ['openFile'],filters:[{name:"Dotgrid Image",extensions:["dot"]}]});
|
||||||
this.width = 300;
|
|
||||||
this.height = 300;
|
|
||||||
|
|
||||||
dialog.showOpenDialog({
|
if(!paths){ console.log("Nothing to load"); return; }
|
||||||
openFile: true,
|
|
||||||
openDirectory: false,
|
fs.readFile(paths[0], 'utf-8', (err, data) => {
|
||||||
multiSelections: false,
|
if(err){ alert("An error ocurred reading the file :" + err.message); return; }
|
||||||
filters: [
|
|
||||||
{ name: "Dotgrid Image", extensions: ["dot"] },
|
|
||||||
{ name: "All Files", extensions: ["*"] }
|
|
||||||
]
|
|
||||||
}, (filePaths) => {
|
|
||||||
if (filePaths === undefined || filePaths.length === 0)
|
|
||||||
return;
|
|
||||||
fs.readFile(filePaths[0], (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
alert("An error ocurred creating the file " + err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dotgrid.serializer.deserialize(JSON.parse(data.toString().trim()));
|
dotgrid.serializer.deserialize(JSON.parse(data.toString().trim()));
|
||||||
dotgrid.draw();
|
dotgrid.draw();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
this.drag = function(e)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var file = e.dataTransfer.files[0];
|
||||||
|
|
||||||
|
if(!file.name || !file.name.indexOf(".dot") < 0){ console.log("Dotgrid","Not a dot file"); return; }
|
||||||
|
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function(e){
|
||||||
|
dotgrid.serializer.deserialize(JSON.parse(e.target.result.toString().trim()));
|
||||||
|
dotgrid.draw();
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.copy = function(e)
|
this.copy = function(e)
|
||||||
@ -586,21 +585,12 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
this.scale = 1
|
this.scale = 1
|
||||||
this.width = 300
|
this.width = 300
|
||||||
this.height = 300
|
this.height = 300
|
||||||
this.draw()
|
this.draw();
|
||||||
|
|
||||||
var svg = this.svg_el.outerHTML
|
var svg = this.svg_el.outerHTML
|
||||||
|
|
||||||
e.clipboardData.items.add(JSON.stringify(this.serializer.serialize()), "text/plain");
|
e.clipboardData.items.add(JSON.stringify(this.serializer.serialize()), "text/plain");
|
||||||
|
|
||||||
e.clipboardData.items.add(svg, "text/html");
|
e.clipboardData.items.add(svg, "text/html");
|
||||||
e.clipboardData.items.add(svg, "text/svg+xml");
|
e.clipboardData.items.add(svg, "text/svg+xml");
|
||||||
|
|
||||||
// Right now, the following doesn't work and breaks "text/plain".
|
|
||||||
// This seems to be a bug in Chromium as others around the web complain, too.
|
|
||||||
/*
|
|
||||||
e.clipboardData.items.add(new File([new Blob([svg], { type: "image/svg+xml" } )], "image.svg"));
|
|
||||||
e.clipboardData.items.add(new File([new Blob([Uint8Array.from(dotgrid.render.buffer()).buffer], { type: "image/png" } ) ], "image.png"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,51 +648,14 @@ function Dotgrid(width,height,grid_x,grid_y,block_x,block_y,thickness = 3,lineca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('dragover',function(e)
|
|
||||||
{
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
e.dataTransfer.dropEffect = 'copy';
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('resize', function(e)
|
window.addEventListener('resize', function(e)
|
||||||
{
|
{
|
||||||
dotgrid.draw()
|
dotgrid.draw()
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
window.addEventListener('drop', function(e)
|
window.addEventListener('dragover',function(e)
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
var files = e.dataTransfer.files;
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
|
|
||||||
for(file_id in files){
|
|
||||||
var file = files[file_id];
|
|
||||||
if(file.name.indexOf(".thm") > -1) {
|
|
||||||
var path = file.path;
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function(e){
|
|
||||||
var o = JSON.parse(e.target.result);
|
|
||||||
dotgrid.theme.install(o);
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(file.name.indexOf(".dot") > -1) {
|
|
||||||
var path = file.path;
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function(e){
|
|
||||||
var o = JSON.parse(e.target.result);
|
|
||||||
dotgrid.serializer.deserialize(o);
|
|
||||||
dotgrid.draw();
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("skipped",file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
@ -11,24 +11,28 @@ function Keyboard()
|
|||||||
|
|
||||||
// save
|
// save
|
||||||
if(e.key == "s" && (e.ctrlKey || e.metaKey)){
|
if(e.key == "s" && (e.ctrlKey || e.metaKey)){
|
||||||
|
e.preventDefault();
|
||||||
dotgrid.export();
|
dotgrid.export();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// open
|
// open
|
||||||
if(e.key == "o" && (e.ctrlKey || e.metaKey)){
|
if(e.key == "o" && (e.ctrlKey || e.metaKey)){
|
||||||
dotgrid.load();
|
e.preventDefault();
|
||||||
|
dotgrid.open();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// undo
|
// undo
|
||||||
if(e.key == "z" && (e.ctrlKey || e.metaKey)){
|
if(e.key == "z" && (e.ctrlKey || e.metaKey)){
|
||||||
|
e.preventDefault();
|
||||||
dotgrid.erase();
|
dotgrid.erase();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// new
|
// new
|
||||||
if(e.key == "n" && (e.ctrlKey || e.metaKey)){
|
if(e.key == "n" && (e.ctrlKey || e.metaKey)){
|
||||||
|
e.preventDefault();
|
||||||
dotgrid.clear();
|
dotgrid.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,70 +1,60 @@
|
|||||||
function Theme()
|
function Theme()
|
||||||
{
|
{
|
||||||
this.el = document.createElement("style");
|
this.el = document.createElement("style");
|
||||||
this.active = null;
|
this.el.type = 'text/css';
|
||||||
|
this.default = { background: "#222", f_high: "#fff", f_med: "#777", f_low: "#444", f_inv: "#000", b_high: "#000", b_med: "#affec7", b_low: "#000", b_inv: "#affec7" }
|
||||||
this.default = { background: "#222", f_high: "#fff", f_med: "#777", f_low: "#444", f_inv: "#affec7", b_high: "#000", b_med: "#affec7", b_low: "#000", b_inv: "#affec7" }
|
this.active = this.default;
|
||||||
|
|
||||||
this.start = function()
|
this.start = function()
|
||||||
{
|
{
|
||||||
if(localStorage.theme && is_json(localStorage.theme)){
|
this.load(localStorage.theme ? localStorage.theme : this.default);
|
||||||
console.log("Theme","Found in localStorage")
|
document.head.appendChild(this.el)
|
||||||
this.install(JSON.parse(localStorage.theme));
|
window.addEventListener('drop', dotgrid.theme.drag);
|
||||||
}
|
|
||||||
else{
|
|
||||||
console.log("Theme","Creating new")
|
|
||||||
this.install(this.default);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.save = function()
|
this.load = function(t)
|
||||||
{
|
{
|
||||||
localStorage.setItem("theme", JSON.stringify(this.active));
|
var theme = is_json(t) ? JSON.parse(t) : t;
|
||||||
console.log("Theme","Saved");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.load = function(theme_str)
|
if(!theme.background){ return; }
|
||||||
{
|
|
||||||
if(is_json(theme_str)){
|
|
||||||
this.install(JSON.parse(theme_str));
|
|
||||||
}
|
|
||||||
console.log("Theme","Loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.install = function(theme)
|
var css = `
|
||||||
{
|
:root {
|
||||||
var html = "";
|
--background: ${theme.background};
|
||||||
|
--f_high: ${theme.f_high};
|
||||||
|
--f_med: ${theme.f_med};
|
||||||
|
--f_low: ${theme.f_low};
|
||||||
|
--f_inv: ${theme.f_inv};
|
||||||
|
--b_high: ${theme.b_high};
|
||||||
|
--b_med: ${theme.b_med};
|
||||||
|
--b_low: ${theme.b_low};
|
||||||
|
--b_inv: ${theme.b_inv};
|
||||||
|
}`;
|
||||||
|
|
||||||
this.active = theme;
|
this.active = theme;
|
||||||
|
this.el.textContent = css;
|
||||||
html += "body { background:"+theme.background+" !important }\n";
|
localStorage.setItem("theme", JSON.stringify(theme));
|
||||||
html += ".fh { color:"+theme.f_high+" !important; stroke:"+theme.f_high+" !important }\n";
|
|
||||||
html += ".fm { color:"+theme.f_med+" !important ; stroke:"+theme.f_med+" !important }\n";
|
|
||||||
html += ".fl { color:"+theme.f_low+" !important ; stroke:"+theme.f_low+" !important }\n";
|
|
||||||
html += ".f_inv { color:"+theme.f_inv+" !important ; stroke:"+theme.f_inv+" !important }\n";
|
|
||||||
html += ".f_inv { color:"+theme.f_inv+" !important ; stroke:"+theme.f_inv+" !important }\n";
|
|
||||||
html += ".bh { background:"+theme.b_high+" !important; fill:"+theme.b_high+" !important }\n";
|
|
||||||
html += ".bm { background:"+theme.b_med+" !important ; fill:"+theme.b_med+" !important }\n";
|
|
||||||
html += ".bl { background:"+theme.b_low+" !important ; fill:"+theme.b_low+" !important }\n";
|
|
||||||
html += ".b_inv { background:"+theme.b_inv+" !important ; fill:"+theme.b_inv+" !important }\n";
|
|
||||||
|
|
||||||
html += ".icon { color:"+theme.f_high+" !important; stroke:"+theme.f_high+" !important }\n";
|
|
||||||
|
|
||||||
html += "#dotgrid svg.vector { fill:"+theme.f_high+" }\n";
|
|
||||||
html += "#dotgrid #preview { stroke:"+theme.f_low+" !important} \n";
|
|
||||||
html += "#dotgrid #cursor { border-color:"+theme.f_med+"}\n";
|
|
||||||
html += "#dotgrid #cursor_from { background:"+theme.f_low+"; border-color:"+theme.f_low+"}\n";
|
|
||||||
html += "#dotgrid #cursor_to { background:"+theme.f_low+"; border-color:"+theme.f_low+"}\n";
|
|
||||||
html += "#dotgrid #cursor_end { background:"+theme.f_low+"; border-color:"+theme.f_low+"}\n";
|
|
||||||
|
|
||||||
this.el.innerHTML = html;
|
|
||||||
this.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reset = function()
|
this.reset = function()
|
||||||
{
|
{
|
||||||
console.log("Theme","reset");
|
this.load(this.default);
|
||||||
this.install(this.default);
|
}
|
||||||
|
|
||||||
|
this.drag = function(e)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var file = e.dataTransfer.files[0];
|
||||||
|
|
||||||
|
if(!file.name || !file.name.indexOf(".thm") < 0){ console.log("Theme","Not a theme"); return; }
|
||||||
|
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function(e){
|
||||||
|
dotgrid.theme.load(e.target.result);
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_json(text)
|
function is_json(text)
|
||||||
|
Loading…
Reference in New Issue
Block a user