<commit_before>//////////////////////////////////////////////////////////////////////////////
/**
 * @module ogs.ui
 */
//////////////////////////////////////////////////////////////////////////////

/*jslint devel: true, forin: true, newcap: true, plusplus: true,
 white: true, indent: 2*/
/*global geoModule, ogs, inherit, $*/

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
  var obj3 = {};
  for (var attrName in obj1) { obj3[attrName] = obj1[attrName]; }
  for (var attrName in obj2) { obj3[attrName] = obj2[attrName]; }
  return obj3;
}

function merge_options_overwrite(obj1, obj2) {
  for (var attrName in obj2) { obj1[attrName] = obj2[attrName]; }
  return obj1;
}

function defaultValue(param, _default) {
  return typeof param !== 'undefined' ? param: _default;
}

function createIdCounter(initialId) {
  initialId = defaultValue(initialId, -1);
  return function() { initialId += 1; return initialId; };
}

var nextWorkflowId = createIdCounter();
var nextModuleId = createIdCounter();
var nextLocationId = createIdCounter();
var nextConnectionId = createIdCounter();
var nextPortId = createIdCounter();


function blankWorkflow(name, version, connections, modules, vistrail_id, id) {
  name = defaultValue(name, 'untitled');
  version = defaultValue(version, '1.0.2');
  connections = defaultValue(connections, []);
  modules = defaultValue(modules, []);
  vistrail_id = defaultValue(vistrail_id, "");
  id = defaultValue(id, nextWorkflowId());
  return {
    "workflow": {
      "@name": name,
      "@version": version,
      "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://www.vistrails.org/workflow.xsd",
      "connection": connections,
      "module": modules,
      "@vistrail_id": vistrail_id,
      "@id": id
    }
  };
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Workflow options object specification
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.workflowOptions = function() {
  "use strict";
  // Check against no use of new()
  if (!(this instanceof uiModule.workflowOptions)) {
    return new uiModule.workflowOptions();
  }

  this.data = blankWorkflow();
  this.modules = {};
  this.connections = {};
  this.style = climatePipesStyle;
  this.translated = {x:0, y:0};
  this.moduleClass = uiModule.inputModule;

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Base class for all workflow types ogs.ui.workflow.
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.workflow = function(options) {
  "use strict";
  this.events = {
    "moduleAdded" : "moduleAdded",
    "connectionAdded" : "connectionAdded"
  };

  if (!(this instanceof uiModule.workflow)) {
    return new uiModule.workflow(options);
  }
  vglModule.object.call(this);

  options = typeof options !== 'undefined' ? options : {};
  options = merge_options(uiModule.workflowOptions(), options);

  /** @private */
  var m_that = this,
    m_data = options.data,
    m_modules = options.modules,
    m_connections = options.connections,
    m_style = options.style,
    m_translated = options.translated,
    m_moduleClass = options.moduleClass,
    m_visible = false;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Generate modules from the workflow JSON data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.generateModulesFromData = function() {
    var i = 0,
      maxId = 0,
      nextId = nextModuleId(),
      modules = m_data.workflow.module;

    for(; i < modules.length; i++) {
      var mid = parseInt(modules[i]['@id']);
      m_modules[mid] = m_moduleClass(null, modules[i]);

      if(mid > maxId) {
        maxId = mid;
      }

      while (maxId > nextId) {
        nextId = nextModuleId();
      }
    }
  };

  this.addNewModule = function(JSONdata, x, y) {
    var moduleInfo = JSON.parse(JSONdata),
      module = {
        "@name": moduleInfo['@name'],
        "@package": moduleInfo['@package'],
        "@version": moduleInfo['@packageVersion'],
        "@namespace": moduleInfo['@namespace'],
        "@cache": "1",
        "location": {
          "@x": parseFloat(x),
          "@y": -parseFloat(y),
          "@id": nextLocationId()
        },
        "@id": nextModuleId()
      };

    m_modules[module['@id']] = m_moduleClass(null, module);
    this.data().workflow.module.push(module);
  };

  this.addConnection = function(sourceModule, sourcePort, targetModule,
                                targetPort) {
    if(sourcePort.data()['@type'] == targetPort.data()['@type']) {
      debug("Must connect output to input");
      return;
    } else if (sourceModule == targetModule) {
      debug("Cannot make connection between ports on same module.")
    }
    var connection = {
        "@id": nextConnectionId(),
        "port": [
          {
            "@moduleName": targetModule.getData()['@name'],
            "@name": targetPort.data()['@name'],
            "@signature": targetPort.data()['@sigstring'],
            "@id": nextPortId(),
            "@type": targetPort.data()['@type'],
            "@moduleId": targetModule.getData()['@id']
          }, {
            "@moduleName": sourceModule.getData()['@name'],
            "@name": sourcePort.data()['@name'],
            "@signature": sourcePort.data()['@sigstring'],
            "@id": nextPortId(),
            "@type": sourcePort.data()['@type'],
            "@moduleId": sourceModule.getData()['@id']
          }
        ]
      },
      options = {vertical: m_moduleClass == uiModule.module};
    m_connections[connection['@id']] = uiModule.connection(options, connection);
    this.data().workflow.connection.push(connection);

  };

  this.generateConnectionsFromData = function() {
    var i = 0,
      maxId = 0,
      nextId = nextConnectionId(),
      connections = m_data.workflow.connection,
      options = {vertical: m_moduleClass == uiModule.module};

    for(; i < connections.length; i++) {
      var cid = parseInt(connections[i]['@id']);
      m_connections[cid] = uiModule.connection(options, connections[i]);

      if(cid > maxId) {
        maxId = cid;
      }

      while (maxId > nextId) {
        nextId = nextConnectionId();
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the workflow onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx) {
    var key;

    ctx.fillStyle = style.fill;
    ctx.fillRect(-m_translated.x, -m_translated.y, ctx.canvas.width, ctx.canvas.height);

    for(key in m_modules) {
      if(m_modules.hasOwnProperty(key)) {
        m_modules[key].draw(ctx, m_style);
      }
    }

    for(key in m_connections) {
      if(m_connections.hasOwnProperty(key)) {
        m_connections[key].draw(ctx, m_style);
      }
    }
  };

  this.resize = function() {
    if(m_visible) {
      var canvasContainer = $('#canvasContainer')[0],
        canvas =  $('#workspace')[0],
        rect = canvasContainer.getBoundingClientRect(),
        translated = this.translated(),
        ctx;
      canvas.width = rect.width - 20;
      canvas.height = rect.height - 20;
      ctx = canvas.getContext('2d');
      ctx.translate(translated.x, translated.y);
      this.draw(ctx);
    }
  };

  this.setVisible = function(visible) {
    m_visible = visible;
  };

  this.visible = function() {
    return m_visible;
  };

  this.translated = function() {
    return m_translated;
  };

  this.translate = function(ctx, x, y) {
    m_translated.x += x;
    m_translated.y += y;
    ctx.translate(x,y);
  };

  this.show = function() {
    this.setVisible(true);
    this.resize();
  };

  this.modules = function() {
    return m_modules;
  };

  this.data = function() {
    return m_data;
  };

  this.updateElementPositions = function() {
    for(var key in m_modules) {
      m_modules[key].updateElementPositions();
    }
  };

  if(options.data.hasOwnProperty('workflow')) {
    this.generateModulesFromData();
    this.generateConnectionsFromData();
  }

  return this;
};

inherit(uiModule.workflow, vglModule.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class module
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.module}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.module = function(options, data) {
  "use strict";
  if (!(this instanceof uiModule.module)) {
    return new uiModule.module(options, data);
  }
  vglModule.object.call(this);

  /** @private */
  var m_that = this,
    m_data = data,
    m_registry = moduleRegistry[data['@package']][data['@name']],
    m_metrics,
    m_ports = m_registry.portSpec,
    m_inPorts = {},
    m_outPorts = {},
    m_inPortCount = 0,
    m_outPortCount = 0,
    i = 0;

  //ensure location values are floating point numbers
  data.location['@x'] = parseFloat(data.location['@x']);
  data.location['@y'] = parseFloat(data.location['@y']);

  if(!(m_ports instanceof Array)) {
    m_ports = [m_ports];
  }

  if(!this.hasOwnProperty('inputPortClass')) {
    this.inputPortClass = uiModule.port;
  }

  if(!this.hasOwnProperty('outputPortClass')) {
    this.outputPortClass = uiModule.port;
  }

  for(; i < m_ports.length; i++) {
    if(m_ports[i]['@type'] != 'output') {
      m_inPorts[m_ports[i]['@name']] = this.inputPortClass(null, m_ports[i]);
      m_inPortCount++;
    } else {
      m_outPorts[m_ports[i]['@name']] = this.outputPortClass(null, m_ports[i]);
      m_outPortCount++;
    }
  }

  this.inPortCount = function() {
    return m_inPortCount;
  };

  this.outPortCount = function() {
    return m_outPortCount;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Recompute drawing metrics for this module
   *
   * @param {CanvasRenderingContext2D} ctx
   * @param {object} style
   */
    ////////////////////////////////////////////////////////////////////////////
  this.recomputeMetrics = function(ctx, style) {
    var portWidth = style.module.port.width,
      totalPortWidth = portWidth + style.module.port.pad,
      inPortsWidth = m_inPortCount * totalPortWidth + style.module.text.xpad,
      outPortsWidth = m_outPortCount * totalPortWidth,
      fontMetrics = ctx.measureText(m_data['@name']),
      textWidth = fontMetrics.width + style.module.text.xpad * 2,
      moduleWidth = Math.max(inPortsWidth, outPortsWidth+style.module.text.xpad,
        textWidth, style.module.minWidth),
      textHeight = 12, //TODO: get real height based on text font
      moduleHeight = style.module.port.pad*4 + portWidth*2 +
        style.module.text.ypad*2 + textHeight,
      mx = Math.floor(m_data.location['@x'] - moduleWidth/2),
      my = -Math.floor(m_data.location['@y']),
      inPortX = mx + style.module.port.pad,
      outPortX = mx + moduleWidth - outPortsWidth,
      key;

    m_metrics = {
      mx: mx,
      my: my,
      totalPortWidth: totalPortWidth,
      inPortsWidth: inPortsWidth,
      fontMetrics: fontMetrics,
      textWidth: textWidth,
      moduleWidth: moduleWidth,
      textHeight: textHeight,
      moduleHeight: moduleHeight,
      inPortX: inPortX,
      inPortY: my + style.module.port.pad,
      outPortX: outPortX,
      outPortY: my + moduleHeight - style.module.port.pad - portWidth
    };

    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        m_inPorts[key].setPosition(inPortX, m_metrics.inPortY);
        inPortX += portWidth + style.module.port.pad;
      }
    }

    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        m_outPorts[key].setPosition(outPortX, m_metrics.outPortY);
        outPortX += portWidth + style.module.port.pad;
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get drawing metrics for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getMetrics = function() {
    return m_metrics;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get input ports for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getInPorts = function() {
    return m_inPorts;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get output ports for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getOutPorts = function() {
    return m_outPorts;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get registry for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getRegistry = function() {
    return m_registry;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get data for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getData = function() {
    return m_data;
  }


  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the module onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, style) {
    if(!m_metrics) {
      this.recomputeMetrics(ctx, style);
    }

    var portWidth = style.module.port.width,
      mx = m_metrics.mx,
      my = m_metrics.my,
      inPortX = m_metrics.inPortX,
      outPortX = m_metrics.outPortX,
      i,
      key;

    //draw rectangle
    ctx.fillStyle = style.module.fill;
    ctx.strokeStyle = style.module.stroke;
    ctx.fillRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);
    ctx.strokeRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);

    //draw ports
    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        m_inPorts[key].draw(ctx, portWidth);
      }
    }

    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        m_outPorts[key].draw(ctx, portWidth);
      }
    }

    //draw module name
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    ctx.fillText(
      m_data['@name'],
      mx + Math.floor((m_metrics.moduleWidth - m_metrics.fontMetrics.width)/2),
      my + m_metrics.textHeight + style.module.text.ypad
    );

  };

  this.contains = function(pos) {
    var metrics = this.getMetrics();
    return pos.x > metrics.mx && pos.x < metrics.mx + metrics.moduleWidth &&
      pos.y > metrics.my && pos.y < metrics.my + metrics.moduleHeight;
  };

  this.portByPos = function(pos) {
    var key;
    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        if (m_inPorts[key].contains(pos)) {
          return m_inPorts[key];
        }
      }
    }
    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        if (m_outPorts[key].contains(pos)) {
          return m_outPorts[key];
        }
      }
    }
    return null;
  };

  this.updateElementPositions = function() {};

  return this;
};

