CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-cytoscape

Graph theory library for analysis and visualisation of network data with interactive rendering capabilities

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

extensions.mddocs/

Extensions

Plugin architecture for extending cytoscape functionality with custom layouts, renderers, utility functions, and third-party integrations. Supports both core extensions and collection extensions.

Capabilities

Extension Registration

Register extensions to add new functionality to cytoscape instances and collections.

/**
 * Register extension using cytoscape.use()
 * @param extension - Extension object or function
 */
cytoscape.use(extension: any): void;

/**
 * Register extension by type and name
 * @param type - Extension type ("core", "collection", "layout", "renderer")
 * @param name - Extension name
 * @param extension - Extension implementation
 */
cytoscape(type: string, name: string, extension: any): void;

/**
 * Get registered extension
 * @param type - Extension type
 * @param name - Extension name
 * @returns Extension instance or undefined
 */
cytoscape(type: string, name: string): any;

Usage Examples:

// Register extension using .use()
import myExtension from 'my-cytoscape-extension';
cytoscape.use(myExtension);

// Register extension by type
cytoscape('layout', 'myLayout', MyLayoutExtension);
cytoscape('core', 'myUtility', MyCoreExtension);

// Get registered extension
const layoutExt = cytoscape('layout', 'myLayout');

Core Extensions

Extend the Core prototype with new methods and properties.

interface CoreExtension {
  /**
   * Extension function that receives core instance
   * @param core - Cytoscape core instance
   */
  (core: Core): void;
}

/**
 * Core extension registration
 */
cytoscape('core', 'extensionName', function(core: Core) {
  // Add methods to core prototype
  core.myCustomMethod = function() {
    // Custom functionality
    return this; // Return core for chaining
  };
  
  // Add properties
  core.myProperty = 'value';
});

// Alternative syntax using extension object
const coreExtension = {
  myMethod: function() {
    // 'this' refers to the core instance
    return this.nodes().length;
  },
  
  myUtility: function(param) {
    // Custom utility function
    console.log('Core utility called with:', param);
    return this;
  }
};

cytoscape('core', 'myExtension', coreExtension);

Usage Examples:

// Define core extension
cytoscape('core', 'analytics', function() {
  return {
    getStats: function() {
      return {
        nodeCount: this.nodes().length,
        edgeCount: this.edges().length,
        selectedCount: this.$(':selected').length
      };
    },
    
    highlightPath: function(source, target) {
      const dijkstra = this.elements().dijkstra({
        root: source,
        directed: false
      });
      
      if (dijkstra.hasPathTo(target)) {
        const path = dijkstra.pathTo(target);
        path.addClass('highlighted-path');
        return path;
      }
      
      return this.collection();
    }
  };
});

// Use core extension
const cy = cytoscape({ /* options */ });
const stats = cy.analytics().getStats();
console.log('Graph stats:', stats);

const path = cy.analytics().highlightPath('#start', '#end');

Collection Extensions

Extend the Collection prototype with new methods for element manipulation.

interface CollectionExtension {
  /**
   * Extension function that receives collection instance
   * @param collection - Element collection
   */
  (collection: Collection): void;
}

/**
 * Collection extension registration
 */
cytoscape('collection', 'extensionName', function(collection: Collection) {
  // Add methods to collection prototype
  collection.myCustomMethod = function() {
    // Custom functionality on elements
    return this; // Return collection for chaining
  };
});

// Alternative object syntax
const collectionExtension = {
  centerOnViewport: function() {
    const cy = this.cy();
    const bb = this.boundingBox();
    
    cy.pan({
      x: (cy.width() / 2) - (bb.x1 + bb.w / 2),
      y: (cy.height() / 2) - (bb.y1 + bb.h / 2)
    });
    
    return this;
  },
  
  fadeOut: function(duration = 1000) {
    return this.animate({
      'opacity': 0
    }, duration);
  },
  
  fadeIn: function(duration = 1000) {
    return this.animate({
      'opacity': 1
    }, duration);
  }
};

cytoscape('collection', 'animations', collectionExtension);

Usage Examples:

// Define collection extension
cytoscape('collection', 'utilities', {
  getNeighborhoodData: function() {
    const data = [];
    this.forEach(node => {
      if (node.isNode()) {
        data.push({
          id: node.id(),
          degree: node.degree(),
          neighbors: node.neighbors().length,
          data: node.data()
        });
      }
    });
    return data;
  },
  
  applyTemplate: function(template) {
    this.style(template.style);
    if (template.classes) {
      this.addClass(template.classes);
    }
    return this;
  },
  
  savePositions: function() {
    const positions = {};
    this.nodes().forEach(node => {
      positions[node.id()] = node.position();
    });
    return positions;
  },
  
  restorePositions: function(positions) {
    this.nodes().forEach(node => {
      const pos = positions[node.id()];
      if (pos) {
        node.position(pos);
      }
    });
    return this;
  }
});

