|
import * as is from '../is'; |
|
import * as util from '../util'; |
|
|
|
function styleCache( key, fn, ele ){ |
|
var _p = ele._private; |
|
var cache = _p.styleCache = _p.styleCache || []; |
|
var val; |
|
|
|
if( (val = cache[key]) != null ){ |
|
return val; |
|
} else { |
|
val = cache[key] = fn( ele ); |
|
|
|
return val; |
|
} |
|
} |
|
|
|
function cacheStyleFunction( key, fn ){ |
|
key = util.hashString( key ); |
|
|
|
return function cachedStyleFunction( ele ){ |
|
return styleCache( key, fn, ele ); |
|
}; |
|
} |
|
|
|
function cachePrototypeStyleFunction( key, fn ){ |
|
key = util.hashString( key ); |
|
|
|
let selfFn = ele => fn.call( ele ); |
|
|
|
return function cachedPrototypeStyleFunction(){ |
|
var ele = this[0]; |
|
|
|
if( ele ){ |
|
return styleCache( key, selfFn, ele ); |
|
} |
|
}; |
|
} |
|
|
|
let elesfn = ({ |
|
|
|
recalculateRenderedStyle: function( useCache ){ |
|
let cy = this.cy(); |
|
let renderer = cy.renderer(); |
|
let styleEnabled = cy.styleEnabled(); |
|
|
|
if( renderer && styleEnabled ){ |
|
renderer.recalculateRenderedStyle( this, useCache ); |
|
} |
|
|
|
return this; |
|
}, |
|
|
|
dirtyStyleCache: function(){ |
|
let cy = this.cy(); |
|
let dirty = ele => ele._private.styleCache = null; |
|
|
|
if( cy.hasCompoundNodes() ){ |
|
let eles; |
|
|
|
eles = this.spawnSelf() |
|
.merge( this.descendants() ) |
|
.merge( this.parents() ) |
|
; |
|
|
|
eles.merge( eles.connectedEdges() ); |
|
|
|
eles.forEach( dirty ); |
|
} else { |
|
this.forEach( ele => { |
|
dirty( ele ); |
|
|
|
ele.connectedEdges().forEach( dirty ); |
|
} ); |
|
} |
|
|
|
return this; |
|
}, |
|
|
|
|
|
updateStyle: function( notifyRenderer ){ |
|
let cy = this._private.cy; |
|
|
|
if( !cy.styleEnabled() ){ return this; } |
|
|
|
if( cy.batching() ){ |
|
let bEles = cy._private.batchStyleEles; |
|
|
|
bEles.merge( this ); |
|
|
|
return this; |
|
} |
|
|
|
let hasCompounds = cy.hasCompoundNodes(); |
|
let updatedEles = this; |
|
|
|
notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; |
|
|
|
if( hasCompounds ){ |
|
updatedEles = this.spawnSelf().merge( this.descendants() ).merge( this.parents() ); |
|
} |
|
|
|
|
|
let changedEles = updatedEles; |
|
|
|
if( notifyRenderer ){ |
|
changedEles.emitAndNotify( 'style' ); |
|
} else { |
|
changedEles.emit( 'style' ); |
|
} |
|
|
|
updatedEles.forEach(ele => ele._private.styleDirty = true); |
|
|
|
return this; |
|
}, |
|
|
|
|
|
cleanStyle: function(){ |
|
let cy = this.cy(); |
|
|
|
if( !cy.styleEnabled() ){ return; } |
|
|
|
for( let i = 0; i < this.length; i++ ){ |
|
let ele = this[i]; |
|
|
|
if( ele._private.styleDirty ){ |
|
|
|
ele._private.styleDirty = false; |
|
|
|
cy.style().apply(ele); |
|
} |
|
} |
|
}, |
|
|
|
|
|
parsedStyle: function( property, includeNonDefault = true ){ |
|
let ele = this[0]; |
|
let cy = ele.cy(); |
|
|
|
if( !cy.styleEnabled() ){ return; } |
|
|
|
if( ele ){ |
|
this.cleanStyle(); |
|
|
|
let overriddenStyle = ele._private.style[ property ]; |
|
|
|
if( overriddenStyle != null ){ |
|
return overriddenStyle; |
|
} else if( includeNonDefault ){ |
|
return cy.style().getDefaultProperty( property ); |
|
} else { |
|
return null; |
|
} |
|
} |
|
}, |
|
|
|
numericStyle: function( property ){ |
|
let ele = this[0]; |
|
|
|
if( !ele.cy().styleEnabled() ){ return; } |
|
|
|
if( ele ){ |
|
let pstyle = ele.pstyle( property ); |
|
|
|
return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value; |
|
} |
|
}, |
|
|
|
numericStyleUnits: function( property ){ |
|
let ele = this[0]; |
|
|
|
if( !ele.cy().styleEnabled() ){ return; } |
|
|
|
if( ele ){ |
|
return ele.pstyle( property ).units; |
|
} |
|
}, |
|
|
|
|
|
|
|
renderedStyle: function( property ){ |
|
let cy = this.cy(); |
|
if( !cy.styleEnabled() ){ return this; } |
|
|
|
let ele = this[0]; |
|
|
|
if( ele ){ |
|
return cy.style().getRenderedStyle( ele, property ); |
|
} |
|
}, |
|
|
|
|
|
style: function( name, value ){ |
|
let cy = this.cy(); |
|
|
|
if( !cy.styleEnabled() ){ return this; } |
|
|
|
let updateTransitions = false; |
|
let style = cy.style(); |
|
|
|
if( is.plainObject( name ) ){ |
|
let props = name; |
|
style.applyBypass( this, props, updateTransitions ); |
|
|
|
this.emitAndNotify( 'style' ); |
|
|
|
} else if( is.string( name ) ){ |
|
|
|
if( value === undefined ){ |
|
let ele = this[0]; |
|
|
|
if( ele ){ |
|
return style.getStylePropertyValue( ele, name ); |
|
} else { |
|
return; |
|
} |
|
|
|
} else { |
|
style.applyBypass( this, name, value, updateTransitions ); |
|
|
|
this.emitAndNotify( 'style' ); |
|
} |
|
|
|
} else if( name === undefined ){ |
|
let ele = this[0]; |
|
|
|
if( ele ){ |
|
return style.getRawStyle( ele ); |
|
} else { |
|
return; |
|
} |
|
} |
|
|
|
return this; |
|
}, |
|
|
|
removeStyle: function( names ){ |
|
let cy = this.cy(); |
|
|
|
if( !cy.styleEnabled() ){ return this; } |
|
|
|
let updateTransitions = false; |
|
let style = cy.style(); |
|
let eles = this; |
|
|
|
if( names === undefined ){ |
|
for( let i = 0; i < eles.length; i++ ){ |
|
let ele = eles[ i ]; |
|
|
|
style.removeAllBypasses( ele, updateTransitions ); |
|
} |
|
} else { |
|
names = names.split( /\s+/ ); |
|
|
|
for( let i = 0; i < eles.length; i++ ){ |
|
let ele = eles[ i ]; |
|
|
|
style.removeBypasses( ele, names, updateTransitions ); |
|
} |
|
} |
|
|
|
this.emitAndNotify( 'style' ); |
|
|
|
return this; |
|
}, |
|
|
|
show: function(){ |
|
this.css( 'display', 'element' ); |
|
return this; |
|
}, |
|
|
|
hide: function(){ |
|
this.css( 'display', 'none' ); |
|
return this; |
|
}, |
|
|
|
effectiveOpacity: function(){ |
|
let cy = this.cy(); |
|
if( !cy.styleEnabled() ){ return 1; } |
|
|
|
let hasCompoundNodes = cy.hasCompoundNodes(); |
|
let ele = this[0]; |
|
|
|
if( ele ){ |
|
let _p = ele._private; |
|
let parentOpacity = ele.pstyle( 'opacity' ).value; |
|
|
|
if( !hasCompoundNodes ){ return parentOpacity; } |
|
|
|
let parents = !_p.data.parent ? null : ele.parents(); |
|
|
|
if( parents ){ |
|
for( let i = 0; i < parents.length; i++ ){ |
|
let parent = parents[ i ]; |
|
let opacity = parent.pstyle( 'opacity' ).value; |
|
|
|
parentOpacity = opacity * parentOpacity; |
|
} |
|
} |
|
|
|
return parentOpacity; |
|
} |
|
}, |
|
|
|
transparent: function(){ |
|
let cy = this.cy(); |
|
if( !cy.styleEnabled() ){ return false; } |
|
|
|
let ele = this[0]; |
|
let hasCompoundNodes = ele.cy().hasCompoundNodes(); |
|
|
|
if( ele ){ |
|
if( !hasCompoundNodes ){ |
|
return ele.pstyle( 'opacity' ).value === 0; |
|
} else { |
|
return ele.effectiveOpacity() === 0; |
|
} |
|
} |
|
}, |
|
|
|
backgrounding: function(){ |
|
let cy = this.cy(); |
|
if( !cy.styleEnabled() ){ return false; } |
|
|
|
let ele = this[0]; |
|
|
|
return ele._private.backgrounding ? true : false; |
|
} |
|
|
|
}); |
|
|
|
function checkCompound( ele, parentOk ){ |
|
let _p = ele._private; |
|
let parents = _p.data.parent ? ele.parents() : null; |
|
|
|
if( parents ){ for( let i = 0; i < parents.length; i++ ){ |
|
let parent = parents[ i ]; |
|
|
|
if( !parentOk( parent ) ){ return false; } |
|
} } |
|
|
|
return true; |
|
} |
|
|
|
function defineDerivedStateFunction( specs ){ |
|
let ok = specs.ok; |
|
let edgeOkViaNode = specs.edgeOkViaNode || specs.ok; |
|
let parentOk = specs.parentOk || specs.ok; |
|
|
|
return function(){ |
|
let cy = this.cy(); |
|
if( !cy.styleEnabled() ){ return true; } |
|
|
|
let ele = this[0]; |
|
let hasCompoundNodes = cy.hasCompoundNodes(); |
|
|
|
if( ele ){ |
|
let _p = ele._private; |
|
|
|
if( !ok( ele ) ){ return false; } |
|
|
|
if( ele.isNode() ){ |
|
return !hasCompoundNodes || checkCompound( ele, parentOk ); |
|
} else { |
|
let src = _p.source; |
|
let tgt = _p.target; |
|
|
|
return ( edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) ) && |
|
( src === tgt || ( edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)) ) ); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
let eleTakesUpSpace = cacheStyleFunction( 'eleTakesUpSpace', function( ele ){ |
|
return ( |
|
ele.pstyle( 'display' ).value === 'element' |
|
&& ele.width() !== 0 |
|
&& ( ele.isNode() ? ele.height() !== 0 : true ) |
|
); |
|
} ); |
|
|
|
elesfn.takesUpSpace = cachePrototypeStyleFunction( 'takesUpSpace', defineDerivedStateFunction({ |
|
ok: eleTakesUpSpace |
|
}) ); |
|
|
|
let eleInteractive = cacheStyleFunction( 'eleInteractive', function( ele ){ |
|
return ( |
|
ele.pstyle('events').value === 'yes' |
|
&& ele.pstyle('visibility').value === 'visible' |
|
&& eleTakesUpSpace( ele ) |
|
); |
|
} ); |
|
|
|
let parentInteractive = cacheStyleFunction( 'parentInteractive', function( parent ){ |
|
return ( |
|
parent.pstyle('visibility').value === 'visible' |
|
&& eleTakesUpSpace( parent ) |
|
); |
|
} ); |
|
|
|
elesfn.interactive = cachePrototypeStyleFunction( 'interactive', defineDerivedStateFunction({ |
|
ok: eleInteractive, |
|
parentOk: parentInteractive, |
|
edgeOkViaNode: eleTakesUpSpace |
|
}) ); |
|
|
|
elesfn.noninteractive = function(){ |
|
let ele = this[0]; |
|
|
|
if( ele ){ |
|
return !ele.interactive(); |
|
} |
|
}; |
|
|
|
let eleVisible = cacheStyleFunction( 'eleVisible', function( ele ){ |
|
return ( |
|
ele.pstyle( 'visibility' ).value === 'visible' |
|
&& ele.pstyle( 'opacity' ).pfValue !== 0 |
|
&& eleTakesUpSpace( ele ) |
|
); |
|
} ); |
|
|
|
let edgeVisibleViaNode = eleTakesUpSpace; |
|
|
|
elesfn.visible = cachePrototypeStyleFunction( 'visible', defineDerivedStateFunction({ |
|
ok: eleVisible, |
|
edgeOkViaNode: edgeVisibleViaNode |
|
}) ); |
|
|
|
elesfn.hidden = function(){ |
|
let ele = this[0]; |
|
|
|
if( ele ){ |
|
return !ele.visible(); |
|
} |
|
}; |
|
|
|
elesfn.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function(){ |
|
if( !this.cy().styleEnabled() ){ return false; } |
|
|
|
return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace(); |
|
}); |
|
|
|
elesfn.bypass = elesfn.css = elesfn.style; |
|
elesfn.renderedCss = elesfn.renderedStyle; |
|
elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle; |
|
elesfn.pstyle = elesfn.parsedStyle; |
|
|
|
export default elesfn; |
|
|