inherit(uiModule.module, vglModule.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class port
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.port}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.port = function(options, data) {
  "use strict"
  if (!(this instanceof uiModule.port)) {
    return new uiModule.port(options, data);
  }
  vglModule.object.call(this);

  /** @private */
  var m_that = this,
    m_data = data,
    m_x = 0,
    m_y = 0,
    m_width = style.module.port.width;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set x and y position
   *
   * @param {Number} x
   * @param {Number} y
   */
    ////////////////////////////////////////////////////////////////////////////
  this.setPosition = function(x, y) {
    m_x = x;
    m_y = y;
  };

  this.x = function() {
    return m_x;
  };

  this.y = function() {
    return m_y;
  };

  this.data = function() {
    return m_data;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the port onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, width) {
    ctx.fillStyle = style.module.port.fill;
    ctx.strokeStyle = style.module.port.stroke;
    ctx.fillRect(m_x, m_y, width, width);
    ctx.strokeRect(m_x, m_y, width, width);
  };

  this.getName = function() {
    return this.data()['@name'];
  };

  this.contains = function(pos) {
    if(pos.x >= m_x) {
      if(pos.y >= m_y) {
        if(pos.x <= m_x + m_width) {
          if(pos.y <= m_y + m_width) {
            return true;
          }
        }
      }
    }
    return false;
  };

  return this;
};

inherit(uiModule.port, vglModule.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class inputModule
 *
 * @class
 * @dec
 * @returns {uiModule.inputModule}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.inputModule = function(options, data) {
  "use strict";
  if (!(this instanceof uiModule.inputModule)) {
    return new uiModule.inputModule(options, data);
  }
  this.inputPortClass = uiModule.inputPort;
  this.outputPortClass = uiModule.outputPort;
  uiModule.module.call(this, options, data);

  /** @priave */
  var m_that = this,
    m_metrics,
    m_baseRecomputeMetrics = this.recomputeMetrics,
    m_baseGetMetrics = this.getMetrics;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Recompute drawing metrics for this module
   *
   * @param {CanvasRenderingContext2D} ctx
   * @param {object} style
   */
  ////////////////////////////////////////////////////////////////////////////

  this.recomputeMetrics = function(ctx, style) {
    m_baseRecomputeMetrics.call(this, ctx, style);
    var base_metrics = m_baseGetMetrics.call(this),
      new_metrics,
      maxInPortTextWidth = 0,
      maxOutPortTextWidth = 0,
      maxInPortTextKey,
      maxOutPortTextKey,
      portWidth = style.module.port.width,
      key;

    //find longest port name
    for(key in this.getInPorts()) {
      if(this.getInPorts().hasOwnProperty(key)) {
        if(this.getInPorts()[key].getName().length > maxInPortTextWidth) {
          maxInPortTextWidth = this.getInPorts()[key].getName().length;
          maxInPortTextKey = key;
        }
      }
    }

    for(key in this.getOutPorts()) {
      if(this.getOutPorts().hasOwnProperty(key)) {
        if(this.getOutPorts()[key].getName().length > maxOutPortTextWidth) {
          maxOutPortTextWidth = this.getOutPorts()[key].getName().length;
          maxOutPortTextKey = key;
        }
      }
    }

    var textHeight = 12, //TODO: get real height based on text font
      totalInPortHeight = style.module.port.inputHeight + textHeight +
        style.module.port.inputYPad + style.module.port.inpad,
      totalOutPortHeight = textHeight + style.module.port.outpad,
      inPortsHeight = this.inPortCount() * totalInPortHeight +
        style.module.text.xpad,
      outPortsHeight = this.outPortCount() * totalOutPortHeight;
    var
      fontMetrics = ctx.measureText(this.getData()['@name']),
      textWidth = fontMetrics.width + style.module.text.xpad * 2;
    var
      inPortFontMetrics = maxInPortTextKey ? ctx.measureText(
        this.getInPorts()[maxInPortTextKey]['@name']) : {width:0};
    var
      outPortFontMetrics = maxOutPortTextKey ? ctx.measureText(
        this.getOutPorts()[maxOutPortTextKey]['@name']) : {width:0},
      inPortsWidth = Math.max(inPortFontMetrics.width,
        style.module.port.inputWidth),
      outPortsWidth = outPortFontMetrics.width,
      moduleWidth = Math.max(
        inPortsWidth + outPortsWidth + portWidth*2 + style.module.port.pad*6,
        textWidth + style.module.text.xpad*2,
        style.module.minWidth
      ),
      moduleHeight = Math.max(
        inPortsHeight,
        outPortsHeight,
        style.module.minWidth
      ) + style.module.text.ypad*2 + textHeight,
      mx = Math.floor(this.getData().location['@x'] - moduleWidth/2),
      my = -Math.floor(this.getData().location['@y']),
      inPortY = my + style.module.port.pad + textHeight + portWidth*2,
      outPortY = my + moduleHeight - style.module.port.pad - portWidth;

    new_metrics = {
      mx: mx,
        my: my,
      fontMetrics: fontMetrics,
      textWidth: textWidth,
      moduleWidth: moduleWidth,
      textHeight: textHeight,
      moduleHeight: moduleHeight,
      inPortX: mx + style.module.port.pad,
      inPortY: inPortY,
      outPortX: mx + moduleWidth - style.module.port.pad - portWidth,
      outPortY: outPortY,
      outPortTextX: mx + inPortsWidth + portWidth + style.module.port.pad*4
    };

    merge_options_overwrite(base_metrics, new_metrics);
    m_metrics = base_metrics;

    //compute port positions
    for(key in this.getInPorts()) {
      if(this.getInPorts().hasOwnProperty(key)) {
        this.getInPorts()[key].setPosition(base_metrics.inPortX, inPortY);
        inPortY += style.module.port.inputHeight + textHeight +
          style.module.port.inputYPad + style.module.port.inpad;
      }
    }

    for(key in this.getOutPorts()) {
      if(this.getOutPorts().hasOwnProperty(key)) {
        this.getOutPorts()[key].setPosition(base_metrics.outPortX, outPortY);
        outPortY -= textHeight + style.module.port.outpad;
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get drawing metrics for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getMetrics = function() {
    return m_metrics;
  };


  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the module onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, style) {

    if(!m_metrics) {
      this.recomputeMetrics(ctx, style);
    }

    var portWidth = style.module.port.width,
      mx = m_metrics.mx,
      my = m_metrics.my,
      m_inPorts = this.getInPorts(),
      m_outPorts = this.getOutPorts(),
      key;

    //draw rectangle
    ctx.fillStyle = style.module.fill;
    ctx.strokeStyle = style.module.stroke;
    ctx.fillRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);
    ctx.strokeRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);

    //draw ports
    ctx.fillStyle = style.module.port.fill;
    ctx.strokeStyle = style.module.port.stroke;
    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        m_inPorts[key].draw(ctx, portWidth);
      }
    }

    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        m_outPorts[key].draw(ctx, portWidth);
      }
    }

    //draw module name
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    ctx.fillText(
      this.getData()['@name'],
      mx + Math.floor((m_metrics.moduleWidth - m_metrics.fontMetrics.width)/2),
      my + m_metrics.textHeight + style.module.text.ypad);

  };

  this.updateElementPositions = function() {
    var key,
      m_inPorts = this.getInPorts();
    for(key in m_inPorts) {
      m_inPorts[key].updateElementPosition(
        m_inPorts[key].x(),
        m_inPorts[key].y()
      );
    }
  };

  return this;
};

