iterate-node
Version:
iterateNode node package
743 lines (689 loc) • 25.3 kB
JavaScript
(function(){
/*
JSON Value
It includes ?
number (integer or floating point)
string
boolean
array
object
null
*/
var iterateNodeDataTypes = {
"[object Number]": {
label: "number",
converter: function () {
return Number();
}
},
"[object String]": {
label: "string",
converter: function () {
return String();
}
},
"[object Boolean]": {
label: "boolean",
converter: function () {
return Boolean();
}
},
"[object Array]": {
label: "list of values",
converter: function (obj) {
return [];
}
},
"[object Object]": {
label: "list of elements",
converter: function (obj) {
return {};
}
},
"[object Null]": {
label: "null",
converter: function (obj) {
return null;
}
}
};
//render in select
function returnDataTypes(elem){
var select = document.createElement("select");
select.classList.add("iterate-node__hide");
for(var k in iterateNodeDataTypes){
var option = document.createElement("option");
option.value = k;
select.options.add(new Option(iterateNodeDataTypes[k].label, k));
}
return select;
}
var defaults = {
separator : ".",
contentEditable : {
add : false,
delete : false,
key : false,
value: false,
selection : false,
drag : false,
type : false
},
appendElements : {},
map : false,
dataKeyOnDOM : '__iterate-node-data__'
};
var methods = {
addElement : addElement,
changeKeyOrValue : changeKeyOrValue,
changeType : changeType,
deleteElement : deleteElement,
openObject : openObject,
iteration : ITERATION,
template : TEMPLATE
}
function dragging(HandlerEl) {
var $super = this;
var dragging = false;
var target, cloneNodes = [],targetLi,targetType;
var dummy = document.querySelector(".iterate_node__dummy");
if(!dummy) {
dummy = document.createElement("div");
dummy.classList.add("iterate_node__dummy");
document.body.appendChild(dummy);
}
var highlight = document.querySelector(".iterate-node__drag-position");
if(!highlight) {
highlight = document.createElement("div");
highlight.classList.add("iterate-node__drag-position");
document.body.appendChild(highlight);
}
function Down(e){
if(dragging
|| !e.target.classList.contains("iterate-node__drag")
|| !isDescendant(HandlerEl,e.target))
return;
dragging = true;
var li = e.target.parentElement;
var clone = li.cloneNode(true);
if(clone.querySelector("ul") ) clone.removeChild(clone.querySelector("ul"));
cloneNodes.push({
original : li,
clone : clone
});
for(var k in cloneNodes)
dummy.appendChild(cloneNodes[k].clone);
dummy.style.transform = "translate(" + e.x + "px," + e.y + "px)";
}
function Move(e){
if(!dragging
|| !isDescendant(HandlerEl,e.target))
return;
target = e.target;
var targetNodeName = target.nodeName.toLowerCase();
var parentNodeName = target.parentElement.nodeName.toLowerCase();
targetLi = targetNodeName == "li" ?
target :
( parentNodeName == "li" ?
target.parentElement :
false);
if(!targetLi)
return;
if(document.querySelector("ul.iterate-node__enlight-ul"))
document.querySelector("ul.iterate-node__enlight-ul").classList.remove("iterate-node__enlight-ul");
var height = 0;
if( targetNodeName == "ul"){
var caret = targetLi.querySelector('.iterate-node__caret');
height = targetLi.offsetHeight;
target.classList.add("iterate-node__enlight-ul");
}
dummy.style.transform = "translate(" + (e.x + 1) + "px," + (e.y +1) + "px)";
var position = offset(targetLi);
highlight.style.width = ( targetLi.offsetWidth + position.left ) + "px";
highlight.style.transform = "translate(0," + ( position.top + height ) + "px)";
}
function Up(e){
if(!dragging )
return;
if(isDescendant(HandlerEl,e.target) && targetLi)
for(var k in cloneNodes) {
var original = cloneNodes[k].original;
if(original == targetLi || isDescendant(original,targetLi))
continue;
var key = original[defaults.dataKeyOnDOM].key;
var value = original[defaults.dataKeyOnDOM].value;
deleteElement(original);
var liToPrepend = target.nodeName.toLowerCase() == "ul" ?
false :
targetLi;
var li = typeof targetLi[defaults.dataKeyOnDOM].key === "undefined" ||
target.nodeName.toLowerCase() == "ul" ?
targetLi :
targetLi.parentElement.parentElement;
addElement(li,liToPrepend,value,key,original);
}
if(document.querySelector("ul.iterate-node__enlight-ul"))
document.querySelector("ul.iterate-node__enlight-ul").classList.remove("iterate-node__enlight-ul");
dragging = false;
dummy.innerHTML = "";
cloneNodes = [];
targetLi = null;
dummy.style.transform = "translate(-9999px,-9999px)";
highlight.style.transform = "translate(-9999px,-9999px)";
}
// TO DO : MOVE IN A SEPARATE FILE ( CORE FUNCTIONALITY OF MANY ELEMENTS )
// desktop case
var desktop = function(){
function mousedown(e){
var ev = {
x : e.pageX,
y : e.pageY,
target : e.target
};
Down(ev);
document.addEventListener("mouseup",mouseup);
document.addEventListener('mousemove',mousemove);
}
HandlerEl.addEventListener("mousedown",mousedown);
function mouseup(e){
var ev = {
x : e.pageX,
y : e.pageY,
target : e.target
};
Up(ev);
document.removeEventListener("mouseup",mouseup);
document.removeEventListener('mousemove',mousemove);
}
function mousemove(e){
var ev = {
x : e.pageX,
y : e.pageY,
target : e.target
};
Move(ev);
}
$super.destroy = function(){
HandlerEl.removeEventListener("mousedown",mousedown);
}
};
// Mobile case
var mobile = function() {
function touchstart(e){
var ev = {
x : e.changedTouches[0].pageX,
y : e.changedTouches[0].pageY,
target : e.target
};
Down(ev);
document.addEventListener('touchend', touchend,false);
document.addEventListener('touchmove', touchmove,false);
}
// listeners
HandlerEl.addEventListener('touchstart', touchstart,{passive:true});
function touchmove(e){
var ev = {
x : e.changedTouches[0].pageX,
y : e.changedTouches[0].pageY,
target : document.elementFromPoint(e.changedTouches[0].pageX, e.changedTouches[0].pageY)
};
Move(ev);
}
function touchend(e){
var ev = {
x : e.changedTouches[0].pageX,
y : e.changedTouches[0].pageY,
target : e.target
};
Up(ev);
document.removeEventListener('touchend', touchend);
document.removeEventListener('touchmove',touchmove,{passive:false});
}
$super.destroy = function(){
HandlerEl.removeEventListener('touchstart', touchstart);
}
};
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) )
mobile();
else
desktop();
}
function elementRoot(obj,cb){
/* main LI */
var ul = document.createElement("ul");
var li = document.createElement("li");
if(defaults.map)
defaults.map({value:obj},eR);
else
eR(obj);
function eR(renderObj) {
li[defaults.dataKeyOnDOM] = {
value: renderObj,
alias: obj
};
if (defaults.alias)
spanAlias(li, defaults.alias(obj));
if (defaults.contentEditable.add) {
spanAddItem(li);
}
ul.appendChild(li);
cb(ul);
}
}
function spanAddItem(li,obj) {
var add = document.createElement("span");
add.classList.add("iterate-node__add-item");
add.textContent = "+";
add.addEventListener("click", function(e){
addElement(e.target.parentElement,null,null,undefined,null)
});
li.appendChild(add);
}
function spanAlias(li,text) {
var alias = document.createElement("span");
alias.classList.add("iterate-node__alias");
alias.innerHTML = text;
li.appendChild(alias);
}
function spanCaret(li) {
var $self = this;
var caret = document.createElement("span");
caret.classList.add("iterate-node__caret");
caret.addEventListener('click', openObject);
li.appendChild(caret);
}
function spanElementDelete(li) {
var deleteElem = document.createElement("span");
deleteElem.className = "iterate-node__delete-item";
deleteElem.innerText = "-";
deleteElem.addEventListener("click",function(e) {
deleteElement(e.target.parentElement);
});
li.appendChild(deleteElem);
}
function spanForDrag(li) {
/* add span for Drag */
var spanForDrag = document.createElement("span");
spanForDrag.classList.add("iterate-node__drag");
spanForDrag.innerText = "D";
li.appendChild(spanForDrag);
}
function spanForKey(li,key,parentElement,typeNode) {
/* span for key */
var keyElem = document.createElement("span");
keyElem.classList.add("iterate-node__key");
keyElem.innerText = key;
// if editable, adding contenteditable and correlate event
if (defaults.contentEditable.key && !Array.isArray(parentElement)) {
keyElem.setAttribute("contenteditable","true");
keyElem.addEventListener("blur", changeKeyOrValue.bind(null,true));
}
li.appendChild(keyElem);
}
function spanForTypeof(li,typeNode,key) {
/* span for typeof */
var typeOfElem = document.createElement("span");
typeOfElem.classList.add("iterate-node__type-node");
typeOfElem.innerText = typeNode;
li.appendChild(typeOfElem);
// if editable, adding event on click and select for choose type
if (defaults.contentEditable.type) {
typeOfElem.addEventListener("click", changeTypeEnable);
var sel = returnDataTypes();
li.appendChild(sel);
sel.addEventListener("blur", changeType.bind(null))
}
}
function spanSeparator(li) {
/* add span separator */
var spanSeparator = document.createElement("span");
spanSeparator.innerText = ":";
spanSeparator.classList.add("iterate-node__separator");
li.appendChild(spanSeparator);
}
function spanValue(li,value) {
var valueElem = document.createElement("span");
valueElem.classList.add("iterate-node__value");
valueElem.textContent = "" + value;
if (defaults.contentEditable.value) {
valueElem.setAttribute("contenteditable","true");
valueElem.addEventListener("blur",
changeKeyOrValue.bind(null,false))
}
li.appendChild(valueElem);
}
function isDescendant(parent, child) {
var node = child.parentNode;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
function iterateNode(targetElement,settings) {
var $self = this;
defaults = $self.defaults =
settings ? merge(settings,defaults,true) : defaults;
$self.state = defaults.obj;
$self.methods = methods;
ITERATION({value:defaults.obj},function(DOMrepresentation){
elementRoot(defaults.obj,function(rootRepresentation){
rootRepresentation.children[0].appendChild(DOMrepresentation);
targetElement.className += " iterateNode-obj";
targetElement.appendChild(rootRepresentation);
if( defaults.contentEditable.drag )
dragging.call($self,targetElement);
})
});
return $self;
};
/**
* Created by Simone on 01/09/15.
*/
function merge(add,base,extension) {
var target = Array.isArray(base) ? [] : {};
for (var i in base) {
var typeNode = Object.prototype.toString.call(base[k]);
if ( ( typeNode == "[object Object]" || typeNode == "[object Array]" ) &&
base[i] )
target[i] = add[i] ? merge(add[i],base[i],extension) : base[i];
else
target[i] = add[i] || base[i];
}
if ( extension ){
for (var k in add){
target[k] = target[k] || add[k];
}
}
return target;
}
function addElement(li,liToPrepend,value,key,liToClone,alias){
var elm = li[defaults.dataKeyOnDOM].value;
// if no container, create
var container = li.querySelector("ul");
// open the caret
var caret = li.querySelector('.iterate-node__caret');
if( caret ) {
if(!container) {
caret.click();
container = li.querySelector("ul");
}
if (!caret.classList.contains('open'))
caret.classList.add('open');
}
var index,newValue,newKey;
if(Array.isArray(elm)) {
if(typeof key === "undefined") {
value = "value";
key = elm.length;
}
if(liToPrepend) {
index = Array.prototype.indexOf.call(
liToPrepend.parentElement.children, liToPrepend);
var parentElementLength = liToPrepend.parentElement.children.length;
for (var ii = index; ii < parentElementLength; ii++) {
var childLI = liToPrepend.parentElement.children[ii];
childLI[defaults.dataKeyOnDOM].key = ii+1;
childLI.querySelector(".iterate-node__key").innerText = ii+1;
}
elm.splice(index, 0, value);
if(liToClone) {
liToClone.querySelector(".iterate-node__key").innerText = index;
liToClone[defaults.dataKeyOnDOM].key = index;
}
key = index;
}
else {
if(liToClone) {
liToClone.querySelector(".iterate-node__key").innerText = elm.length;
liToClone[defaults.dataKeyOnDOM].key = elm.length;
}
elm[elm.length] = value;
}
}
else {
if(typeof key === "undefined") {
// every newItem key must have "newItem" + maxNumber as key
var number = 0;
for (var k in elm) {
if (k.indexOf("newItem") === 0) {
var itemNumber = Number(k.replace("newItem", ""));
number = !isNaN(itemNumber) && number < itemNumber ? itemNumber : number;
}
}
value = "value";
key = "newItem" + ( number + 1 );
}
else {
//coerce key to string
key = typeof key === "number" ? "0" + key : key;
while (Object.keys(elm).indexOf(key) > -1)
key = key + Math.floor(Math.random() * (9007199254740991));
if(liToClone) {
liToClone.querySelector(".iterate-node__key").innerText = key;
liToClone[defaults.dataKeyOnDOM].key = key;
}
}
// insertment
if(liToPrepend) {
prependObjectElement(elm, key,
value, liToPrepend[defaults.dataKeyOnDOM].key);
var children = container.children;
for(var ch = 0;ch<children.length;ch++) {
var chi = children[ch][defaults.dataKeyOnDOM];
chi.value = elm[chi.key];
chi.parentElement = elm;
}
}
else
elm[key] = value;
}
if(liToClone)
liToClone[defaults.dataKeyOnDOM].parentElement = elm;
var newElementDOM = liToClone ? liToClone :
TEMPLATE(key, value, elm, alias || null);
if(liToPrepend) {
liToPrepend.parentElement.insertBefore(
newElementDOM,
liToPrepend
);
}
else {
container.appendChild(newElementDOM)
}
defaults.addElementListener &&
defaults.addElementListener(newElementDOM[defaults.dataKeyOnDOM],
li[defaults.dataKeyOnDOM],
liToPrepend ? liToPrepend[defaults.dataKeyOnDOM] : false);
}
function changeKeyOrValue(isKey,e) {
var thisTarget = e.target;
var li = thisTarget.parentElement;
var oldKey = li[defaults.dataKeyOnDOM].key;
var oldValue = li[defaults.dataKeyOnDOM].value;
var typeNode = Object.prototype.toString.call(oldValue);
var parentElement = li[defaults.dataKeyOnDOM].parentElement;
var innerText = thisTarget.innerText;
if(isKey) {
if(innerText == oldKey)
return;
while (Object.keys(parentElement).indexOf(innerText) > -1)
innerText = innerText + Math.floor(Math.random() * (9007199254740991));
var nextElement = li.nextElementSibling;
li[defaults.dataKeyOnDOM].key = innerText;
thisTarget.innerText = innerText;
if(nextElement) {
prependObjectElement(parentElement, innerText,
oldValue, nextElement[defaults.dataKeyOnDOM].key);
delete parentElement[oldKey];
}
else {
delete parentElement[oldKey];
parentElement[innerText] = li[defaults.dataKeyOnDOM].value;
}
defaults.changeKeyListener && defaults.changeKeyListener(li[defaults.dataKeyOnDOM]);
}
else{
if(innerText == oldValue)
return;
// number sanitation
switch(typeNode) {
case ("[object Number]") :
// Number : if is NaN, is 0
innerText = isNaN(Number(innerText)) ?
0 : Number(innerText);
break;
case ("[object Boolean]") :
// boolean : if is not 'true', then is false
innerText = ( innerText.toLowerCase() == "true" );
break;
case ("[object Null]") :
thisTarget.innerText = "" + null;
return;
break;
default:
break;
}
parentElement[oldKey] = innerText;
li[defaults.dataKeyOnDOM].value = innerText;
thisTarget.innerText = innerText;
defaults.changeValueListener && defaults.changeValueListener(li[defaults.dataKeyOnDOM]);
}
}
function changeTypeEnable(e){
e.target.classList.add('iterate-node__hide');
e.target.nextElementSibling.classList.remove('iterate-node__hide');
}
function changeType(e){
var li = e.target.parentElement;
var key = li[defaults.dataKeyOnDOM].key;
var parent = li[defaults.dataKeyOnDOM].parentElement;
var newValue = iterateNodeDataTypes[e.target.value].converter();
var newLi = TEMPLATE(key,newValue,parent);
li.parentElement.replaceChild(
newLi,
li
);
//li[defaults.dataKeyOnDOM].value = newValue;
parent[key] = newValue;
defaults.changeTypeListener && defaults.changeTypeListener(newLi[defaults.dataKeyOnDOM]);
}
function deleteElement(li){
var parentElm = li.parentElement.parentElement[defaults.dataKeyOnDOM].value;
var keyElm = li[defaults.dataKeyOnDOM].key;
if(Array.isArray(parentElm)) {
var parentElement = li.parentElement;
var index = Array.prototype.indexOf.call(parentElement.children, li);
var parentElementLength = parentElement.children.length;
for (var ii = index+1; ii < parentElementLength; ii++) {
var childLI = parentElement.children[ii];
childLI[defaults.dataKeyOnDOM].key = ii-1;
childLI.querySelector(".iterate-node__key").innerText = ii-1;
}
parentElm.splice(index,1);
}
else
delete parentElm[keyElm];
defaults.removeElementListener && defaults.removeElementListener(li[defaults.dataKeyOnDOM]);
li.parentElement.removeChild(li);
}
function ITERATION (obj,callback) {
var DOMrepresentation = document.createElement("ul");
if(defaults.map) {
defaults.map(obj,function(newObj){
for (var k in newObj) {
DOMrepresentation.appendChild(
TEMPLATE(newObj.key || k,
newObj[k].value, newObj,newObj[k].alias));
}
});
callback(DOMrepresentation)
}
else {
for (var k in obj.value) {
DOMrepresentation.appendChild(TEMPLATE(k, obj.value[k], obj.value));
}
callback(DOMrepresentation);
}
}
function openObject(e){
var target = e.target;
var li = target.parentElement;
var ul = li.querySelector("ul");
if (!ul){
var newObj = li[defaults.dataKeyOnDOM];
ITERATION(newObj,function(newUl){
li.appendChild(newUl);
});
}
target.classList.toggle('open');
}
function prependObjectElement(elm,key,value,keyToPrepend) {
var temp = JSON.parse(JSON.stringify(elm));
for (var OldesKey in elm)
delete elm[OldesKey];
for (var copyKey in temp) {
if (copyKey == keyToPrepend) {
elm[key] = value;
elm[copyKey] = temp[copyKey];
}
else
elm[copyKey] = temp[copyKey];
}
}
function TEMPLATE (key,value,parentElement,alias){
var typeNode = Object.prototype.toString.call(value);
/* main LI */
var li = document.createElement("li");
li[defaults.dataKeyOnDOM] = {
key : key,
value : value,
parentElement : parentElement,
alias : alias
};
if( defaults.contentEditable.drag )
spanForDrag(li);
spanForKey(li,key,parentElement,typeNode);
spanForTypeof(li,typeNode,key);
spanSeparator(li);
if (defaults.alias)
spanAlias(li,defaults.alias(alias));
/*if (typeNode == "[object Object]" ||
typeNode == "[object Array]" ) {*/
if(value && typeof value === 'object'){
spanCaret(li);
if (defaults.contentEditable.add)
spanAddItem(li);
}
else
if(!defaults.alias) spanValue(li,value,typeNode);
/* adding delete element */
if (defaults.contentEditable.delete)
spanElementDelete(li);
if (Object.keys(defaults.appendElements).length)
for(var k in defaults.appendElements)
defaults.appendElements[k](key,value);
return li;
}
/**
* Created by Simone on 25/12/14.
*/
function offset(elem){
var box = { top: 0, left: 0 };
var docElem = document.documentElement;
var win = window;
var core_strundefined = typeof undefined;
// If we don't have gBCR, just use 0,0 rather than error
// BlackBerry 5, iOS 3 (original iPhone)
if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
box = elem.getBoundingClientRect();
}
return {
top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
};
}
window.iterateNode = iterateNode;
})();