cytoscape
Version:
Graph theory (a.k.a. network) library for analysis and visualisation
209 lines (169 loc) • 6.03 kB
JavaScript
import * as util from '../util';
import * as is from '../is';
import get from 'lodash/get';
import set from 'lodash/set';
import toPath from 'lodash/toPath';
let define = {
// access data field
data: function( params ){
let defaults = {
field: 'data',
bindingEvent: 'data',
allowBinding: false,
allowSetting: false,
allowGetting: false,
settingEvent: 'data',
settingTriggersEvent: false,
triggerFnName: 'trigger',
immutableKeys: {}, // key => true if immutable
updateStyle: false,
beforeGet: function( self ){},
beforeSet: function( self, obj ){},
onSet: function( self ){},
canSet: function( self ){ return true; }
};
params = util.extend( {}, defaults, params );
return function dataImpl( name, value ){
let p = params;
let self = this;
let selfIsArrayLike = self.length !== undefined;
let all = selfIsArrayLike ? self : [ self ]; // put in array if not array-like
let single = selfIsArrayLike ? self[0] : self;
// .data('foo', ...)
if (is.string(name)) { // set or get property
let isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
let path = isPathLike && toPath(name);
// .data('foo')
if( p.allowGetting && value === undefined ){ // get
let ret;
if( single ){
p.beforeGet( single );
// check if it's path and a field with the same name doesn't exist
if (path && single._private[ p.field ][ name ] === undefined) {
ret = get(single._private[ p.field ], path);
} else {
ret = single._private[ p.field ][ name ];
}
}
return ret;
// .data('foo', 'bar')
} else if( p.allowSetting && value !== undefined ){ // set
let valid = !p.immutableKeys[ name ];
if( valid ){
let change = { [name]: value };
p.beforeSet( self, change );
for( let i = 0, l = all.length; i < l; i++ ){
let ele = all[i];
if( p.canSet( ele ) ){
if (path && single._private[ p.field ][ name ] === undefined) {
set(ele._private[ p.field ], path, value);
} else {
ele._private[ p.field ][ name ] = value;
}
}
}
// update mappers if asked
if( p.updateStyle ){ self.updateStyle(); }
// call onSet callback
p.onSet( self );
if( p.settingTriggersEvent ){
self[ p.triggerFnName ]( p.settingEvent );
}
}
}
// .data({ 'foo': 'bar' })
} else if( p.allowSetting && is.plainObject( name ) ){ // extend
let obj = name;
let k, v;
let keys = Object.keys( obj );
p.beforeSet( self, obj );
for( let i = 0; i < keys.length; i++ ){
k = keys[ i ];
v = obj[ k ];
let valid = !p.immutableKeys[ k ];
if( valid ){
for( let j = 0; j < all.length; j++ ){
let ele = all[j];
if( p.canSet( ele ) ){
ele._private[ p.field ][ k ] = v;
}
}
}
}
// update mappers if asked
if( p.updateStyle ){ self.updateStyle(); }
// call onSet callback
p.onSet( self );
if( p.settingTriggersEvent ){
self[ p.triggerFnName ]( p.settingEvent );
}
// .data(function(){ ... })
} else if( p.allowBinding && is.fn( name ) ){ // bind to event
let fn = name;
self.on( p.bindingEvent, fn );
// .data()
} else if( p.allowGetting && name === undefined ){ // get whole object
let ret;
if( single ){
p.beforeGet( single );
ret = single._private[ p.field ];
}
return ret;
}
return self; // maintain chainability
}; // function
}, // data
// remove data field
removeData: function( params ){
let defaults = {
field: 'data',
event: 'data',
triggerFnName: 'trigger',
triggerEvent: false,
immutableKeys: {} // key => true if immutable
};
params = util.extend( {}, defaults, params );
return function removeDataImpl( names ){
let p = params;
let self = this;
let selfIsArrayLike = self.length !== undefined;
let all = selfIsArrayLike ? self : [ self ]; // put in array if not array-like
// .removeData('foo bar')
if( is.string( names ) ){ // then get the list of keys, and delete them
let keys = names.split( /\s+/ );
let l = keys.length;
for( let i = 0; i < l; i++ ){ // delete each non-empty key
let key = keys[ i ];
if( is.emptyString( key ) ){ continue; }
let valid = !p.immutableKeys[ key ]; // not valid if immutable
if( valid ){
for( let i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
all[ i_a ]._private[ p.field ][ key ] = undefined;
}
}
}
if( p.triggerEvent ){
self[ p.triggerFnName ]( p.event );
}
// .removeData()
} else if( names === undefined ){ // then delete all keys
for( let i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
let _privateFields = all[ i_a ]._private[ p.field ];
let keys = Object.keys( _privateFields );
for( let i = 0; i < keys.length; i++ ){
let key = keys[i];
let validKeyToDelete = !p.immutableKeys[ key ];
if( validKeyToDelete ){
_privateFields[ key ] = undefined;
}
}
}
if( p.triggerEvent ){
self[ p.triggerFnName ]( p.event );
}
}
return self; // maintain chaining
}; // function
}, // removeData
}; // define
export default define;