inherit(uiModule.inputModule, uiModule.module);


//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class outputPort
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.port}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.outputPort = function(options, data) {
  "use strict"
  if (!(this instanceof uiModule.outputPort)) {
    return new uiModule.outputPort(options, data);
  }
  uiModule.port.call(this, options, data);

  var m_name_width = 0;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the port onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, width) {
    ctx.fillStyle = style.module.port.fill;
    ctx.strokeStyle = style.module.port.stroke;
    ctx.fillRect(this.x(), this.y(), width, width);
    ctx.strokeRect(this.x(), this.y(), width, width);
    this.drawName(ctx, width);
  };

  this.drawName = function(ctx, width) {
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    if(m_name_width == 0) {
      var metrics = ctx.measureText(this.data()['@name']);
      m_name_width = metrics.width;
    }
    ctx.fillText(this.data()['@name'], this.x()-width-m_name_width,
      this.y()+width);
  };

  return this;
};

inherit(uiModule.outputPort, uiModule.port);


//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class inputPort
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.inputPort}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.inputPort = function(options, data) {
  "use strict"
  if (!(this instanceof uiModule.inputPort)) {
    return new uiModule.inputPort(options, data);
  }
  uiModule.outputPort.call(this, options, data);

  var m_input_elem,
    m_baseSetPosition = this.setPosition;

  this.drawName = function(ctx, width) {
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    ctx.fillText(this.data()['@name'], this.x() + width*2, this.y()+width);
  };

  function createElementFromType() {
    //TODO: support other types of input for color, dates, etc.
    m_input_elem = document.createElement('input');
    m_input_elem.type = 'text';
    $(m_input_elem).css({
      position: 'absolute'
    });
    $('#canvasContainer').append(m_input_elem);
  }

  this.setPosition = function(x,y) {
    m_baseSetPosition(x,y);
    this.updateElementPosition(x,y);
  };

  this.updateElementPosition = function(x,y) {
    var totalOffsetX = 0,
      totalOffsetY = 0,
      currentElement = $('#workspace')[0],
      translated = activeWorkflow.translated();

//    do{
//      totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
//      totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
//    }
//    while(currentElement = currentElement.offsetParent)

    $(m_input_elem).css({
      top: y + translated.y + style.module.port.width,
      left: x + translated.x
    });
  }

  createElementFromType();

  return this;
};

inherit(uiModule.inputPort, uiModule.outputPort);

uiModule.connectionOptions = function() {

  "use strict";
  // Check against no use of new()
  if (!(this instanceof uiModule.connectionOptions)) {
    return new uiModule.connectionOptions();
  }

  this.vertical = false;<commit_msg>Fixes some style issues<commit_after>//////////////////////////////////////////////////////////////////////////////
/**
 * @module ogs.ui
 */
//////////////////////////////////////////////////////////////////////////////

/*jslint devel: true, forin: true, newcap: true, plusplus: true,
 white: true, indent: 2*/
/*global geoModule, ogs, inherit, $*/

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
  var obj3 = {};
  for (var attrName in obj1) { obj3[attrName] = obj1[attrName]; }
  for (var attrName in obj2) { obj3[attrName] = obj2[attrName]; }
  return obj3;
}

function merge_options_overwrite(obj1, obj2) {
  for (var attrName in obj2) { obj1[attrName] = obj2[attrName]; }
  return obj1;
}

function defaultValue(param, _default) {
  return typeof param !== 'undefined' ? param: _default;
}

function createIdCounter(initialId) {
  initialId = defaultValue(initialId, -1);
  return function() { initialId += 1; return initialId; };
}

var nextWorkflowId = createIdCounter();
var nextModuleId = createIdCounter();
var nextLocationId = createIdCounter();
var nextConnectionId = createIdCounter();
var nextPortId = createIdCounter();


function blankWorkflow(name, version, connections, modules, vistrail_id, id) {
  name = defaultValue(name, 'untitled');
  version = defaultValue(version, '1.0.2');
  connections = defaultValue(connections, []);
  modules = defaultValue(modules, []);
  vistrail_id = defaultValue(vistrail_id, "");
  id = defaultValue(id, nextWorkflowId());
  return {
    "workflow": {
      "@name": name,
      "@version": version,
      "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://www.vistrails.org/workflow.xsd",
      "connection": connections,
      "module": modules,
      "@vistrail_id": vistrail_id,
      "@id": id
    }
  };
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Workflow options object specification
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.workflowOptions = function() {
  "use strict";
  // Check against no use of new()
  if (!(this instanceof uiModule.workflowOptions)) {
    return new uiModule.workflowOptions();
  }

  this.data = blankWorkflow();
  this.modules = {};
  this.connections = {};
  this.style = climatePipesStyle;
  this.translated = {x:0, y:0};
  this.moduleClass = uiModule.inputModule;

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Base class for all workflow types ogs.ui.workflow.
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.workflow = function(options) {
  "use strict";
  this.events = {
    "moduleAdded" : "moduleAdded",
    "connectionAdded" : "connectionAdded"
  };

  if (!(this instanceof uiModule.workflow)) {
    return new uiModule.workflow(options);
  }
  vglModule.object.call(this);

  options = typeof options !== 'undefined' ? options : {};
  options = merge_options(uiModule.workflowOptions(), options);

  /** @private */
  var m_that = this,
    m_data = options.data,
    m_modules = options.modules,
    m_connections = options.connections,
    m_style = options.style,
    m_translated = options.translated,
    m_moduleClass = options.moduleClass,
    m_visible = false;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Generate modules from the workflow JSON data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.generateModulesFromData = function() {
    var i = 0,
      maxId = 0,
      nextId = nextModuleId(),
      modules = m_data.workflow.module;

    for(; i < modules.length; i++) {
      var mid = parseInt(modules[i]['@id']);
      m_modules[mid] = m_moduleClass(null, modules[i]);

      if(mid > maxId) {
        maxId = mid;
      }

      while (maxId > nextId) {
        nextId = nextModuleId();
      }
    }
  };

  this.addNewModule = function(JSONdata, x, y) {
    var moduleInfo = JSON.parse(JSONdata),
      module = {
        "@name": moduleInfo['@name'],
        "@package": moduleInfo['@package'],
        "@version": moduleInfo['@packageVersion'],
        "@namespace": moduleInfo['@namespace'],
        "@cache": "1",
        "location": {
          "@x": parseFloat(x),
          "@y": -parseFloat(y),
          "@id": nextLocationId()
        },
        "@id": nextModuleId()
      };

    m_modules[module['@id']] = m_moduleClass(null, module);
    this.data().workflow.module.push(module);
  };

  this.addConnection = function(sourceModule, sourcePort, targetModule,
                                targetPort) {
    if(sourcePort.data()['@type'] == targetPort.data()['@type']) {
      debug("Must connect output to input");
      return;
    } else if (sourceModule == targetModule) {
      debug("Cannot make connection between ports on same module.")
    }
    var connection = {
        "@id": nextConnectionId(),
        "port": [
          {
            "@moduleName": targetModule.getData()['@name'],
            "@name": targetPort.data()['@name'],
            "@signature": targetPort.data()['@sigstring'],
            "@id": nextPortId(),
            "@type": targetPort.data()['@type'],
            "@moduleId": targetModule.getData()['@id']
          }, {
            "@moduleName": sourceModule.getData()['@name'],
            "@name": sourcePort.data()['@name'],
            "@signature": sourcePort.data()['@sigstring'],
            "@id": nextPortId(),
            "@type": sourcePort.data()['@type'],
            "@moduleId": sourceModule.getData()['@id']
          }
        ]
      },
      options = {vertical: m_moduleClass == uiModule.module};
    m_connections[connection['@id']] = uiModule.connection(options, connection);
    this.data().workflow.connection.push(connection);

  };

  this.generateConnectionsFromData = function() {
    var i = 0,
      maxId = 0,
      nextId = nextConnectionId(),
      connections = m_data.workflow.connection,
      options = {vertical: m_moduleClass == uiModule.module};

    for(; i < connections.length; i++) {
      var cid = parseInt(connections[i]['@id']);
      m_connections[cid] = uiModule.connection(options, connections[i]);

      if(cid > maxId) {
        maxId = cid;
      }

      while (maxId > nextId) {
        nextId = nextConnectionId();
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the workflow onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx) {
    var key;

    ctx.fillStyle = style.fill;
    ctx.fillRect(-m_translated.x, -m_translated.y, ctx.canvas.width, ctx.canvas.height);

    for(key in m_modules) {
      if(m_modules.hasOwnProperty(key)) {
        m_modules[key].draw(ctx, m_style);
      }
    }

    for(key in m_connections) {
      if(m_connections.hasOwnProperty(key)) {
        m_connections[key].draw(ctx, m_style);
      }
    }
  };

  this.resize = function() {
    if(m_visible) {
      var canvasContainer = $('#canvasContainer')[0],
        canvas =  $('#workspace')[0],
        rect = canvasContainer.getBoundingClientRect(),
        translated = this.translated(),
        ctx;
      canvas.width = rect.width - 20;
      canvas.height = rect.height - 20;
      ctx = canvas.getContext('2d');
      ctx.translate(translated.x, translated.y);
      this.draw(ctx);
    }
  };

  this.setVisible = function(visible) {
    m_visible = visible;
  };

  this.visible = function() {
    return m_visible;
  };

  this.translated = function() {
    return m_translated;
  };

  this.translate = function(ctx, x, y) {
    m_translated.x += x;
    m_translated.y += y;
    ctx.translate(x,y);
  };

  this.show = function() {
    this.setVisible(true);
    this.resize();
  };

  this.modules = function() {
    return m_modules;
  };

  this.data = function() {
    return m_data;
  };

  this.updateElementPositions = function() {
    for(var key in m_modules) {
      m_modules[key].updateElementPositions();
    }
  };

  if(options.data.hasOwnProperty('workflow')) {
    this.generateModulesFromData();
    this.generateConnectionsFromData();
  }

  return this;
};

inherit(uiModule.workflow, vglModule.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class module
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.module}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.module = function(options, data) {
  "use strict";
  if (!(this instanceof uiModule.module)) {
    return new uiModule.module(options, data);
  }
  vglModule.object.call(this);

  /** @private */
  var m_that = this,
    m_data = data,
    m_registry = moduleRegistry[data['@package']][data['@name']],
    m_metrics,
    m_ports = m_registry.portSpec,
    m_inPorts = {},
    m_outPorts = {},
    m_inPortCount = 0,
    m_outPortCount = 0,
    i = 0;

  //ensure location values are floating point numbers
  data.location['@x'] = parseFloat(data.location['@x']);
  data.location['@y'] = parseFloat(data.location['@y']);

  if(!(m_ports instanceof Array)) {
    m_ports = [m_ports];
  }

  if(!this.hasOwnProperty('inputPortClass')) {
    this.inputPortClass = uiModule.port;
  }

  if(!this.hasOwnProperty('outputPortClass')) {
    this.outputPortClass = uiModule.port;
  }

  for(; i < m_ports.length; i++) {
    if(m_ports[i]['@type'] != 'output') {
      m_inPorts[m_ports[i]['@name']] = this.inputPortClass(null, m_ports[i]);
      m_inPortCount++;
    } else {
      m_outPorts[m_ports[i]['@name']] = this.outputPortClass(null, m_ports[i]);
      m_outPortCount++;
    }
  }

  this.inPortCount = function() {
    return m_inPortCount;
  };

  this.outPortCount = function() {
    return m_outPortCount;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Recompute drawing metrics for this module
   *
   * @param {CanvasRenderingContext2D} ctx
   * @param {object} style
   */
    ////////////////////////////////////////////////////////////////////////////
  this.recomputeMetrics = function(ctx, style) {
    var portWidth = style.module.port.width,
      totalPortWidth = portWidth + style.module.port.pad,
      inPortsWidth = m_inPortCount * totalPortWidth + style.module.text.xpad,
      outPortsWidth = m_outPortCount * totalPortWidth,
      fontMetrics = ctx.measureText(m_data['@name']),
      textWidth = fontMetrics.width + style.module.text.xpad * 2,
      moduleWidth = Math.max(inPortsWidth, outPortsWidth+style.module.text.xpad,
        textWidth, style.module.minWidth),
      textHeight = 12, //TODO: get real height based on text font
      moduleHeight = style.module.port.pad*4 + portWidth*2 +
        style.module.text.ypad*2 + textHeight,
      mx = Math.floor(m_data.location['@x'] - moduleWidth/2),
      my = -Math.floor(m_data.location['@y']),
      inPortX = mx + style.module.port.pad,
      outPortX = mx + moduleWidth - outPortsWidth,
      key;

    m_metrics = {
      mx: mx,
      my: my,
      totalPortWidth: totalPortWidth,
      inPortsWidth: inPortsWidth,
      fontMetrics: fontMetrics,
      textWidth: textWidth,
      moduleWidth: moduleWidth,
      textHeight: textHeight,
      moduleHeight: moduleHeight,
      inPortX: inPortX,
      inPortY: my + style.module.port.pad,
      outPortX: outPortX,
      outPortY: my + moduleHeight - style.module.port.pad - portWidth
    };

    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        m_inPorts[key].setPosition(inPortX, m_metrics.inPortY);
        inPortX += portWidth + style.module.port.pad;
      }
    }

    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        m_outPorts[key].setPosition(outPortX, m_metrics.outPortY);
        outPortX += portWidth + style.module.port.pad;
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get drawing metrics for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getMetrics = function() {
    return m_metrics;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get input ports for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getInPorts = function() {
    return m_inPorts;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get output ports for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getOutPorts = function() {
    return m_outPorts;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get registry for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getRegistry = function() {
    return m_registry;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get data for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getData = function() {
    return m_data;
  }


  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the module onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, style) {
    if(!m_metrics) {
      this.recomputeMetrics(ctx, style);
    }

    var portWidth = style.module.port.width,
      mx = m_metrics.mx,
      my = m_metrics.my,
      inPortX = m_metrics.inPortX,
      outPortX = m_metrics.outPortX,
      i,
      key;

    //draw rectangle
    ctx.fillStyle = style.module.fill;
    ctx.strokeStyle = style.module.stroke;
    ctx.fillRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);
    ctx.strokeRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);

    //draw ports
    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        m_inPorts[key].draw(ctx, portWidth);
      }
    }

    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        m_outPorts[key].draw(ctx, portWidth);
      }
    }

    //draw module name
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    ctx.fillText(
      m_data['@name'],
      mx + Math.floor((m_metrics.moduleWidth - m_metrics.fontMetrics.width)/2),
      my + m_metrics.textHeight + style.module.text.ypad
    );

  };

  this.contains = function(pos) {
    var metrics = this.getMetrics();
    return pos.x > metrics.mx && pos.x < metrics.mx + metrics.moduleWidth &&
      pos.y > metrics.my && pos.y < metrics.my + metrics.moduleHeight;
  };

  this.portByPos = function(pos) {
    var key;
    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        if (m_inPorts[key].contains(pos)) {
          return m_inPorts[key];
        }
      }
    }
    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        if (m_outPorts[key].contains(pos)) {
          return m_outPorts[key];
        }
      }
    }
    return null;
  };

  this.updateElementPositions = function() {};

  return this;
};

inherit(uiModule.module, vglModule.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class port
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.port}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.port = function(options, data) {
  "use strict"
  if (!(this instanceof uiModule.port)) {
    return new uiModule.port(options, data);
  }
  vglModule.object.call(this);

  /** @private */
  var m_that = this,
    m_data = data,
    m_x = 0,
    m_y = 0,
    m_width = style.module.port.width;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set x and y position
   *
   * @param {Number} x
   * @param {Number} y
   */
    ////////////////////////////////////////////////////////////////////////////
  this.setPosition = function(x, y) {
    m_x = x;
    m_y = y;
  };

  this.x = function() {
    return m_x;
  };

  this.y = function() {
    return m_y;
  };

  this.data = function() {
    return m_data;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the port onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, width) {
    ctx.fillStyle = style.module.port.fill;
    ctx.strokeStyle = style.module.port.stroke;
    ctx.fillRect(m_x, m_y, width, width);
    ctx.strokeRect(m_x, m_y, width, width);
  };

  this.getName = function() {
    return this.data()['@name'];
  };

  this.contains = function(pos) {
    if(pos.x >= m_x) {
      if(pos.y >= m_y) {
        if(pos.x <= m_x + m_width) {
          if(pos.y <= m_y + m_width) {
            return true;
          }
        }
      }
    }
    return false;
  };

  return this;
};

