text: import * as util from '../util';
  import * as is from '../is';
  import Set from '../set';
  
  // represents a node or an edge
  let Element = function( cy, params, restore = true ){
    if( cy === undefined || params === undefined || !is.core( cy ) ){
      util.error( 'An element must have a core reference and parameters set' );
      return;
    }
  
    let group = params.group;
  
    // try to automatically infer the group if unspecified
    if( group == null ){
      if( params.data && params.data.source != null && params.data.target != null ){
        group = 'edges';
      } else {
        group = 'nodes';
      }
    }
  
    // validate group
    if( group !== 'nodes' && group !== 'edges' ){
      util.error( 'An element must be of type `nodes` or `edges`; you specified `' + group + '`' );
      return;
    }
  
    // make the element array-like, just like a collection
    this.length = 1;
    this[0] = this;
  
    // NOTE: when something is added here, add also to ele.json()
    let _p = this._private = {
      cy: cy,
      single: true, // indicates this is an element
      data: params.data || {}, // data object
      position: params.position || { x: 0, y: 0 }, // (x, y) position pair
      autoWidth: undefined, // width and height of nodes calculated by the renderer when set to special 'auto' value
      autoHeight: undefined,
      autoPadding: undefined,
      compoundBoundsClean: false, // whether the compound dimensions need to be recalculated the next time dimensions are read
      listeners: [], // array of bound listeners
      group: group, // string; 'nodes' or 'edges'
      style: {}, // properties as set by the style
      rstyle: {}, // properties for style sent from the renderer to the core
      styleCxts: [], // applied style contexts from the styler
      styleKeys: {}, // per-group keys of style property values
      removed: true, // whether it's inside the vis; true if removed (set true here since we call restore)
      selected: params.selected ? true : false, // whether it's selected
      selectable: params.selectable === undefined ? true : ( params.selectable ? true : false ), // whether it's selectable
      locked: params.locked ? true : false, // whether the element is locked (cannot be moved)
      grabbed: false, // whether the element is grabbed by the mouse; renderer sets this privately
      grabbable: params.grabbable === undefined ? true : ( params.grabbable ? true : false ), // whether the element can be grabbed
      pannable: params.pannable === undefined ? (group === 'edges' ? true : false) : ( params.pannable ? true : false ), // whether the element has passthrough panning enabled
      active: false, // whether the element is active from user interaction
      classes: new Set(), // map ( className => true )
      animation: { // object for currently-running animations
        current: [],
        queue: []
      },
      rscratch: {}, // object in which the renderer can store information
      scratch: params.scratch || {}, // scratch objects
      edges: [], // array of connected edges
      children: [], // array of children
      parent: null, // parent ref
      traversalCache: {}, // cache of output of traversal functions
      backgrounding: false, // whether background images are loading
      bbCache: null, // cache of the current bounding box
      bbCacheShift: { x: 0, y: 0 }, // shift applied to cached bb to be applied on next get
      bodyBounds: null, // bounds cache of element body, w/o overlay
      overlayBounds: null, // bounds cache of element body, including overlay
      labelBounds: { // bounds cache of labels
        all: null,
        source: null,
        target: null,
        main: null
      },
      arrowBounds: { // bounds cache of edge arrows
        source: null,
        target: null,
        'mid-source': null,
        'mid-target': null
      }
    };
  
    if( _p.position.x == null ){ _p.position.x = 0; }
    if( _p.position.y == null ){ _p.position.y = 0; }
  
    // renderedPosition overrides if specified
    if( params.renderedPosition ){
      let rpos = params.renderedPosition;
      let pan = cy.pan();
      let zoom = cy.zoom();
  
      _p.position = {
        x: (rpos.x - pan.x) / zoom,
        y: (rpos.y - pan.y) / zoom
      };
    }
  
    let classes = [];
    if( is.array( params.classes ) ){
      classes = params.classes;
    } else if( is.string( params.classes ) ){
      classes = params.classes.split( /\s+/ );
    }
    for( let i = 0, l = classes.length; i < l; i++ ){
      let cls = classes[ i ];
      if( !cls || cls === '' ){ continue; }
  
      _p.classes.add(cls);
    }
  
    this.createEmitter();
  
    let bypass = params.style || params.css;
    if( bypass ){
      util.warn('Setting a `style` bypass at element creation is deprecated');
  
      this.style(bypass);
    }
  
    if( restore === undefined || restore ){
      this.restore();
    }
  
  };
  
  export default Element;
  