UNPKG

10.6 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 updatedEles = this;
96
97 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
98
99 if( hasCompounds ){ // then add everything up and down for compound selector checks
100 updatedEles = this.spawnSelf().merge( this.descendants() ).merge( this.parents() );
101 }
102
103 // let changedEles = style.apply( updatedEles );
104 let changedEles = 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 updatedEles.forEach(ele => ele._private.styleDirty = true);
113
114 return this; // chaining
115 },
116
117 // get the internal parsed style object for the specified property
118 parsedStyle: function( property, includeNonDefault = true ){
119 let ele = this[0];
120 let cy = ele.cy();
121
122 if( !cy.styleEnabled() ){ return; }
123
124 if( ele ){
125 if( ele._private.styleDirty ){
126 ele._private.styleDirty = false;
127
128 cy.style().apply(ele);
129 ele.emitAndNotify('style');
130 }
131
132 let overriddenStyle = ele._private.style[ property ];
133
134 if( overriddenStyle != null ){
135 return overriddenStyle;
136 } else if( includeNonDefault ){
137 return cy.style().getDefaultProperty( property );
138 } else {
139 return null;
140 }
141 }
142 },
143
144 numericStyle: function( property ){
145 let ele = this[0];
146
147 if( !ele.cy().styleEnabled() ){ return; }
148
149 if( ele ){
150 let pstyle = ele.pstyle( property );
151
152 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
153 }
154 },
155
156 numericStyleUnits: function( property ){
157 let ele = this[0];
158
159 if( !ele.cy().styleEnabled() ){ return; }
160
161 if( ele ){
162 return ele.pstyle( property ).units;
163 }
164 },
165
166 // get the specified css property as a rendered value (i.e. on-screen value)
167 // or get the whole rendered style if no property specified (NB doesn't allow setting)
168 renderedStyle: function( property ){
169 let cy = this.cy();
170 if( !cy.styleEnabled() ){ return this; }
171
172 let ele = this[0];
173
174 if( ele ){
175 return cy.style().getRenderedStyle( ele, property );
176 }
177 },
178
179 // read the calculated css style of the element or override the style (via a bypass)
180 style: function( name, value ){
181 let cy = this.cy();
182
183 if( !cy.styleEnabled() ){ return this; }
184
185 let updateTransitions = false;
186 let style = cy.style();
187
188 if( is.plainObject( name ) ){ // then extend the bypass
189 let props = name;
190 style.applyBypass( this, props, updateTransitions );
191
192 this.emitAndNotify( 'style' ); // let the renderer know we've updated style
193
194 } else if( is.string( name ) ){
195
196 if( value === undefined ){ // then get the property from the style
197 let ele = this[0];
198
199 if( ele ){
200 return style.getStylePropertyValue( ele, name );
201 } else { // empty collection => can't get any value
202 return;
203 }
204
205 } else { // then set the bypass with the property value
206 style.applyBypass( this, name, value, updateTransitions );
207
208 this.emitAndNotify( 'style' ); // let the renderer know we've updated style
209 }
210
211 } else if( name === undefined ){
212 let ele = this[0];
213
214 if( ele ){
215 return style.getRawStyle( ele );
216 } else { // empty collection => can't get any value
217 return;
218 }
219 }
220
221 return this; // chaining
222 },
223
224 removeStyle: function( names ){
225 let cy = this.cy();
226
227 if( !cy.styleEnabled() ){ return this; }
228
229 let updateTransitions = false;
230 let style = cy.style();
231 let eles = this;
232
233 if( names === undefined ){
234 for( let i = 0; i < eles.length; i++ ){
235 let ele = eles[ i ];
236
237 style.removeAllBypasses( ele, updateTransitions );
238 }
239 } else {
240 names = names.split( /\s+/ );
241
242 for( let i = 0; i < eles.length; i++ ){
243 let ele = eles[ i ];
244
245 style.removeBypasses( ele, names, updateTransitions );
246 }
247 }
248
249 this.emitAndNotify( 'style' ); // let the renderer know we've updated style
250
251 return this; // chaining
252 },
253
254 show: function(){
255 this.css( 'display', 'element' );
256 return this; // chaining
257 },
258
259 hide: function(){
260 this.css( 'display', 'none' );
261 return this; // chaining
262 },
263
264 effectiveOpacity: function(){
265 let cy = this.cy();
266 if( !cy.styleEnabled() ){ return 1; }
267
268 let hasCompoundNodes = cy.hasCompoundNodes();
269 let ele = this[0];
270
271 if( ele ){
272 let _p = ele._private;
273 let parentOpacity = ele.pstyle( 'opacity' ).value;
274
275 if( !hasCompoundNodes ){ return parentOpacity; }
276
277 let parents = !_p.data.parent ? null : ele.parents();
278
279 if( parents ){
280 for( let i = 0; i < parents.length; i++ ){
281 let parent = parents[ i ];
282 let opacity = parent.pstyle( 'opacity' ).value;
283
284 parentOpacity = opacity * parentOpacity;
285 }
286 }
287
288 return parentOpacity;
289 }
290 },
291
292 transparent: function(){
293 let cy = this.cy();
294 if( !cy.styleEnabled() ){ return false; }
295
296 let ele = this[0];
297 let hasCompoundNodes = ele.cy().hasCompoundNodes();
298
299 if( ele ){
300 if( !hasCompoundNodes ){
301 return ele.pstyle( 'opacity' ).value === 0;
302 } else {
303 return ele.effectiveOpacity() === 0;
304 }
305 }
306 },
307
308 backgrounding: function(){
309 let cy = this.cy();
310 if( !cy.styleEnabled() ){ return false; }
311
312 let ele = this[0];
313
314 return ele._private.backgrounding ? true : false;
315 }
316
317});
318
319function checkCompound( ele, parentOk ){
320 let _p = ele._private;
321 let parents = _p.data.parent ? ele.parents() : null;
322
323 if( parents ){ for( let i = 0; i < parents.length; i++ ){
324 let parent = parents[ i ];
325
326 if( !parentOk( parent ) ){ return false; }
327 } }
328
329 return true;
330}
331
332function defineDerivedStateFunction( specs ){
333 let ok = specs.ok;
334 let edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
335 let parentOk = specs.parentOk || specs.ok;
336
337 return function(){
338 let cy = this.cy();
339 if( !cy.styleEnabled() ){ return true; }
340
341 let ele = this[0];
342 let hasCompoundNodes = cy.hasCompoundNodes();
343
344 if( ele ){
345 let _p = ele._private;
346
347 if( !ok( ele ) ){ return false; }
348
349 if( ele.isNode() ){
350 return !hasCompoundNodes || checkCompound( ele, parentOk );
351 } else {
352 let src = _p.source;
353 let tgt = _p.target;
354
355 return ( edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) ) &&
356 ( src === tgt || ( edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)) ) );
357 }
358 }
359 };
360}
361
362let eleTakesUpSpace = cacheStyleFunction( 'eleTakesUpSpace', function( ele ){
363 return (
364 ele.pstyle( 'display' ).value === 'element'
365 && ele.width() !== 0
366 && ( ele.isNode() ? ele.height() !== 0 : true )
367 );
368} );
369
370elesfn.takesUpSpace = cachePrototypeStyleFunction( 'takesUpSpace', defineDerivedStateFunction({
371 ok: eleTakesUpSpace
372}) );
373
374let eleInteractive = cacheStyleFunction( 'eleInteractive', function( ele ){
375 return (
376 ele.pstyle('events').value === 'yes'
377 && ele.pstyle('visibility').value === 'visible'
378 && eleTakesUpSpace( ele )
379 );
380} );
381
382let parentInteractive = cacheStyleFunction( 'parentInteractive', function( parent ){
383 return (
384 parent.pstyle('visibility').value === 'visible'
385 && eleTakesUpSpace( parent )
386 );
387} );
388
389elesfn.interactive = cachePrototypeStyleFunction( 'interactive', defineDerivedStateFunction({
390 ok: eleInteractive,
391 parentOk: parentInteractive,
392 edgeOkViaNode: eleTakesUpSpace
393}) );
394
395elesfn.noninteractive = function(){
396 let ele = this[0];
397
398 if( ele ){
399 return !ele.interactive();
400 }
401};
402
403let eleVisible = cacheStyleFunction( 'eleVisible', function( ele ){
404 return (
405 ele.pstyle( 'visibility' ).value === 'visible'
406 && ele.pstyle( 'opacity' ).pfValue !== 0
407 && eleTakesUpSpace( ele )
408 );
409} );
410
411let edgeVisibleViaNode = eleTakesUpSpace;
412
413elesfn.visible = cachePrototypeStyleFunction( 'visible', defineDerivedStateFunction({
414 ok: eleVisible,
415 edgeOkViaNode: edgeVisibleViaNode
416}) );
417
418elesfn.hidden = function(){
419 let ele = this[0];
420
421 if( ele ){
422 return !ele.visible();
423 }
424};
425
426elesfn.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function(){
427 if( !this.cy().styleEnabled() ){ return false; }
428
429 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
430});
431
432elesfn.bypass = elesfn.css = elesfn.style;
433elesfn.renderedCss = elesfn.renderedStyle;
434elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle;
435elesfn.pstyle = elesfn.parsedStyle;
436
437export default elesfn;