inherit(uiModule.port, vglModule.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class inputModule
 *
 * @class
 * @dec
 * @returns {uiModule.inputModule}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.inputModule = function(options, data) {
  "use strict";
  if (!(this instanceof uiModule.inputModule)) {
    return new uiModule.inputModule(options, data);
  }
  this.inputPortClass = uiModule.inputPort;
  this.outputPortClass = uiModule.outputPort;
  uiModule.module.call(this, options, data);

  /** @priave */
  var m_that = this,
    m_metrics,
    m_baseRecomputeMetrics = this.recomputeMetrics,
    m_baseGetMetrics = this.getMetrics;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Recompute drawing metrics for this module
   *
   * @param {CanvasRenderingContext2D} ctx
   * @param {object} style
   */
  ////////////////////////////////////////////////////////////////////////////

  this.recomputeMetrics = function(ctx, style) {
    m_baseRecomputeMetrics.call(this, ctx, style);
    var base_metrics = m_baseGetMetrics.call(this),
      new_metrics,
      maxInPortTextWidth = 0,
      maxOutPortTextWidth = 0,
      maxInPortTextKey,
      maxOutPortTextKey,
      portWidth = style.module.port.width,
      key;

    //find longest port name
    for(key in this.getInPorts()) {
      if(this.getInPorts().hasOwnProperty(key)) {
        if(this.getInPorts()[key].getName().length > maxInPortTextWidth) {
          maxInPortTextWidth = this.getInPorts()[key].getName().length;
          maxInPortTextKey = key;
        }
      }
    }

    for(key in this.getOutPorts()) {
      if(this.getOutPorts().hasOwnProperty(key)) {
        if(this.getOutPorts()[key].getName().length > maxOutPortTextWidth) {
          maxOutPortTextWidth = this.getOutPorts()[key].getName().length;
          maxOutPortTextKey = key;
        }
      }
    }

    var textHeight = 12, //TODO: get real height based on text font
      totalInPortHeight = style.module.port.inputHeight + textHeight +
        style.module.port.inputYPad + style.module.port.inpad,
      totalOutPortHeight = textHeight + style.module.port.outpad,
      inPortsHeight = this.inPortCount() * totalInPortHeight +
        style.module.text.xpad,
      outPortsHeight = this.outPortCount() * totalOutPortHeight;
    var
      fontMetrics = ctx.measureText(this.getData()['@name']),
      textWidth = fontMetrics.width + style.module.text.xpad * 2;
    var
      inPortFontMetrics = maxInPortTextKey ? ctx.measureText(
        this.getInPorts()[maxInPortTextKey]['@name']) : {width:0};
    var
      outPortFontMetrics = maxOutPortTextKey ? ctx.measureText(
        this.getOutPorts()[maxOutPortTextKey]['@name']) : {width:0},
      inPortsWidth = Math.max(inPortFontMetrics.width,
        style.module.port.inputWidth),
      outPortsWidth = outPortFontMetrics.width,
      moduleWidth = Math.max(
        inPortsWidth + outPortsWidth + portWidth*2 + style.module.port.pad*6,
        textWidth + style.module.text.xpad*2,
        style.module.minWidth
      ),
      moduleHeight = Math.max(
        inPortsHeight,
        outPortsHeight,
        style.module.minWidth
      ) + style.module.text.ypad*2 + textHeight,
      mx = Math.floor(this.getData().location['@x'] - moduleWidth/2),
      my = -Math.floor(this.getData().location['@y']),
      inPortY = my + style.module.port.pad + textHeight + portWidth*2,
      outPortY = my + moduleHeight - style.module.port.pad - portWidth;

    debug('inportwidth ' + inPortsWidth);
    debug('outputwidth ' + outPortsWidth);

    new_metrics = {
      mx: mx,
        my: my,
      fontMetrics: fontMetrics,
      textWidth: textWidth,
      moduleWidth: moduleWidth,
      textHeight: textHeight,
      moduleHeight: moduleHeight,
      inPortX: mx + style.module.port.pad,
      inPortY: inPortY,
      outPortX: mx + moduleWidth - style.module.port.pad - portWidth,
      outPortY: outPortY,
      outPortTextX: mx + inPortsWidth + portWidth + style.module.port.pad*4
    };

    merge_options_overwrite(base_metrics, new_metrics);
    m_metrics = base_metrics;

    //compute port positions
    for(key in this.getInPorts()) {
      if(this.getInPorts().hasOwnProperty(key)) {
        this.getInPorts()[key].setPosition(base_metrics.inPortX, inPortY);
        inPortY += style.module.port.inputHeight + textHeight +
          style.module.port.inputYPad + style.module.port.inpad;
      }
    }

    for(key in this.getOutPorts()) {
      if(this.getOutPorts().hasOwnProperty(key)) {
        this.getOutPorts()[key].setPosition(base_metrics.outPortX, outPortY);
        outPortY -= textHeight + style.module.port.outpad;
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get drawing metrics for this module
   */
    ////////////////////////////////////////////////////////////////////////////
  this.getMetrics = function() {
    return m_metrics;
  };


  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the module onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, style) {

    if(!m_metrics) {
      this.recomputeMetrics(ctx, style);
    }

    var portWidth = style.module.port.width,
      mx = m_metrics.mx,
      my = m_metrics.my,
      m_inPorts = this.getInPorts(),
      m_outPorts = this.getOutPorts(),
      key;

    //draw rectangle
    ctx.fillStyle = style.module.fill;
    ctx.strokeStyle = style.module.stroke;
    ctx.fillRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);
    ctx.strokeRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);

    //draw ports
    ctx.fillStyle = style.module.port.fill;
    ctx.strokeStyle = style.module.port.stroke;
    for(key in m_inPorts) {
      if(m_inPorts.hasOwnProperty(key)) {
        m_inPorts[key].draw(ctx, portWidth);
      }
    }

    for(key in m_outPorts) {
      if(m_outPorts.hasOwnProperty(key)) {
        m_outPorts[key].draw(ctx, portWidth);
      }
    }

    //draw module name
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    ctx.fillText(
      this.getData()['@name'],
      mx + Math.floor((m_metrics.moduleWidth - m_metrics.fontMetrics.width)/2),
      my + m_metrics.textHeight + style.module.text.ypad);

  };

  this.updateElementPositions = function() {
    var key,
      m_inPorts = this.getInPorts();
    for(key in m_inPorts) {
      m_inPorts[key].updateElementPosition(
        m_inPorts[key].x(),
        m_inPorts[key].y()
      );
    }
  };

  return this;
};

inherit(uiModule.inputModule, uiModule.module);


//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class outputPort
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.port}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.outputPort = function(options, data) {
  "use strict"
  if (!(this instanceof uiModule.outputPort)) {
    return new uiModule.outputPort(options, data);
  }
  uiModule.port.call(this, options, data);

  var m_name_width = 0;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Draw the port onto given context.
   *
   * @param {CanvasRenderingContext2D} ctx
   */
    ////////////////////////////////////////////////////////////////////////////
  this.draw = function(ctx, width) {
    ctx.fillStyle = style.module.port.fill;
    ctx.strokeStyle = style.module.port.stroke;
    ctx.fillRect(this.x(), this.y(), width, width);
    ctx.strokeRect(this.x(), this.y(), width, width);
    this.drawName(ctx, width);
  };

  this.drawName = function(ctx, width) {
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    if(m_name_width == 0) {
      var metrics = ctx.measureText(this.data()['@name']);
      m_name_width = metrics.width;
    }
    ctx.fillText(this.data()['@name'], this.x()-width-m_name_width,
      this.y()+width);
  };

  return this;
};

