You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
in case someone needs it: https://www.diffchecker.com/CMh3zZKM
I did some attempts and it works, I am not going to pull request yet because I am not sure I placed my code in the right code block and it needs to be tidy
/**
* Created by richard.livingston on 18/02/2017.
*/
'use strict';
var util = require('util'),
EE = require('events').EventEmitter;
module.exports = JSONTreeView;
util.inherits(JSONTreeView, EE);
function JSONTreeView(name_, value_, parent_, isRoot_){
var self = this;
if (typeof isRoot_ === 'undefined' && arguments.length < 4) {
isRoot_ = true;
}
EE.call(self);
if(arguments.length < 2){
value_ = name_;
name_ = undefined;
}
if (parent_){
//console.log(parent_.name)
if (parent_.hasOwnProperty('parentsKeys')){
parentsKeys = [...parent_.parentsKeys, parent_.name + '']
}else {
//var parentsKeys = []
}
}else{
var parentsKeys = []
}
var name, value, type, oldType = null, filterText = '', hidden = false,
//parentsKeys = [...parent_.parentsKeys],
readonly = parent_ ? parent_.readonly : false,
readonlyWhenFiltering = parent_ ? parent_.readonlyWhenFiltering : false,
alwaysShowRoot = false,
showCount = parent_ ? parent_.showCountOfObjectOrArray : true,
includingRootName = true,
domEventListeners = [], children = [], expanded = false,
edittingName = false, edittingValue = false,
nameEditable = true, valueEditable = true;
var dom = {
container : document.createElement('div'),
collapseExpand : document.createElement('div'),
name : document.createElement('div'),
separator : document.createElement('div'),
value : document.createElement('div'),
spacing: document.createElement('div'),
delete : document.createElement('div'),
children : document.createElement('div'),
insert : document.createElement('div')
};
Object.defineProperties(self, {
dom : {
value : dom.container,
enumerable : true
},
isRoot: {
get : function(){
return isRoot_;
}
},
parent: {
get: function() {
return parent_;
}
},
parentsKeys: {
get: function () {
return parentsKeys
},
set: function (parentKey) {
this.parentsKeys.push(parentKey);
}
},
children: {
get: function() {
var result = null;
if (type === 'array') {
result = children;
}
else if (type === 'object') {
result = {};
children.forEach(function(e) {
result[e.name] = e;
});
}
return result;
}
},
readonly: {
get: function() {
return !!(readonly & 1);
},
set: function(ro) {
readonly = setBit(readonly, 0, +ro);
!!(readonly & 1) ? dom.container.classList.add('readonly')
: dom.container.classList.remove('readonly');
for (var i in children) {
if (typeof children[i] === 'object') {
children[i].readonly = setBit(readonly, 0, +ro);
}
}
}
},
readonlyWhenFiltering: {
get: function() {
return readonlyWhenFiltering;
},
set: function(rowf) {
readonly = setBit(readonly, 1, +rowf);
readonlyWhenFiltering = rowf;
(readonly && this.filterText) || !!(readonly & 1)
? dom.container.classList.add('readonly')
: dom.container.classList.remove('readonly');
for (var i in children) {
if (typeof children[i] === 'object') {
children[i].readonly = setBit(readonly, 1, +rowf);
children[i].readonlyWhenFiltering = rowf;
}
}
}
},
hidden: {
get: function() {
return hidden;
},
set: function(h) {
hidden = h;
h ? dom.container.classList.add('hidden')
: dom.container.classList.remove('hidden');
if (!h) {
parent_ && (parent_.hidden = h);
}
}
},
showCountOfObjectOrArray: {
get: function() {
return showCount;
},
set: function(show) {
showCount = show;
for (var i in children) {
if (typeof children[i] === 'object') {
children[i].showCountOfObjectOrArray = show;
}
}
(this.type === 'object' || this.type === 'array') && this.updateCount();
}
},
filterText: {
get: function() {
return filterText;
},
set: function(text) {
//console.log(this.parentsKeys)
filterText = text;
if (text) {
if (readonly > 0) {
dom.container.classList.add('readonly');
}
var key = this.name + '';
var value = this.value + '';
if (this.type === 'object' || this.type === 'array') {
value = '';
}
if (key.indexOf(text) > -1 || value.indexOf(text) > -1) {
this.hidden = false;
}
else {
if (!this.alwaysShowRoot || !isRoot_) {
this.hidden = true;
}
}
//console.log(key)
if (this.parentsKeys.length > 0) {
for (var p = 0; p < this.parentsKeys.length; p++){
console.log(this.parentsKeys,p,this.parentsKeys[p])
if (this.parentsKeys[p].indexOf(text) > -1){
this.hidden = false
}
}
}
/* if (this.parentsKeys.includes(text + '')){
this.hidden = false
}*/
}
else {
!this.readonly && dom.container.classList.remove('readonly');
this.hidden = false;
}
for (var i in children) {
//children[i].parentsKeys = this.name + '';
if (typeof children[i] === 'object') {
//children[i].parentsKeys.push(this.name + '');
children[i].filterText = text;
}
}
}
},
alwaysShowRoot: {
get: function() {
return alwaysShowRoot;
},
set: function(value) {
if (isRoot_ && this.filterText) {
this.hidden = !value;
}
alwaysShowRoot = value;
for (var i in children) {
if (typeof children[i] === 'object') {
children[i].alwaysShowRoot = value;
}
}
}
},
withRootName: {
get: function() {
return includingRootName;
},
set: function(value) {
includingRootName = value;
}
},
name : {
get : function(){
return name;
},
set : setName,
enumerable : true
},
value : {
get : function(){
return value;
},
set : setValue,
enumerable : true
},
type : {
get : function(){
return type;
},
enumerable : true
},
oldType: {
get: function () {
return oldType;
},
enumerable: true
},
nameEditable : {
get : function(){
return nameEditable;
},
set : function(value){
nameEditable = !!value;
},
enumerable : true
},
valueEditable : {
get : function(){
return valueEditable;
},
set : function(value){
valueEditable = !!value;
},
enumerable : true
},
refresh : {
value : refresh,
enumerable : true
},
updateCount: {
value: updateObjectChildCount,
enumerable: true
},
collapse : {
value : collapse,
enumerable : true
},
expand : {
value : expand,
enumerable : true
},
destroy : {
value : destroy,
enumerable : true
},
editName : {
value : editField.bind(null, 'name'),
enumerable : true
},
editValue : {
value : editField.bind(null, 'value'),
enumerable : true
}
});
Object.keys(dom).forEach(function(k){
if (k === 'delete' && self.isRoot) {
return;
}
var element = dom[k];
if(k == 'container'){
return;
}
element.className = k;
if (['name', 'separator', 'value', 'spacing'].indexOf(k) > -1) {
element.className += ' item';
}
dom.container.appendChild(element);
});
dom.container.className = 'jsonView';
addDomEventListener(dom.collapseExpand, 'click', onCollapseExpandClick);
addDomEventListener(dom.value, 'click', expand.bind(null, false));
addDomEventListener(dom.name, 'click', expand.bind(null, false));
addDomEventListener(dom.name, 'dblclick', editField.bind(null, 'name'));
addDomEventListener(dom.name, 'click', itemClicked.bind(null, 'name'));
addDomEventListener(dom.name, 'blur', editFieldStop.bind(null, 'name'));
addDomEventListener(dom.name, 'keypress',
editFieldKeyPressed.bind(null, 'name'));
addDomEventListener(dom.name, 'keydown',
editFieldTabPressed.bind(null, 'name'));
addDomEventListener(dom.value, 'dblclick', editField.bind(null, 'value'));
addDomEventListener(dom.value, 'click', itemClicked.bind(null, 'value'));
addDomEventListener(dom.value, 'blur', editFieldStop.bind(null, 'value'));
addDomEventListener(dom.value, 'keypress',
editFieldKeyPressed.bind(null, 'value'));
addDomEventListener(dom.value, 'keydown',
editFieldTabPressed.bind(null, 'value'));
addDomEventListener(dom.value, 'keydown', numericValueKeyDown);
addDomEventListener(dom.insert, 'click', onInsertClick);
addDomEventListener(dom.delete, 'click', onDeleteClick);
setName(name_);
setValue(value_);
function setBit(n, i, b) {
var j = 0;
while ((n >> j << j)) {
j++;
}
return i >= j
? (n | +b << i )
: (n >> (i + 1) << (i + 1)) | (n % (n >> i << i)) | (+b << i);
}
function squarebracketify(exp) {
return typeof exp === 'string'
? exp.replace(/\.([0-9]+)/g, '[$1]') : exp + '';
}
function refresh(silent){
var expandable = type == 'object' || type == 'array';
children.forEach(function(child){
child.refresh(true);
});
dom.collapseExpand.style.display = expandable ? '' : 'none';
if(expanded && expandable){
expand(false, silent);
}
else{
collapse(false, silent);
}
if (!silent) {
self.emit('refresh', self, [self.name], self.value);
}
}
function collapse(recursive, silent){
if(recursive){
children.forEach(function(child){
child.collapse(true, true);
});
}
expanded = false;
dom.children.style.display = 'none';
dom.collapseExpand.className = 'expand';
dom.container.classList.add('collapsed');
dom.container.classList.remove('expanded');
if (!silent && (type == 'object' || type == 'array')) {
self.emit('collapse', self, [self.name], self.value);
}
}
function expand(recursive, silent){
var keys;
if(type == 'object'){
keys = Object.keys(value);
}
else if(type == 'array'){
keys = value.map(function(v, k){
return k;
});
}
else{
keys = [];
}
// Remove children that no longer exist
for(var i = children.length - 1; i >= 0; i --){
var child = children[i];
if (!child) {
break;
}
if(keys.indexOf(child.name) == -1){
children.splice(i, 1);
removeChild(child);
}
}
if(type != 'object' && type != 'array'){
return collapse();
}
keys.forEach(function(key){
addChild(key, value[key]);
});
if(recursive){
children.forEach(function(child){
child.expand(true, true);
});
}
expanded = true;
dom.children.style.display = '';
dom.collapseExpand.className = 'collapse';
dom.container.classList.add('expanded');
dom.container.classList.remove('collapsed');
if (!silent && (type == 'object' || type == 'array')) {
self.emit('expand', self, [self.name], self.value);
}
}
function destroy(){
var child, event;
while(event = domEventListeners.pop()){
event.element.removeEventListener(event.name, event.fn);
}
while(child = children.pop()){
removeChild(child);
}
}
function setName(newName){
var nameType = typeof newName,
oldName = name;
if(newName === name){
return;
}
if(nameType != 'string' && nameType != 'number'){
throw new Error('Name must be either string or number, ' + newName);
}
dom.name.innerText = newName;
name = newName;
self.emit('rename', self, [name], oldName, newName, true);
}
function setValue(newValue){
var oldValue = value,
str, len;
if (isRoot_ && !oldValue) {
oldValue = newValue;
}
type = getType(newValue);
oldType = oldValue ? getType(oldValue) : type;
switch(type){
case 'null':
str = 'null';
break;
case 'undefined':
str = 'undefined';
break;
case 'object':
len = Object.keys(newValue).length;
str = showCount ? 'Object[' + len + ']' : (len < 1 ? '{}' : '');
break;
case 'array':
len = newValue.length;
str = showCount ? 'Array[' + len + ']' : (len < 1 ? '[]' : '');
break;
default:
str = newValue;
break;
}
dom.value.innerText = str;
dom.value.className = 'value item ' + type;
if(newValue === value){
return;
}
value = newValue;
if(type == 'array' || type == 'object'){
// Cannot edit objects as string because the formatting is too messy
// Would have to either pass as JSON and force user to wrap properties in quotes
// Or first JSON stringify the input before passing, this could allow users to reference globals
// Instead the user can modify individual properties, or just delete the object and start again
valueEditable = false;
if(type == 'array'){
// Obviously cannot modify array keys
nameEditable = false;
}
}
self.emit('change', self, [name], oldValue, newValue);
refresh();
}
function updateObjectChildCount() {
var str = '', len;
if (type === 'object') {
len = Object.keys(value).length;
str = showCount ? 'Object[' + len + ']' : (len < 1 ? '{}' : '');
}
if (type === 'array') {
len = value.length;
str = showCount ? 'Array[' + len + ']' : (len < 1 ? '[]' : '');
}
dom.value.innerText = str;
}
function addChild(key, val){
var child;
for(var i = 0, len = children.length; i < len; i ++){
if(children[i].name == key){
child = children[i];
break;
}
}
if(child){
child.value = val;
}
else{
child = new JSONTreeView(key, val, self, false);
child.on('rename', onChildRename);
child.on('delete', onChildDelete);
child.on('change', onChildChange);
child.on('append', onChildAppend);
child.on('click', onChildClick);
child.on('expand', onChildExpand);
child.on('collapse', onChildCollapse);
child.on('refresh', onChildRefresh);
children.push(child);
child.emit('append', child, [key], 'value', val, true);
}
dom.children.appendChild(child.dom);
return child;
}
function removeChild(child){
if(child.dom.parentNode){
dom.children.removeChild(child.dom);
}
child.destroy();
child.emit('delete', child, [child.name], child.value,
child.parent.isRoot ? child.parent.oldType : child.parent.type, true);
child.removeAllListeners();
}
function editField(field){
if((readonly > 0 && filterText) || !!(readonly & 1)) {
return;
}
if(field === 'value' && (type === 'object' || type === 'array')){
return;
}
if(parent_ && parent_.type == 'array'){
// Obviously cannot modify array keys
nameEditable = false;
}
var editable = field == 'name' ? nameEditable : valueEditable,
element = dom[field];
if(!editable && (parent_ && parent_.type === 'array')){
if (!parent_.inserting) {
// throw new Error('Cannot edit an array index.');
return;
}
}
if(field == 'value' && type == 'string'){
element.innerText = '"' + value + '"';
}
if(field == 'name'){
edittingName = true;
}
if(field == 'value'){
edittingValue = true;
}
element.classList.add('edit');
element.setAttribute('contenteditable', true);
element.focus();
document.execCommand('selectAll', false, null);
}
function itemClicked(field) {
self.emit('click', self,
!self.withRootName && self.isRoot ? [''] : [self.name], self.value);
}
function editFieldStop(field){
var element = dom[field];
if(field == 'name'){
if(!edittingName){
return;
}
edittingName = false;
}
if(field == 'value'){
if(!edittingValue){
return;
}
edittingValue = false;
}
if(field == 'name'){
var p = self.parent;
var edittingNameText = element.innerText;
if (p && p.type === 'object' && edittingNameText in p.value) {
element.innerText = name;
element.classList.remove('edit');
element.removeAttribute('contenteditable');
// throw new Error('Name exist, ' + edittingNameText);
}
else {
setName.call(self, edittingNameText);
}
}
else{
var text = element.innerText;
try{
setValue(text === 'undefined' ? undefined : JSON.parse(text));
}
catch(err){
setValue(text);
}
}
element.classList.remove('edit');
element.removeAttribute('contenteditable');
}
function editFieldKeyPressed(field, e){
switch(e.key){
case 'Escape':
case 'Enter':
editFieldStop(field);
break;
}
}
function editFieldTabPressed(field, e){
if(e.key == 'Tab'){
editFieldStop(field);
if(field == 'name'){
e.preventDefault();
editField('value');
}
else{
editFieldStop(field);
}
}
}
function numericValueKeyDown(e){
var increment = 0, currentValue;
if(type != 'number'){
return;
}
switch(e.key){
case 'ArrowDown':
case 'Down':
increment = -1;
break;
case 'ArrowUp':
case 'Up':
increment = 1;
break;
}
if(e.shiftKey){
increment *= 10;
}
if(e.ctrlKey || e.metaKey){
increment /= 10;
}
if(increment){
currentValue = parseFloat(dom.value.innerText);
if(!isNaN(currentValue)){
setValue(Number((currentValue + increment).toFixed(10)));
}
}
}
function getType(value){
var type = typeof value;
if(type == 'object'){
if(value === null){
return 'null';
}
if(Array.isArray(value)){
return 'array';
}
}
if (type === 'undefined') {
return 'undefined';
}
return type;
}
function onCollapseExpandClick(){
if(expanded){
collapse();
}
else{
expand();
}
}
function onInsertClick(){
var newName = type == 'array' ? value.length : undefined,
child = addChild(newName, null);
if (child.parent) {
child.parent.inserting = true;
}
if(type == 'array'){
value.push(null);
child.editValue();
child.emit('append', self, [value.length - 1], 'value', null, true);
if (child.parent) {
child.parent.inserting = false;
}
}
else{
child.editName();
}
}
function onDeleteClick(){
self.emit('delete', self, [self.name], self.value,
self.parent.isRoot ? self.parent.oldType : self.parent.type, false);
}
function onChildRename(child, keyPath, oldName, newName, original){
var allow = newName && type != 'array' && !(newName in value) && original;
if(allow){
value[newName] = child.value;
delete value[oldName];
if (self.inserting) {
child.emit('append', child, [newName], 'name', newName, true);
self.inserting = false;
return;
}
}
else if(oldName === undefined){
// A new node inserted via the UI
original && removeChild(child);
}
else if (original){
// Cannot rename array keys, or duplicate object key names
child.name = oldName;
return;
}
// value[keyPath] = newName;
// child.once('rename', onChildRename);
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
else if (self.withRootName && self.isRoot) {
keyPath.unshift(name);
}
if (oldName !== undefined) {
self.emit('rename', child, keyPath, oldName, newName, false);
}
}
function onChildAppend(child, keyPath, nameOrValue, newValue, sender){
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
self.emit('append', child, keyPath, nameOrValue, newValue, false);
sender && updateObjectChildCount();
}
function onChildChange(child, keyPath, oldValue, newValue, recursed){
if(!recursed){
value[keyPath] = newValue;
}
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
self.emit('change', child, keyPath, oldValue, newValue, true);
}
function onChildDelete(child, keyPath, deletedValue, parentType, passive){
var key = child.name;
if (passive) {
if (self.withRootName/* || !self.isRoot*/) {
keyPath.unshift(name);
}
self.emit('delete', child, keyPath, deletedValue, parentType, passive);
updateObjectChildCount();
}
else {
if (type == 'array') {
value.splice(key, 1);
}
else {
delete value[key];
}
refresh(true);
}
}
function onChildClick(child, keyPath, value) {
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
self.emit('click', child, keyPath, value);
}
function onChildExpand(child, keyPath, value) {
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
self.emit('expand', child, keyPath, value);
}
function onChildCollapse(child, keyPath, value) {
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
self.emit('collapse', child, keyPath, value);
}
function onChildRefresh(child, keyPath, value) {
if (self.withRootName || !self.isRoot) {
keyPath.unshift(name);
}
self.emit('refresh', child, keyPath, value);
}
function addDomEventListener(element, name, fn){
element.addEventListener(name, fn);
domEventListeners.push({element : element, name : name, fn : fn});
}
}
The text was updated successfully, but these errors were encountered:
in case someone needs it: https://www.diffchecker.com/CMh3zZKM
I did some attempts and it works, I am not going to pull request yet because I am not sure I placed my code in the right code block and it needs to be tidy
The text was updated successfully, but these errors were encountered: