Repository: sketchpunk/NEditorJS
Branch: master
Commit: 6de5529d3716
Files: 4
Total size: 14.9 KB
Directory structure:
gitextract_439mgt12/
├── NEditor.css
├── NEditor.js
├── README.md
└── ui.html
================================================
FILE CONTENTS
================================================
================================================
FILE: NEditor.css
================================================
*{font-family: arial; }
body{background:url(bg_grid.png);}
svg{position:absolute; z-index:-100; top:0px; left:0px; width:100%; height:100%;}
/*---------------------------------------*/
.NodeContainer{ position:absolute; background-color:rgba(63,63,63,.7); display:inline-block; border-radius:5px; box-shadow: 0px 5px 10px #000000;}
.NodeContainer > header{ display:block; background-color:#297286; color:white; cursor:pointer; border-radius:5px 5px 0px 0px;
text-align:center; padding:4px 12px;
-webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
}
.NodeContainer > ul { margin:0px; padding:0px; list-style:none; }
.NodeContainer > ul > li { position:relative; margin-top:8px; margin-bottom:8px; }
.NodeContainer > ul > li > span{padding-left:1em; padding-right:1em; color:white;}
.NodeContainer > ul > li > i{ position:absolute; width:0.6em; height:0.6em; background-color:gray; border-radius:1em; cursor:pointer; }
.NodeContainer > ul > li > i:hover{ background-color:red; }
.NodeContainer > ul > li.Input > i { left:-0.3em; top:0.4em; }
.NodeContainer > ul > li.Output { text-align:right;}
.NodeContainer > ul > li.Output > i { right:-0.25em; top:0.4em; }
.NodeContainer > ul > li.Active > i { background-color:#72a836;}
================================================
FILE: NEditor.js
================================================
//Copyright 2016 Sketchpunk Labs
//###########################################################################
//Main Static Object
//###########################################################################
var NEditor = {};
NEditor.dragMode = 0;
NEditor.dragItem = null; //reference to the dragging item
NEditor.startPos = null; //Used for starting position of dragging lines
NEditor.offsetX = 0; //OffsetX for dragging nodes
NEditor.offsetY = 0; //OffsetY for dragging nodes
NEditor.svg = null; //SVG where the line paths are drawn.
NEditor.pathColor = "#999999";
NEditor.pathColorA = "#86d530";
NEditor.pathWidth = 2;
NEditor.pathDashArray = "20,5,5,5,5,5";
NEditor.init = function(){
NEditor.svg = document.getElementById("connsvg");
NEditor.svg.ns = NEditor.svg.namespaceURI;
};
/*--------------------------------------------------------
Global Function */
//Trail up the parent nodes to get the X,Y position of an element
NEditor.getOffset = function(elm){
var pos = {x:0,y:0};
while(elm){
pos.x += elm.offsetLeft;
pos.y += elm.offsetTop;
elm = elm.offsetParent;
}
return pos;
};
//Gets the position of one of the connection points
NEditor.getConnPos = function(elm){
var pos = NEditor.getOffset(elm);
pos.x += (elm.offsetWidth / 2) + 1.5; //Add some offset so its centers on the element
pos.y += (elm.offsetHeight / 2) + 0.5;
return pos;
};
//Used to reset the svg path between two nodes
NEditor.updateConnPath = function(o){
var pos1 = o.output.getPos(),
pos2 = o.input.getPos();
NEditor.setQCurveD(o.path,pos1.x,pos1.y,pos2.x,pos2.y);
};
//Creates an Quadratic Curve path in SVG
NEditor.createQCurve = function (x1, y1, x2, y2) {
var elm = document.createElementNS(NEditor.svg.ns,"path");
elm.setAttribute("fill", "none");
elm.setAttribute("stroke", NEditor.pathColor);
elm.setAttribute("stroke-width", NEditor.pathWidth);
elm.setAttribute("stroke-dasharray", NEditor.pathDashArray);
NEditor.setQCurveD(elm,x1,y1,x2,y2);
return elm;
}
//This is seperated from the create so it can be reused as a way to update an existing path without duplicating code.
NEditor.setQCurveD = function(elm,x1,y1,x2,y2){
var dif = Math.abs(x1-x2) / 1.5,
str = "M" + x1 + "," + y1 + " C" + //MoveTo
(x1 + dif) + "," + y1 + " " + //First Control Point
(x2 - dif) + "," + y2 + " " + //Second Control Point
(x2) + "," + y2; //End Point
elm.setAttribute('d', str);
}
NEditor.setCurveColor = function(elm,isActive){ elm.setAttribute('stroke', (isActive)? NEditor.pathColorA : NEditor.pathColor); }
/*Unused function at the moment, it creates a straight line
NEditor.createline = function (x1, y1, x2, y2, color, w) {
var line = document.createElementNS(NEditor.svg.ns, 'line');
line.setAttribute('x1', x1);
line.setAttribute('y1', y1);
line.setAttribute('x2', x2);
line.setAttribute('y2', y2);
line.setAttribute('stroke', color);
line.setAttribute('stroke-width', w);
return line;
}*/
/*--------------------------------------------------------
Dragging Nodes */
NEditor.beginNodeDrag = function(n,x,y){
if(NEditor.dragMode != 0) return;
NEditor.dragMode = 1;
NEditor.dragItem = n;
this.offsetX = n.offsetLeft - x;
this.offsetY = n.offsetTop - y;
window.addEventListener("mousemove",NEditor.onNodeDragMouseMove);
window.addEventListener("mouseup",NEditor.onNodeDragMouseUp);
};
NEditor.onNodeDragMouseUp = function(e){
e.stopPropagation(); e.preventDefault();
NEditor.dragItem = null;
NEditor.dragMode = 0;
window.removeEventListener("mousemove",NEditor.onNodeDragMouseMove);
window.removeEventListener("mouseup",NEditor.onNodeDragMouseUp);
};
NEditor.onNodeDragMouseMove = function(e){
e.stopPropagation(); e.preventDefault();
if(NEditor.dragItem){
NEditor.dragItem.style.left = e.pageX + NEditor.offsetX + "px";
NEditor.dragItem.style.top = e.pageY + NEditor.offsetY + "px";
NEditor.dragItem.ref.updatePaths();
}
};
/*--------------------------------------------------------
Dragging Paths */
NEditor.beginConnDrag = function(path){
if(NEditor.dragMode != 0) return;
NEditor.dragMode = 2;
NEditor.dragItem = path;
NEditor.startPos = path.output.getPos();
NEditor.setCurveColor(path.path,false);
window.addEventListener("click",NEditor.onConnDragClick);
window.addEventListener("mousemove",NEditor.onConnDragMouseMove);
};
NEditor.endConnDrag = function(){
NEditor.dragMode = 0;
NEditor.dragItem = null;
window.removeEventListener("click",NEditor.onConnDragClick);
window.removeEventListener("mousemove",NEditor.onConnDragMouseMove);
}
NEditor.onConnDragClick = function(e){
e.stopPropagation(); e.preventDefault();
NEditor.dragItem.output.removePath(NEditor.dragItem);
NEditor.endConnDrag();
};
NEditor.onConnDragMouseMove = function(e){
e.stopPropagation(); e.preventDefault();
if(NEditor.dragItem) NEditor.setQCurveD(NEditor.dragItem.path,NEditor.startPos.x,NEditor.startPos.y,e.pageX,e.pageY);
};
/*--------------------------------------------------------
Connection Event Handling */
NEditor.onOutputClick = function(e){
e.stopPropagation(); e.preventDefault();
var path = e.target.parentNode.ref.addPath();
NEditor.beginConnDrag(path);
}
NEditor.onInputClick = function(e){
e.stopPropagation(); e.preventDefault();
var o = this.parentNode.ref;
switch(NEditor.dragMode){
case 2: //Path Drag
o.applyPath(NEditor.dragItem);
NEditor.endConnDrag();
break;
case 0: //Not in drag mode
var path = o.clearPath();
if(path != null) NEditor.beginConnDrag(path);
break;
}
}
//###########################################################################
// Connector Object
//###########################################################################
//Connector UI Object. Ideally this should be an abstract class as a base for an output and input class, but save time
//I wrote this object to handle both types. Its a bit hokey but if it becomes a problem I'll rewrite it in a better OOP way.
NEditor.Connector = function(pElm,isInput,name){
this.name = name;
this.root = document.createElement("li");
this.dot = document.createElement("i");
this.label = document.createElement("span");
//Input/Output Specific values
if(isInput) this.OutputConn = null; //Input can only handle a single connection.
else this.paths = []; //Outputs can connect to as many inputs is needed
//Create Elements
pElm.appendChild(this.root);
this.root.appendChild(this.dot);
this.root.appendChild(this.label);
//Define the Elements
this.root.className = (isInput)?"Input":"Output";
this.root.ref = this;
this.label.innerHTML = name;
this.dot.innerHTML = " ";
this.dot.addEventListener("click", (isInput)?NEditor.onInputClick:NEditor.onOutputClick );
};
/*--------------------------------------------------------
Common Methods */
//Get the position of the connection ui element
NEditor.Connector.prototype.getPos = function(){ return NEditor.getConnPos(this.dot); }
//Just updates the UI if the connection is currently active
NEditor.Connector.prototype.resetState = function(){
var isActive = (this.paths && this.paths.length > 0) || (this.OutputConn != null);
if(isActive) this.root.classList.add("Active");
else this.root.classList.remove("Active");
}
//Used mostly for dragging nodes, so this allows the paths to be redrawn
NEditor.Connector.prototype.updatePaths = function(){
if(this.paths && this.paths.length > 0) for(var i=0; i < this.paths.length; i++) NEditor.updateConnPath(this.paths[i]);
else if( this.OutputConn ) NEditor.updateConnPath(this.OutputConn);
}
/*--------------------------------------------------------
Output Methods */
//This creates a new path between nodes
NEditor.Connector.prototype.addPath = function(){
var pos = NEditor.getConnPos(this.dot),
dat = {
path: NEditor.createQCurve(pos.x,pos.y,pos.x,pos.y),
input:null,
output:this
};
NEditor.svg.appendChild(dat.path);
this.paths.push(dat);
return dat;
}
//Remove Path
NEditor.Connector.prototype.removePath = function(o){
var i = this.paths.indexOf(o);
if(i > -1){
NEditor.svg.removeChild(o.path);
this.paths.splice(i,1);
this.resetState();
}
}
NEditor.Connector.prototype.connectTo = function(o){
if(o.OutputConn === undefined){
console.log("connectTo - not an input");
return;
}
var conn = this.addPath();
o.applyPath(conn);
}
/*--------------------------------------------------------
Input Methods */
//Applying a connection from an output
NEditor.Connector.prototype.applyPath = function(o){
//If a connection exists, disconnect it.
if(this.OutputConn != null) this.OutputConn.output.removePath(this.OutputConn);
//If moving a connection to here, tell previous input to clear itself.
if(o.input != null) o.input.clearPath();
o.input = this; //Saving this connection as the input reference
this.OutputConn = o; //Saving the path reference to this object
this.resetState(); //Update the state on both sides of the connection, TODO some kind of event handling scheme would work better maybe
o.output.resetState();
NEditor.updateConnPath(o);
NEditor.setCurveColor(o.path,true);
}
//clearing the connection from an output
NEditor.Connector.prototype.clearPath = function(){
if(this.OutputConn != null){
var tmp = this.OutputConn;
tmp.input = null;
this.OutputConn = null;
this.resetState();
return tmp;
}
}
//###########################################################################
// Node Object
//###########################################################################
NEditor.Node = function(sTitle){
this.Title = sTitle;
this.Inputs = [];
this.Outputs = [];
//.........................
this.eRoot = document.createElement("div");
document.body.appendChild(this.eRoot);
this.eRoot.className = "NodeContainer";
this.eRoot.ref = this;
//.........................
this.eHeader = document.createElement("header");
this.eRoot.appendChild(this.eHeader);
this.eHeader.innerHTML = this.Title;
this.eHeader.addEventListener("mousedown",this.onHeaderDown);
//.........................
this.eList = document.createElement("ul");
this.eRoot.appendChild(this.eList);
};
NEditor.Node.prototype.addInput = function(name){
var o = new NEditor.Connector(this.eList,true,name) ;
this.Inputs.push(o);
return o;
}
NEditor.Node.prototype.addOutput = function(name){
var o = new NEditor.Connector(this.eList,false,name);
this.Outputs.push(o);
return o;
}
NEditor.Node.prototype.getInputPos = function(i){ return NEditor.getConnPos(this.Inputs[i].dot); }
NEditor.Node.prototype.getOutputPos = function(i){ return NEditor.getConnPos(this.Outputs[i].dot); }
NEditor.Node.prototype.updatePaths = function(){
var i;
for(i=0; i < this.Inputs.length; i++) this.Inputs[i].updatePaths();
for(i=0; i < this.Outputs.length; i++) this.Outputs[i].updatePaths();
}
//Handle the start node dragging functionality
NEditor.Node.prototype.onHeaderDown = function(e){
e.stopPropagation();
NEditor.beginNodeDrag(e.target.parentNode,e.pageX,e.pageY);
};
NEditor.Node.prototype.setPosition = function(x,y){
this.eRoot.style.left = x + "px";
this.eRoot.style.top = y + "px";
};
NEditor.Node.prototype.setWidth = function(w){ this.eRoot.style.width = w+"px"; }
//###########################################################################
// SETUP
//###########################################################################
window.addEventListener("load",function(e){
NEditor.init();
});
================================================
FILE: README.md
================================================
# NEditorJS
**Youtube Demo** :
https://www.youtube.com/watch?v=HpyD2hrySd0
**Live Demo** :
http://sketchpunk.com/NEditorJS/ui.html
**Purpose**:
These are one of those projects you do just for the experience and fun of it, I really have no need for it but who knows, maybe I'll
make my own automation software and this will make for a good UI for creating the process flow. I'll probably add on to this from time to
time.
Note, if you have a need for this sort of interface for some application but you need it to do more, give me a ring maybe we can work
together. I'm always open to working on interesting and worthwhile projects.
### Links
* [SketchPunk Labs Blog](http://sketchpunklabs.tumblr.com/)
* [Support SketchPunk Labs @ Patreon](https://www.patreon.com/sketchpunk)
### License
This application is released as in open source application, following the Creative Common - Attribution-NonCommercial Licence.
https://creativecommons.org/licenses/by-nc/3.0/us/
================================================
FILE: ui.html
================================================
<html>
<head>
<link rel="stylesheet" type="text/css" href="NEditor.css">
<script src="NEditor.js"></script>
<script>
window.addEventListener("load",function(e){
var n4 = new NEditor.Node("Compile Results");
var n4i1 = n4.addInput("Input A");
var n4i2 = n4.addInput("Input B");
n4.setPosition(700,180);
var n3 = new NEditor.Node("Some Process");
var n3i1 = n3.addInput("Input");
var n3o1 = n3.addOutput("Output");
n3.setPosition(300,50);
n3.setWidth(200);
n3o1.connectTo(n4i2);
var n2 = new NEditor.Node("Some Other Process");
var n2i1 = n2.addInput("Input");
var n2o1 = n2.addOutput("Output");
n2.setPosition(300,300);
n2.setWidth(200);
n2o1.connectTo(n4i1);
var n1 = new NEditor.Node("Starting Node");
var n1o1 = n1.addOutput("Output");
n1.setPosition(50,200);
n1o1.connectTo(n2i1);
n1o1.connectTo(n3i1);
var n5 = new NEditor.Node("SketchPunk Labs");
n5.addInput("Donate :)");
n5.setPosition(700,500);
var n6 = new NEditor.Node("The User");
n6.addOutput("1 dollar");
n6.setPosition(500,500);
});
</script>
</head>
<body>
<svg id="connsvg"></svg>
<p style="color:#a0a0a0; bottom:1px; position:absolute">
<i style="color:lime;">Instructions</i><br>
- Click on an output connecting dots to start a new connection.<br>
- When new connection is made, click on an input connecting dot to complete connection.<br>
- Click and hold node title bars to drag them around.
<br><br>© 2016 Sketchpunk Labs;
</p>
</body>
</html>
gitextract_439mgt12/ ├── NEditor.css ├── NEditor.js ├── README.md └── ui.html
Condensed preview — 4 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
{
"path": "NEditor.css",
"chars": 1313,
"preview": "*{font-family: arial; }\n\nbody{background:url(bg_grid.png);}\nsvg{position:absolute; z-index:-100; top:0px; left:0px; widt"
},
{
"path": "NEditor.js",
"chars": 11456,
"preview": "//Copyright 2016 Sketchpunk Labs\n\n//###########################################################################\n//Main S"
},
{
"path": "README.md",
"chars": 974,
"preview": "# NEditorJS\n\n**Youtube Demo** : \nhttps://www.youtube.com/watch?v=HpyD2hrySd0\n\n**Live Demo** :\nhttp://sketchpunk.com/NEdi"
},
{
"path": "ui.html",
"chars": 1565,
"preview": "<html>\n\t<head>\n\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"NEditor.css\">\n\t\t<script src=\"NEditor.js\"></script>\n\t\t<scri"
}
]
About this extraction
This page contains the full source code of the sketchpunk/NEditorJS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 4 files (14.9 KB), approximately 3.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.