// Use collection extension
const selectedNodes = cy.nodes(':selected');
const neighborhoodData = selectedNodes.utilities().getNeighborhoodData();

const positions = cy.nodes().utilities().savePositions();
// ... later ...
cy.nodes().utilities().restorePositions(positions);

Layout Extensions

Create custom layout algorithms for automatic node positioning.

interface LayoutExtension {
  /**
   * Layout name
   */
  name: string;
  
  /**
   * Layout run function
   * @param options - Layout options
   * @returns Layout instance
   */
  run(options?: any): Layout;
  
  /**
   * Layout stop function
   * @returns Layout instance
   */
  stop?(): Layout;
}

/**
 * Layout extension registration
 */
cytoscape('layout', 'layoutName', function(options) {
  const layout = this;
  
  // Layout implementation
  layout.run = function() {
    const nodes = layout.options.eles.nodes();
    const edges = layout.options.eles.edges();
    
    // Custom positioning logic
    nodes.forEach((node, i) => {
      node.position({
        x: i * 100,
        y: Math.sin(i * 0.5) * 100
      });
    });
    
    // Emit layout events
    layout.emit('layoutready');
    layout.emit('layoutstop');
    
    return layout;
  };
  
  layout.stop = function() {
    // Stop layout if running
    return layout;
  };
  
  return layout;
});

Usage Examples:

// Define custom spiral layout
cytoscape('layout', 'spiral', function(options) {
  const defaults = {
    radius: 100,
    turns: 3,
    angleStep: 0.1
  };
  
  const layout = this;
  const opts = Object.assign({}, defaults, options);
  
  layout.run = function() {
    const nodes = layout.options.eles.nodes();
    let angle = 0;
    
    nodes.forEach((node, i) => {
      const r = (opts.radius * angle) / (2 * Math.PI * opts.turns);
      
      node.position({
        x: r * Math.cos(angle),
        y: r * Math.sin(angle)
      });
      
      angle += opts.angleStep;
    });
    
    if (opts.fit) {
      layout.cy.fit(nodes, opts.padding);
    }
    
    layout.emit('layoutready');
    layout.emit('layoutstop');
    
    return layout;
  };
  
  return layout;
});

// Use custom layout
cy.layout({
  name: 'spiral',
  radius: 150,
  turns: 4,
  angleStep: 0.2,
  fit: true,
  padding: 50
}).run();

Renderer Extensions

Create custom rendering backends for specialized visualization needs.

interface RendererExtension {
  /**
   * Renderer name
   */
  name: string;
  
  /**
   * Initialize renderer
   * @param cy - Cytoscape core instance
   * @param options - Renderer options
   */
  initialize(cy: Core, options: any): void;
  
  /**
   * Destroy renderer and clean up
   */
  destroy?(): void;
}

/**
 * Renderer extension registration
 */
cytoscape('renderer', 'rendererName', RendererImplementation);

Usage Examples:

// Register custom canvas renderer
cytoscape('renderer', 'mycanvas', MyCanvasRenderer);

// Use custom renderer
const cy = cytoscape({
  container: document.getElementById('cy'),
  renderer: {
    name: 'mycanvas',
    options: {
      // Renderer-specific options
    }
  },
  elements: [/* ... */]
});

Extension Development Patterns

Best practices and common patterns for extension development.

/**
 * Extension factory pattern
 */
function createExtension(name, implementation) {
  return function(cytoscape) {
    if (!cytoscape) {
      return;
    }
    
    cytoscape('core', name, implementation.core || {});
    cytoscape('collection', name, implementation.collection || {});
    
    if (implementation.layout) {
      cytoscape('layout', name, implementation.layout);
    }
  };
}

/**
 * Extension with options pattern
 */
function createConfigurableExtension(defaultOptions) {
  return function(cytoscape) {
    cytoscape('core', 'myExt', function(options) {
      const opts = Object.assign({}, defaultOptions, options);
      
      return {
        doSomething: function() {
          // Use opts for configuration
          return this;
        }
      };
    });
  };
}

/**
 * Extension with initialization pattern
 */
function createInitializableExtension() {
  let initialized = false;
  
  return function(cytoscape) {
    cytoscape('core', 'myExt', function() {
      if (!initialized) {
        // One-time initialization
        this.on('ready', function() {
          console.log('Extension initialized');
        });
        initialized = true;
      }
      
      return {
        // Extension methods
      };
    });
  };
}

