UNPKG

10.3 kBJavaScriptView Raw
1import * as is from '../is';
2import * as util from '../util';
3
4function styleCache( key, fn, ele ){
5 var _p = ele._private;
6 var cache = _p.styleCache = _p.styleCache || [];
7 var val;
8
9 if( (val = cache[key]) != null ){
10 return val;
11 } else {
12 val = cache[key] = fn( ele );
13
14 return val;
15 }
16}
17
18function cacheStyleFunction( key, fn ){
19 key = util.hashString( key );
20
21 return function cachedStyleFunction( ele ){
22 return styleCache( key, fn, ele );
23 };
24}
25
26function cachePrototypeStyleFunction( key, fn ){
27 key = util.hashString( key );
28
29 let selfFn = ele => fn.call( ele );
30
31 return function cachedPrototypeStyleFunction(){
32 var ele = this[0];
33
34 if( ele ){
35 return styleCache( key, selfFn, ele );
36 }
37 };
38}
39
40let elesfn = ({
41
42 recalculateRenderedStyle: function( useCache ){
43 let cy = this.cy();
44 let renderer = cy.renderer();
45 let styleEnabled = cy.styleEnabled();
46
47 if( renderer && styleEnabled ){
48 renderer.recalculateRenderedStyle( this, useCache );
49 }
50
51 return this;
52 },
53
54 dirtyStyleCache: function(){
55 let cy = this.cy();
56 let dirty = ele => ele._private.styleCache = null;
57
58 if( cy.hasCompoundNodes() ){
59 let eles;
60
61 eles = this.spawnSelf()
62 .merge( this.descendants() )
63 .merge( this.parents() )
64 ;
65
66 eles.merge( eles.connectedEdges() );
67
68 eles.forEach( dirty );
69 } else {
70 this.forEach( ele => {
71 dirty( ele );
72
73 ele.connectedEdges().forEach( dirty );
74 } );
75 }
76
77 return this;
78 },
79
80 // fully updates (recalculates) the style for the elements
81 updateStyle: function( notifyRenderer ){
82 let cy = this._private.cy;
83
84 if( !cy.styleEnabled() ){ return this; }
85
86 if( cy.batching() ){
87 let bEles = cy._private.batchStyleEles;
88
89 bEles.merge( this );
90
91 return this; // chaining and exit early when batching
92 }
93
94 let hasCompounds = cy.hasCompoundNodes();
95 let style = cy.style();
96 let updatedEles = this;
97
98 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
99
100 if( hasCompounds ){ // then add everything up and down for compound selector checks
101 updatedEles = this.spawnSelf().merge( this.descendants() ).merge( this.parents() );
102 }
103
104 let changedEles = style.apply( updatedEles );
105
106 if( notifyRenderer ){
107 changedEles.emitAndNotify( 'style' ); // let renderer know we changed style
108 } else {
109 changedEles.emit( 'style' ); // just fire the event
110 }
111
112 return this; // chaining
113 },
114
115 // get the internal parsed style object for the specified property
116 parsedStyle: function( property, includeNonDefault = true ){
117 let ele = this[0];
118 let cy = ele.cy();
119
120 if( !cy.styleEnabled() ){ return; }
121
122 if( ele ){
123 let overriddenStyle = ele._private.style[ property ];
124
125 if( overriddenStyle != null ){
126 return overriddenStyle;
127 } else if( includeNonDefault ){
128 return cy.style().getDefaultProperty( property );
129 } else {
130 return null;
131 }
132 }
133 },
134
135 numericStyle: function( property ){
136 let ele = this[0];
137
138 if( !ele.cy().styleEnabled() ){ return; }
139
140 if( ele ){
141 let pstyle = ele.pstyle( property );
142
143 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
144 }
145 },
146
147 numericStyleUnits: function( property ){
148 let ele = this[0];
149
150 if( !ele.cy().styleEnabled() ){ return; }
151
152 if( ele ){
153 return ele.pstyle( property ).units;
154 }
155 },
156
157 // get the specified css property as a rendered value (i.e. on-screen value)
158 // or get the whole rendered style if no property specified (NB doesn't allow setting)
159 renderedStyle: function( property ){
160 let cy = this.cy();
161 if( !cy.styleEnabled() ){ return this; }
162
163 let ele = this[0];
164
165 if( ele ){
166 return cy.style().getRenderedStyle( ele, property );
167 }
168 },
169
170 // read the calculated css style of the element or override the style (via a bypass)
171 style: function( name, value ){
172 let cy = this.cy();
173
174 if( !cy.styleEnabled() ){ return this; }
175
176 let updateTransitions = false;
177 let style = cy.style();
178
179 if( is.plainObject( name ) ){ // then extend the bypass
180 let props = name;
181 style.applyBypass( this, props, updateTransitions );
182
183 this.emitAndNotify( 'style' ); // let the renderer know we've updated style
184
185 } else if( is.string( name ) ){
186
187 if( value === undefined ){ // then get the property from the style
188 let ele = this[0];
189
190 if( ele ){
191 return style.getStylePropertyValue( ele, name );
192 } else { // empty collection => can't get any value
193 return;
194 }
195
196 } else { // then set the bypass with the property value
197 style.applyBypass( this, name, value, updateTransitions );
198
199 this.emitAndNotify( 'style' ); // let the renderer know we've updated style
200 }
201
202 } else if( name === undefined ){
203 let ele = this[0];
204
205 if( ele ){
206 return style.getRawStyle( ele );
207 } else { // empty collection => can't get any value
208 return;
209 }
210 }
211
212 return this; // chaining
213 },
214
215 removeStyle: function( names ){
216 let cy = this.cy();
217
218 if( !cy.styleEnabled() ){ return this; }
219
220 let updateTransitions = false;
221 let style = cy.style();
222 let eles = this;
223
224 if( names === undefined ){
225 for( let i = 0; i < eles.length; i++ ){
226 let ele = eles[ i ];
227
228 style.removeAllBypasses( ele, updateTransitions );
229 }
230 } else {
231 names = names.split( /\s+/ );
232
233 for( let i = 0; i < eles.length; i++ ){
234 let ele = eles[ i ];
235
236 style.removeBypasses( ele, names, updateTransitions );
237 }
238 }
239
240 this.emitAndNotify( 'style' ); // let the renderer know we've updated style
241
242 return this; // chaining
243 },
244
245 show: function(){
246 this.css( 'display', 'element' );
247 return this; // chaining
248 },
249
250 hide: function(){
251 this.css( 'display', 'none' );
252 return this; // chaining
253 },
254
255 effectiveOpacity: function(){
256 let cy = this.cy();
257 if( !cy.styleEnabled() ){ return 1; }
258
259 let hasCompoundNodes = cy.hasCompoundNodes();
260 let ele = this[0];
261
262 if( ele ){
263 let _p = ele._private;
264 let parentOpacity = ele.pstyle( 'opacity' ).value;
265
266 if( !hasCompoundNodes ){ return parentOpacity; }
267
268 let parents = !_p.data.parent ? null : ele.parents();
269
270 if( parents ){
271 for( let i = 0; i < parents.length; i++ ){
272 let parent = parents[ i ];
273 let opacity = parent.pstyle( 'opacity' ).value;
274
275 parentOpacity = opacity * parentOpacity;
276 }
277 }
278
279 return parentOpacity;
280 }
281 },
282
283 transparent: function(){
284 let cy = this.cy();
285 if( !cy.styleEnabled() ){ return false; }
286
287 let ele = this[0];
288 let hasCompoundNodes = ele.cy().hasCompoundNodes();
289
290 if( ele ){
291 if( !hasCompoundNodes ){
292 return ele.pstyle( 'opacity' ).value === 0;
293 } else {
294 return ele.effectiveOpacity() === 0;
295 }
296 }
297 },
298
299 backgrounding: function(){
300 let cy = this.cy();
301 if( !cy.styleEnabled() ){ return false; }
302
303 let ele = this[0];
304
305 return ele._private.backgrounding ? true : false;
306 }
307
308});
309
310function checkCompound( ele, parentOk ){
311 let _p = ele._private;
312 let parents = _p.data.parent ? ele.parents() : null;
313
314 if( parents ){ for( let i = 0; i < parents.length; i++ ){
315 let parent = parents[ i ];
316
317 if( !parentOk( parent ) ){ return false; }
318 } }
319
320 return true;
321}
322
323function defineDerivedStateFunction( specs ){
324 let ok = specs.ok;
325 let edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
326 let parentOk = specs.parentOk || specs.ok;
327
328 return function(){
329 let cy = this.cy();
330 if( !cy.styleEnabled() ){ return true; }
331
332 let ele = this[0];
333 let hasCompoundNodes = cy.hasCompoundNodes();
334
335 if( ele ){
336 let _p = ele._private;
337
338 if( !ok( ele ) ){ return false; }
339
340 if( ele.isNode() ){
341 return !hasCompoundNodes || checkCompound( ele, parentOk );
342 } else {
343 let src = _p.source;
344 let tgt = _p.target;
345
346 return ( edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) ) &&
347 ( src === tgt || ( edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)) ) );
348 }
349 }
350 };
351}
352
353let eleTakesUpSpace = cacheStyleFunction( 'eleTakesUpSpace', function( ele ){
354 return (
355 ele.pstyle( 'display' ).value === 'element'
356 && ele.width() !== 0
357 && ( ele.isNode() ? ele.height() !== 0 : true )
358 );
359} );
360
361elesfn.takesUpSpace = cachePrototypeStyleFunction( 'takesUpSpace', defineDerivedStateFunction({
362 ok: eleTakesUpSpace
363}) );
364
365let eleInteractive = cacheStyleFunction( 'eleInteractive', function( ele ){
366 return (
367 ele.pstyle('events').value === 'yes'
368 && ele.pstyle('visibility').value === 'visible'
369 && eleTakesUpSpace( ele )
370 );
371} );
372
373let parentInteractive = cacheStyleFunction( 'parentInteractive', function( parent ){
374 return (
375 parent.pstyle('visibility').value === 'visible'
376 && eleTakesUpSpace( parent )
377 );
378} );
379
380elesfn.interactive = cachePrototypeStyleFunction( 'interactive', defineDerivedStateFunction({
381 ok: eleInteractive,
382 parentOk: parentInteractive,
383 edgeOkViaNode: eleTakesUpSpace
384}) );
385
386elesfn.noninteractive = function(){
387 let ele = this[0];
388
389 if( ele ){
390 return !ele.interactive();
391 }
392};
393
394let eleVisible = cacheStyleFunction( 'eleVisible', function( ele ){
395 return (
396 ele.pstyle( 'visibility' ).value === 'visible'
397 && ele.pstyle( 'opacity' ).pfValue !== 0
398 && eleTakesUpSpace( ele )
399 );
400} );
401
402let edgeVisibleViaNode = eleTakesUpSpace;
403
404elesfn.visible = cachePrototypeStyleFunction( 'visible', defineDerivedStateFunction({
405 ok: eleVisible,
406 edgeOkViaNode: edgeVisibleViaNode
407}) );
408
409elesfn.hidden = function(){
410 let ele = this[0];
411
412 if( ele ){
413 return !ele.visible();
414 }
415};
416
417elesfn.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function(){
418 if( !this.cy().styleEnabled() ){ return false; }
419
420 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
421});
422
423elesfn.bypass = elesfn.css = elesfn.style;
424elesfn.renderedCss = elesfn.renderedStyle;
425elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle;
426elesfn.pstyle = elesfn.parsedStyle;
427
428export default elesfn;