UNPKG

6.46 kBJavaScriptView Raw
1import * as util from'./util';
2import define from './define';
3import Collection from './collection';
4import Core from './core';
5import incExts from './extensions';
6import * as is from './is';
7import Emitter from './emitter';
8
9// registered extensions to cytoscape, indexed by name
10let extensions = {};
11
12// registered modules for extensions, indexed by name
13let modules = {};
14
15function setExtension( type, name, registrant ){
16
17 let ext = registrant;
18
19 let overrideErr = function( field ){
20 util.error( 'Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden' );
21 };
22
23 if( type === 'core' ){
24 if( Core.prototype[ name ] ){
25 return overrideErr( name );
26 } else {
27 Core.prototype[ name ] = registrant;
28 }
29
30 } else if( type === 'collection' ){
31 if( Collection.prototype[ name ] ){
32 return overrideErr( name );
33 } else {
34 Collection.prototype[ name ] = registrant;
35 }
36
37 } else if( type === 'layout' ){
38 // fill in missing layout functions in the prototype
39
40 let Layout = function( options ){
41 this.options = options;
42
43 registrant.call( this, options );
44
45 // make sure layout has _private for use w/ std apis like .on()
46 if( !is.plainObject( this._private ) ){
47 this._private = {};
48 }
49
50 this._private.cy = options.cy;
51 this._private.listeners = [];
52
53 this.createEmitter();
54 };
55
56 let layoutProto = Layout.prototype = Object.create( registrant.prototype );
57
58 let optLayoutFns = [];
59
60 for( let i = 0; i < optLayoutFns.length; i++ ){
61 let fnName = optLayoutFns[ i ];
62
63 layoutProto[ fnName ] = layoutProto[ fnName ] || function(){ return this; };
64 }
65
66 // either .start() or .run() is defined, so autogen the other
67 if( layoutProto.start && !layoutProto.run ){
68 layoutProto.run = function(){ this.start(); return this; };
69 } else if( !layoutProto.start && layoutProto.run ){
70 layoutProto.start = function(){ this.run(); return this; };
71 }
72
73 let regStop = registrant.prototype.stop;
74 layoutProto.stop = function(){
75 let opts = this.options;
76
77 if( opts && opts.animate ){
78 let anis = this.animations;
79
80 if( anis ){
81 for( let i = 0; i < anis.length; i++ ){
82 anis[ i ].stop();
83 }
84 }
85 }
86
87 if( regStop ){
88 regStop.call( this );
89 } else {
90 this.emit( 'layoutstop' );
91 }
92
93 return this;
94 };
95
96 if( !layoutProto.destroy ){
97 layoutProto.destroy = function(){
98 return this;
99 };
100 }
101
102 layoutProto.cy = function(){
103 return this._private.cy;
104 };
105
106 let getCy = layout => layout._private.cy;
107
108 let emitterOpts = {
109 addEventFields: function( layout, evt ){
110 evt.layout = layout;
111 evt.cy = getCy(layout);
112 evt.target = layout;
113 },
114 bubble: function(){ return true; },
115 parent: function( layout ){ return getCy(layout); }
116 };
117
118 util.assign( layoutProto, {
119 createEmitter: function(){
120 this._private.emitter = new Emitter( emitterOpts, this );
121
122 return this;
123 },
124 emitter: function(){ return this._private.emitter; },
125 on: function( evt, cb ){ this.emitter().on( evt, cb ); return this; },
126 one: function( evt, cb ){ this.emitter().one( evt, cb ); return this; },
127 once: function( evt, cb ){ this.emitter().one( evt, cb ); return this; },
128 removeListener: function( evt, cb ){ this.emitter().removeListener( evt, cb ); return this; },
129 removeAllListeners: function(){ this.emitter().removeAllListeners(); return this; },
130 emit: function( evt, params ){ this.emitter().emit( evt, params ); return this; }
131 } );
132
133 define.eventAliasesOn( layoutProto );
134
135 ext = Layout; // replace with our wrapped layout
136
137 } else if( type === 'renderer' && name !== 'null' && name !== 'base' ){
138 // user registered renderers inherit from base
139
140 let BaseRenderer = getExtension( 'renderer', 'base' );
141 let bProto = BaseRenderer.prototype;
142 let RegistrantRenderer = registrant;
143 let rProto = registrant.prototype;
144
145 let Renderer = function(){
146 BaseRenderer.apply( this, arguments );
147 RegistrantRenderer.apply( this, arguments );
148 };
149
150 let proto = Renderer.prototype;
151
152 for( let pName in bProto ){
153 let pVal = bProto[ pName ];
154 let existsInR = rProto[ pName ] != null;
155
156 if( existsInR ){
157 return overrideErr( pName );
158 }
159
160 proto[ pName ] = pVal; // take impl from base
161 }
162
163 for( let pName in rProto ){
164 proto[ pName ] = rProto[ pName ]; // take impl from registrant
165 }
166
167 bProto.clientFunctions.forEach( function( name ){
168 proto[ name ] = proto[ name ] || function(){
169 util.error( 'Renderer does not implement `renderer.' + name + '()` on its prototype' );
170 };
171 } );
172
173 ext = Renderer;
174
175 }
176
177 return util.setMap( {
178 map: extensions,
179 keys: [ type, name ],
180 value: ext
181 } );
182}
183
184function getExtension( type, name ){
185 return util.getMap( {
186 map: extensions,
187 keys: [ type, name ]
188 } );
189}
190
191function setModule( type, name, moduleType, moduleName, registrant ){
192 return util.setMap( {
193 map: modules,
194 keys: [ type, name, moduleType, moduleName ],
195 value: registrant
196 } );
197}
198
199function getModule( type, name, moduleType, moduleName ){
200 return util.getMap( {
201 map: modules,
202 keys: [ type, name, moduleType, moduleName ]
203 } );
204}
205
206let extension = function(){
207 // e.g. extension('renderer', 'svg')
208 if( arguments.length === 2 ){
209 return getExtension.apply( null, arguments );
210 }
211
212 // e.g. extension('renderer', 'svg', { ... })
213 else if( arguments.length === 3 ){
214 return setExtension.apply( null, arguments );
215 }
216
217 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
218 else if( arguments.length === 4 ){
219 return getModule.apply( null, arguments );
220 }
221
222 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
223 else if( arguments.length === 5 ){
224 return setModule.apply( null, arguments );
225 }
226
227 else {
228 util.error( 'Invalid extension access syntax' );
229 }
230
231};
232
233// allows a core instance to access extensions internally
234Core.prototype.extension = extension;
235
236// included extensions
237incExts.forEach( function( group ){
238 group.extensions.forEach( function( ext ){
239 setExtension( group.type, ext.name, ext.impl );
240 } );
241} );
242
243export default extension;