inherit(uiModule.outputPort, uiModule.port);


//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class inputPort
 *
 * @class
 * @dec
 * @param {object} data
 * @returns {uiModule.inputPort}
 */
//////////////////////////////////////////////////////////////////////////////
uiModule.inputPort = function(options, data) {
  "use strict"
  if (!(this instanceof uiModule.inputPort)) {
    return new uiModule.inputPort(options, data);
  }
  uiModule.outputPort.call(this, options, data);

  var m_input_elem,
    m_baseSetPosition = this.setPosition;

  this.drawName = function(ctx, width) {
    ctx.fillStyle = style.module.text.fill;
    ctx.font = style.module.text.font;
    ctx.fillText(this.data()['@name'], this.x() + width*2, this.y()+width);
  };

  function createElementFromType() {
    //TODO: support other types of input for color, dates, etc.
    m_input_elem = document.createElement('input');
    m_input_elem.type = 'text';
    $(m_input_elem).css({
      position: 'absolute'
    });
    $('#canvasContainer').append(m_input_elem);
  }

  this.setPosition = function(x,y) {
    m_baseSetPosition(x,y);
    this.updateElementPosition(x,y);
  };

  this.updateElementPosition = function(x,y) {
    var translated = activeWorkflow.translated();

    $(m_input_elem).css({
      top: y + translated.y + style.module.port.width*2,
      left: x + translated.x
    });
  }

  createElementFromType();

  return this;
};

inherit(uiModule.inputPort, uiModule.outputPort);

uiModule.connectionOptions = function() {

  "use strict";
  // Check against no use of new()
  if (!(this instanceof uiModule.connectionOptions)) {
    return new uiModule.connectionOptions();
  }

  this.vertical = false;
{'fraction_non_alphanumeric': 0.25090181083615903, 'fraction_numerical': 0.003246519010172426, 'mean_word_length': 1.781289189867068, 'pattern_counts': {'":': 64, '<': 17, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2680153', 'n_tokens_mistral': 16471, 'n_tokens_neox': 16256, 'n_words': 3045}