Third-Party Extension Integration

Common patterns for using popular cytoscape extensions.

/**
 * Layout extensions
 */
import cola from 'cytoscape-cola';
import dagre from 'cytoscape-dagre';
import klay from 'cytoscape-klay';
import euler from 'cytoscape-euler';

cytoscape.use(cola);
cytoscape.use(dagre);
cytoscape.use(klay);
cytoscape.use(euler);

// Use third-party layouts
cy.layout({
  name: 'cola',
  nodeSpacing: 5,
  edgeLengthRate: 45,
  animate: true
}).run();

cy.layout({
  name: 'dagre',
  nodeSep: 10,
  rankSep: 10,
  rankDir: 'TB'
}).run();

/**
 * Interaction extensions
 */
import edgehandles from 'cytoscape-edgehandles';
import nodeResize from 'cytoscape-node-resize';
import contextMenus from 'cytoscape-context-menus';

cytoscape.use(edgehandles);
cytoscape.use(nodeResize);
cytoscape.use(contextMenus);

// Configure extensions
cy.edgehandles({
  canConnect: function(sourceNode, targetNode) {
    return !sourceNode.same(targetNode);
  },
  edgeParams: function(sourceNode, targetNode) {
    return {};
  }
});

cy.contextMenus({
  menuItems: [
    {
      id: 'remove',
      content: 'Remove',
      tooltipText: 'Remove element',
      selector: 'node, edge',
      onClickFunction: function(event) {
        event.target.remove();
      }
    }
  ]
});

/**
 * Export extensions
 */
import panzoom from 'cytoscape-panzoom';
import navigator from 'cytoscape-navigator';

cytoscape.use(panzoom);
cytoscape.use(navigator);

// Add UI controls
cy.panzoom({
  zoomFactor: 0.05,
  zoomDelay: 45,
  minZoom: 0.1,
  maxZoom: 10
});

cy.navigator({
  container: document.getElementById('nav'),
  viewLiveFramerate: 0,
  thumbnailEventFramerate: 30
});

Extension Testing and Debugging

Tools and techniques for extension development.

/**
 * Extension testing pattern
 */
function testExtension() {
  // Create test instance
  const cy = cytoscape({
    elements: [
      { data: { id: 'a' } },
      { data: { id: 'b' } },
      { data: { id: 'ab', source: 'a', target: 'b' } }
    ]
  });
  
  // Test extension methods
  console.assert(typeof cy.myExtension === 'function', 'Extension not registered');
  console.assert(cy.nodes().myMethod, 'Collection method not available');
  
  // Test functionality
  const result = cy.myExtension().doSomething();
  console.assert(result === cy, 'Method should return core for chaining');
}

/**
 * Extension debugging utilities
 */
const debugExtension = {
  logMethodCalls: function(target, methods) {
    methods.forEach(method => {
      const original = target[method];
      target[method] = function(...args) {
        console.log(`Called ${method} with:`, args);
        return original.apply(this, args);
      };
    });
  },
  
  validateExtension: function(extension, requiredMethods) {
    requiredMethods.forEach(method => {
      if (typeof extension[method] !== 'function') {
        console.warn(`Extension missing required method: ${method}`);
      }
    });
  }
};

Extension Distribution

Package and distribute extensions for the community.

/**
 * Extension module pattern
 */
(function(factory) {
  // UMD pattern for broad compatibility
  if (typeof module !== 'undefined' && module.exports) {
    // CommonJS
    module.exports = factory(require('cytoscape'));
  } else if (typeof define === 'function' && define.amd) {
    // AMD
    define(['cytoscape'], factory);
  } else {
    // Global
    if (typeof cytoscape !== 'undefined') {
      factory(cytoscape);
    }
  }
})(function(cytoscape) {
  'use strict';
  
  if (!cytoscape) {
    return;
  }
  
  // Extension implementation
  const implementation = {
    // Extension code here
  };
  
  // Register extension
  cytoscape('core', 'myExtension', implementation);
  
  // Return for chaining
  return cytoscape;
});

/**
 * Package.json for extension
 */
{
  "name": "cytoscape-my-extension",
  "version": "1.0.0",
  "description": "My custom cytoscape extension",
  "main": "index.js",
  "peerDependencies": {
    "cytoscape": "^3.0.0"
  },
  "keywords": [
    "cytoscape",
    "extension",
    "graph",
    "visualization"
  ]
}

Install with Tessl CLI

npx tessl i tessl/npm-cytoscape

docs

core-management.md

element-collections.md

event-system.md

extensions.md

graph-algorithms.md

index.md

layout-system.md

styling-system.md

tile.json