text: /*! JointJS v0.6.4 - JavaScript diagramming library  2013-10-15 
  
  
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
   */
  /**
   * jQuery.fn.sortElements
   * --------------
   * @author James Padolsey (http://james.padolsey.com)
   * @version 0.11
   * @updated 18-MAR-2010
   * --------------
   * @param Function comparator:
   *   Exactly the same behaviour as [1,2,3].sort(comparator)
   *   
   * @param Function getSortable
   *   A function that should return the element that is
   *   to be sorted. The comparator will run on the
   *   current collection, but you may want the actual
   *   resulting sort to occur on a parent or another
   *   associated element.
   *   
   *   E.g. $('td').sortElements(comparator, function(){
   *      return this.parentNode; 
   *   })
   *   
   *   The <td>'s parent (<tr>) will be sorted instead
   *   of the <td> itself.
   */
  jQuery.fn.sortElements = (function(){
      
      var sort = [].sort;
      
      return function(comparator, getSortable) {
          
          getSortable = getSortable || function(){return this;};
          
          var placements = this.map(function(){
              
              var sortElement = getSortable.call(this),
                  parentNode = sortElement.parentNode,
                  
                  // Since the element itself will change position, we have
                  // to have some way of storing it's original position in
                  // the DOM. The easiest way is to have a 'flag' node:
                  nextSibling = parentNode.insertBefore(
                      document.createTextNode(''),
                      sortElement.nextSibling
                  );
              
              return function() {
                  
                  if (parentNode === this) {
                      throw new Error(
                          "You can't sort elements if any one is a descendant of another."
                      );
                  }
                  
                  // Insert before flag:
                  parentNode.insertBefore(this, nextSibling);
                  // Remove flag:
                  parentNode.removeChild(nextSibling);
                  
              };
              
          });
         
          return sort.call(this, comparator).each(function(i){
              placements[i].call(getSortable.call(this));
          });
          
      };
      
  })();
  (function() {
    var arrays, basicObjects, deepClone, deepExtend, deepExtendCouple, isBasicObject,
      __slice = [].slice;
   
    deepClone = function(obj) {
      var func, isArr;
      if (!_.isObject(obj) || _.isFunction(obj)) {
        return obj;
      }
      if (_.isDate(obj)) {
        return new Date(obj.getTime());
      }
      if (_.isRegExp(obj)) {
        return new RegExp(obj.source, obj.toString().replace(/.*\//, ""));
      }
      isArr = _.isArray(obj || _.isArguments(obj));
      func = function(memo, value, key) {
        if (isArr) {
          memo.push(deepClone(value));
        } else {
          memo[key] = deepClone(value);
        }
        return memo;
      };
      return _.reduce(obj, func, isArr ? [] : {});
    };
   
    isBasicObject = function(object) {
      return (object.prototype === {}.prototype || object.prototype === Object.prototype) && _.isObject(object) && !_.isArray(object) && !_.isFunction(object) && !_.isDate(object) && !_.isRegExp(object) && !_.isArguments(object);
    };
   
    basicObjects = function(object) {
      return _.filter(_.keys(object), function(key) {
        return isBasicObject(object[key]);
      });
    };
   
    arrays = function(object) {
      return _.filter(_.keys(object), function(key) {
        return _.isArray(object[key]);
      });
    };
   
    deepExtendCouple = function(destination, source, maxDepth) {
      var combine, recurse, sharedArrayKey, sharedArrayKeys, sharedObjectKey, sharedObjectKeys, _i, _j, _len, _len1;
      if (maxDepth == null) {
        maxDepth = 20;
      }
      if (maxDepth <= 0) {
        console.warn('_.deepExtend(): Maximum depth of recursion hit.');
        return _.extend(destination, source);
      }
      sharedObjectKeys = _.intersection(basicObjects(destination), basicObjects(source));
      recurse = function(key) {
        return source[key] = deepExtendCouple(destination[key], source[key], maxDepth - 1);
      };
      for (_i = 0, _len = sharedObjectKeys.length; _i < _len; _i++) {
        sharedObjectKey = sharedObjectKeys[_i];
        recurse(sharedObjectKey);
      }
      sharedArrayKeys = _.intersection(arrays(destination), arrays(source));
      combine = function(key) {
        return source[key] = _.union(destination[key], source[key]);
      };
      for (_j = 0, _len1 = sharedArrayKeys.length; _j < _len1; _j++) {
        sharedArrayKey = sharedArrayKeys[_j];
        combine(sharedArrayKey);
      }
      return _.extend(destination, source);
    };
   
    deepExtend = function() {
      var finalObj, maxDepth, objects, _i;
      objects = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), maxDepth = arguments[_i++];
      if (!_.isNumber(maxDepth)) {
        objects.push(maxDepth);
        maxDepth = 20;
      }
      if (objects.length <= 1) {
        return objects[0];
      }
      if (maxDepth <= 0) {
        return _.extend.apply(this, objects);
      }
      finalObj = objects.shift();
      while (objects.length > 0) {
        finalObj = deepExtendCouple(finalObj, deepClone(objects.shift()), maxDepth);
      }
      return finalObj;
    };
   
    _.mixin({
      deepClone: deepClone,
      isBasicObject: isBasicObject,
      basicObjects: basicObjects,
      arrays: arrays,
      deepExtend: deepExtend
    });
   
  }).call(this);
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS library.
  //      (c) 2011-2013 client IO
  
  
  // Global namespace.
  
  var joint = {
  
      // `joint.dia` namespace.
      dia: {},
  
      // `joint.ui` namespace.
      ui: {},
  
      // `joint.layout` namespace.
      layout: {},
  
      // `joint.shapes` namespace.
      shapes: {},
  
      // `joint.format` namespace.
      format: {},
  
      util: {
  
          getByPath: function(obj, path, delim) {
              
              delim = delim || '.';
              var keys = path.split(delim);
              var key;
              
              while (keys.length) {
                  key = keys.shift();
                  if (key in obj) {
                      obj = obj[key];
                  } else {
                      return undefined;
                  }
              }
              return obj;
          },
  
          setByPath: function(obj, path, value, delim) {
  
              delim = delim || '.';
  
              var keys = path.split(delim);
              var diver = obj;
              var i = 0;
  
              if (path.indexOf(delim) > -1) {
  
                  for (var len = keys.length; i < len - 1; i++) {
                      // diver creates an empty object if there is no nested object under such a key.
                      // This means that one can populate an empty nested object with setByPath().
                      diver = diver[keys[i]] || (diver[keys[i]] = {});
                  }
                  diver[keys[len - 1]] = value;
              } else {
                  obj[path] = value;
              }
              return obj;
          },
  
          flattenObject: function(obj, delim, stop) {
              
              delim = delim || '.';
              var ret = {};
  	    
  	    for (var key in obj) {
  		if (!obj.hasOwnProperty(key)) continue;
  
                  var shouldGoDeeper = typeof obj[key] === 'object';
                  if (shouldGoDeeper && stop && stop(obj[key])) {
                      shouldGoDeeper = false;
                  }
                  
  		if (shouldGoDeeper) {
  		    var flatObject = this.flattenObject(obj[key], delim, stop);
  		    for (var flatKey in flatObject) {
  			if (!flatObject.hasOwnProperty(flatKey)) continue;
  			
  			ret[key + delim + flatKey] = flatObject[flatKey];
  		    }
  		} else {
  		    ret[key] = obj[key];
  		}
  	    }
  	    return ret;
          },
  
          uuid: function() {
  
              // credit: http://stackoverflow.com/posts/2117523/revisions
              
              return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                  var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
                  return v.toString(16);
              });
          },
  
          // Generate global unique id for obj and store it as a property of the object.
          guid: function(obj) {
              
              this.guid.id = this.guid.id || 1;
              obj.id = (obj.id === undefined ? 'j_' + this.guid.id++ : obj.id);
              return obj.id;
          },
  
          // Copy all the properties to the first argument from the following arguments.
          // All the properties will be overwritten by the properties from the following
          // arguments. Inherited properties are ignored.
          mixin: function() {
              
              var target = arguments[0];
              
              for (var i = 1, l = arguments.length; i < l; i++) {
                  
                  var extension = arguments[i];
                  
                  // Only functions and objects can be mixined.
  
                  if ((Object(extension) !== extension) &&
                      !_.isFunction(extension) &&
                      (extension === null || extension === undefined)) {
  
                      continue;
                  }
  
                  _.each(extension, function(copy, key) {
                      
                      if (this.mixin.deep && (Object(copy) === copy)) {
  
                          if (!target[key]) {
  
                              target[key] = _.isArray(copy) ? [] : {};
                          }
                          
                          this.mixin(target[key], copy);
                          return;
                      }
                      
                      if (target[key] !== copy) {
                          
                          if (!this.mixin.supplement || !target.hasOwnProperty(key)) {
                              
  	                    target[key] = copy;
                          }
  
                      }
                      
                  }, this);
              }
              
              return target;
          },
  
          // Copy all properties to the first argument from the following
          // arguments only in case if they don't exists in the first argument.
          // All the function propererties in the first argument will get
          // additional property base pointing to the extenders same named
          // property function's call method.
          supplement: function() {
  
              this.mixin.supplement = true;
              var ret = this.mixin.apply(this, arguments);
              this.mixin.supplement = false;
              return ret;
          },
  
          // Same as `mixin()` but deep version.
          deepMixin: function() {
              
              this.mixin.deep = true;
              var ret = this.mixin.apply(this, arguments);
              this.mixin.deep = false;
              return ret;
          },
  
          // Same as `supplement()` but deep version.
          deepSupplement: function() {
              
              this.mixin.deep = this.mixin.supplement = true;
              var ret = this.mixin.apply(this, arguments);
              this.mixin.deep = this.mixin.supplement = false;
              return ret;
          }
          
      }
  };
  
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  // Vectorizer.
  // -----------
  
  // A tiny library for making your live easier when dealing with SVG.
  
  // Copyright © 2012 - 2013 client IO
  
  (function(root, factory) {
  
      if (typeof define === 'function' && define.amd) {
          // AMD. Register as an anonymous module.
          define(['underscore'], factory);
          
      } else {
          // Browser globals.
          root.Vectorizer = root.V = factory(root._);
      }
  
  }(this, function(_) {
  
      // Well, if SVG is not supported, this library is useless.
      var SVGsupported = !!(window.SVGAngle || document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1'));
  
      // XML namespaces.
      var ns = {
          xmlns: 'http://www.w3.org/2000/svg',
          xlink: 'http://www.w3.org/1999/xlink'
      };
      // SVG version.
      var SVGversion = '1.1';
  
      // Create SVG element.
      // -------------------
  
      function createElement(el, attrs, children) {
  
          if (!el) return undefined;
          
          // If `el` is an object, it is probably a native SVG element. Wrap it to VElement.
          if (typeof el === 'object') {
              return new VElement(el);
          }
          attrs = attrs || {};
  
          // If `el` is a `'svg'` or `'SVG'` string, create a new SVG canvas.
          if (el.toLowerCase() === 'svg') {
              
              attrs.xmlns = ns.xmlns;
              attrs['xmlns:xlink'] = ns.xlink;
              attrs.version = SVGversion;
              
          } else if (el[0] === '<') {
              // Create element from an SVG string.
              // Allows constructs of type: `document.appendChild(Vectorizer('<rect></rect>').node)`.
              
              var svg = '<svg xmlns="' + ns.xmlns + '" xmlns:xlink="' + ns.xlink + '" version="' + SVGversion + '">' + el + '</svg>';
              var parser = new DOMParser();
              parser.async = false;
              var svgDoc = parser.parseFromString(svg, 'text/xml').documentElement;
  
              // Note that `createElement()` might also return an array should the SVG string passed as
              // the first argument contain more then one root element.
              if (svgDoc.childNodes.length > 1) {
  
                  return _.map(svgDoc.childNodes, function(childNode) {
  
                      return new VElement(document.importNode(childNode, true));
                  });
              }
              
              return new VElement(document.importNode(svgDoc.firstChild, true));
          }
          
          el = document.createElementNS(ns.xmlns, el);
  
          // Set attributes.
          for (var key in attrs) {
  
              setAttribute(el, key, attrs[key]);
          }
          
          // Normalize `children` array.
          if (Object.prototype.toString.call(children) != '[object Array]') children = [children];
  
          // Append children if they are specified.
          var i = 0, len = (children[0] && children.length) || 0, child;
          for (; i < len; i++) {
              child = children[i];
              el.appendChild(child instanceof VElement ? child.node : child);
          }
          
          return new VElement(el);
      }
  
      function setAttribute(el, name, value) {
          
          if (name.indexOf(':') > -1) {
              // Attribute names can be namespaced. E.g. `image` elements
              // have a `xlink:href` attribute to set the source of the image.
              var combinedKey = name.split(':');
              el.setAttributeNS(ns[combinedKey[0]], combinedKey[1], value);
          } else if (name === 'id') {
              el.id = value;
          } else {
              el.setAttribute(name, value);
          }
      }
  
      function parseTransformString(transform) {
          var translate,
              rotate,
              scale;
          
          if (transform) {
              var translateMatch = transform.match(/translate\((.*)\)/);
              if (translateMatch) {
                  translate = translateMatch[1].split(',');
              }
              var rotateMatch = transform.match(/rotate\((.*)\)/);
              if (rotateMatch) {
                  rotate = rotateMatch[1].split(',');
              }
              var scaleMatch = transform.match(/scale\((.*)\)/);
              if (scaleMatch) {
                  scale = scaleMatch[1].split(',');
              }
          }
  
          var sx = (scale && scale[0]) ? parseFloat(scale[0]) : 1;
          
          return {
              translate: {
                  tx: (translate && translate[0]) ? parseInt(translate[0], 10) : 0,
                  ty: (translate && translate[1]) ? parseInt(translate[1], 10) : 0
              },
              rotate: {
                  angle: (rotate && rotate[0]) ? parseInt(rotate[0], 10) : 0,
                  cx: (rotate && rotate[1]) ? parseInt(rotate[1], 10) : undefined,
                  cy: (rotate && rotate[2]) ? parseInt(rotate[2], 10) : undefined
              },
              scale: {
                  sx: sx,
                  sy: (scale && scale[1]) ? parseFloat(scale[1]) : sx
              }
          };
      }
  
  
      // Matrix decomposition.
      // ---------------------
  
      function deltaTransformPoint(matrix, point)  {
          
  	var dx = point.x * matrix.a + point.y * matrix.c + 0;
  	var dy = point.x * matrix.b + point.y * matrix.d + 0;
  	return { x: dx, y: dy };
      }
  
      function decomposeMatrix(matrix) {
  
          // @see https://gist.github.com/2052247
          
          // calculate delta transform point
  	var px = deltaTransformPoint(matrix, { x: 0, y: 1 });
  	var py = deltaTransformPoint(matrix, { x: 1, y: 0 });
          
  	// calculate skew
  	var skewX = ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90);
  	var skewY = ((180 / Math.PI) * Math.atan2(py.y, py.x));
          
  	return {
              
  	    translateX: matrix.e,
  	    translateY: matrix.f,
  	    scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b),
  	    scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d),
  	    skewX: skewX,
  	    skewY: skewY,
  	    rotation: skewX // rotation is the same as skew x
  	};
      }
      
      // VElement.
      // ---------
  
      function VElement(el) {
          this.node = el;
          if (!this.node.id) {
              this.node.id = _.uniqueId('v_');
          }
      }
  
      // VElement public API.
      // --------------------
  
      VElement.prototype = {
          
          translate: function(tx, ty) {
              ty = ty || 0;
              
              var transformAttr = this.attr('transform') || '',
                  transform = parseTransformString(transformAttr);
  
              // Is it a getter?
              if (typeof tx === 'undefined') {
                  return transform.translate;
              }
              
              transformAttr = transformAttr.replace(/translate\([^\)]*\)/g, '').trim();
  
              var newTx = transform.translate.tx + tx,
                  newTy = transform.translate.ty + ty;
  
              // Note that `translate()` is always the first transformation. This is
              // usually the desired case.
              this.attr('transform', 'translate(' + newTx + ',' + newTy + ') ' + transformAttr);
              return this;
          },
  
          rotate: function(angle, cx, cy) {
              var transformAttr = this.attr('transform') || '',
                  transform = parseTransformString(transformAttr);
  
              // Is it a getter?
              if (typeof angle === 'undefined') {
                  return transform.rotate;
              }
              
              transformAttr = transformAttr.replace(/rotate\([^\)]*\)/g, '').trim();
  
              var newAngle = transform.rotate.angle + angle % 360,
                  newOrigin = (cx !== undefined && cy !== undefined) ? ',' + cx + ',' + cy : '';
              
              this.attr('transform', transformAttr + ' rotate(' + newAngle + newOrigin + ')');
              return this;
          },
  
          // Note that `scale` as the only transformation does not combine with previous values.
          scale: function(sx, sy) {
              sy = (typeof sy === 'undefined') ? sx : sy;
              
              var transformAttr = this.attr('transform') || '',
                  transform = parseTransformString(transformAttr);
  
              // Is it a getter?
              if (typeof sx === 'undefined') {
                  return transform.scale;
              }
              
              transformAttr = transformAttr.replace(/scale\([^\)]*\)/g, '').trim();
  
              this.attr('transform', transformAttr + ' scale(' + sx + ',' + sy + ')');
              return this;
          },
  
          // Get SVGRect that contains coordinates and dimension of the real bounding box,
          // i.e. after transformations are applied.
          // If `target` is specified, bounding box will be computed relatively to `target` element.
          bbox: function(withoutTransformations, target) {
  
              var box;
              try {
  
                  box = this.node.getBBox();
  
  		// Opera returns infinite values in some cases.
  		// Note that Infinity | 0 produces 0 as opposed to Infinity || 0.
  		// We also have to create new object as the standard says that you can't
  		// modify the attributes of a bbox.
  		box = { x: box.x | 0, y: box.y | 0, width: box.width | 0, height: box.height | 0};
  
              } catch (e) {
  
                  // Fallback for IE.
                  box = {
                      x: this.node.clientLeft,
                      y: this.node.clientTop,
                      width: this.node.clientWidth,
                      height: this.node.clientHeight
                  };
              }
  
              if (withoutTransformations) {
  
                  return box;
              }
  
              var matrix = this.node.getTransformToElement(target || this.node.ownerSVGElement);
              var corners = [];
              var point = this.node.ownerSVGElement.createSVGPoint();
  
  
              point.x = box.x;
              point.y = box.y;
              corners.push(point.matrixTransform(matrix));
              
              point.x = box.x + box.width;
              point.y = box.y;
              corners.push(point.matrixTransform(matrix));
              
              point.x = box.x + box.width;
              point.y = box.y + box.height;
              corners.push(point.matrixTransform(matrix));
              
              point.x = box.x;
              point.y = box.y + box.height;
              corners.push(point.matrixTransform(matrix));
  
              var minX = corners[0].x;
              var maxX = minX;
              var minY = corners[0].y;
              var maxY = minY;
              
              for (var i = 1, len = corners.length; i < len; i++) {
                  
                  var x = corners[i].x;
                  var y = corners[i].y;
  
                  if (x < minX) {
                      minX = x;
                  } else if (x > maxX) {
                      maxX = x;
                  }
                  
                  if (y < minY) {
                      minY = y;
                  } else if (y > maxY) {
                      maxY = y;
                  }
              }
  
              return {
                  x: minX,
                  y: minY,
                  width: maxX - minX,
                  height: maxY - minY
              };
          },
  
          text: function(content) {
              var lines = content.split('\n'), i = 0,
                  tspan;
  
              // `alignment-baseline` does not work in Firefox.
  	    // Setting `dominant-baseline` on the `<text>` element doesn't work in IE9.
              // In order to have the 0,0 coordinate of the `<text>` element (or the first `<tspan>`)
  	    // in the top left corner we translate the `<text>` element by `0.8em`.
  	    // See `http://www.w3.org/Graphics/SVG/WG/wiki/How_to_determine_dominant_baseline`.
  	    // See also `http://apike.ca/prog_svg_text_style.html`.
  	    this.attr('y', '0.8em');
              
              if (lines.length === 1) {
                  this.node.textContent = content;
                  return this;
              }
              // Easy way to erase all `<tspan>` children;
              this.node.textContent = '';
              
              for (; i < lines.length; i++) {
  
                  // Shift all the <tspan> but first by one line (`1em`)
                  tspan = V('tspan', { dy: (i == 0 ? '0em' : '1em'), x: this.attr('x') || 0});
                  tspan.node.textContent = lines[i];
                  
                  this.append(tspan);
              }
              return this;
          },
          
          attr: function(name, value) {
              
              if (typeof name === 'string' && typeof value === 'undefined') {
                  return this.node.getAttribute(name);
              }
              
              if (typeof name === 'object') {
                  
                  _.each(name, function(value, name) {
  
                      setAttribute(this.node, name, value);
                      
                  }, this);
                  
              } else {
  
                  setAttribute(this.node, name, value);
              }
  
              return this;
          },
  
          remove: function() {
              if (this.node.parentNode) {
                  this.node.parentNode.removeChild(this.node);
              }
          },
  
          append: function(el) {
  
              var els = el;
              
              if (!_.isArray(el)) {
                  
                  els = [el];
              }
  
              _.each(els, function(el) {
  
                  this.node.appendChild(el instanceof VElement ? el.node : el);
                  
              }, this);
              
              return this;
          },
  
          prepend: function(el) {
              this.node.insertBefore(el instanceof VElement ? el.node : el, this.node.firstChild);
          },
  
          svg: function() {
  
              return this.node instanceof window.SVGSVGElement ? this : V(this.node.ownerSVGElement);
          },
  
          defs: function() {
  
              var defs = this.svg().node.getElementsByTagName('defs');
              
              return (defs && defs.length) ? V(defs[0]) : undefined;
          },
  
          clone: function() {
              var clone = V(this.node.cloneNode(true));
              // Note that clone inherits also ID. Therefore, we need to change it here.
              clone.node.id = _.uniqueId('v-');
              return clone;
          },
  
          // Convert global point into the coordinate space of this element.
          toLocalPoint: function(x, y) {
  
              var svg = this.svg().node;
              
              var p = svg.createSVGPoint();
              p.x = x;
              p.y = y;
  
  	    try {
  
  		var globalPoint = p.matrixTransform(svg.getScreenCTM().inverse());
  		var globalToLocalMatrix = this.node.getTransformToElement(svg).inverse();
  
  	    } catch(e) {
  		// IE9 throws an exception in odd cases. (`Unexpected call to method or property access`)
  		// We have to make do with the original coordianates.
  		return p;
  	    }
  
              return globalPoint.matrixTransform(globalToLocalMatrix);
          },
  
          translateCenterToPoint: function(p) {
  
              var bbox = this.bbox();
              var center = g.rect(bbox).center();
  
              this.translate(p.x - center.x, p.y - center.y);
          },
  
          // Efficiently auto-orient an element. This basically implements the orient=auto attribute
          // of markers. The easiest way of understanding on what this does is to imagine the element is an
          // arrowhead. Calling this method on the arrowhead makes it point to the `position` point while
          // being auto-oriented (properly rotated) towards the `reference` point.
          // `target` is the element relative to which the transformations are applied. Usually a viewport.
          translateAndAutoOrient: function(position, reference, target) {
  
              // Clean-up previously set transformations except the scale. If we didn't clean up the
              // previous transformations then they'd add up with the old ones. Scale is an exception as
              // it doesn't add up, consider: `this.scale(2).scale(2).scale(2)`. The result is that the
              // element is scaled by the factor 2, not 8.
  
              var s = this.scale();
              this.attr('transform', '');
              this.scale(s.sx, s.sy);
  
              var svg = this.svg().node;
              var bbox = this.bbox(false, target);
  
              // 1. Translate to origin.
              var translateToOrigin = svg.createSVGTransform();
              translateToOrigin.setTranslate(-bbox.x - bbox.width/2, -bbox.y - bbox.height/2);
  
              // 2. Rotate around origin.
              var rotateAroundOrigin = svg.createSVGTransform();
              var angle = g.point(position).changeInAngle(position.x - reference.x, position.y - reference.y, reference);
              rotateAroundOrigin.setRotate(angle, 0, 0);
  
              // 3. Translate to the `position` + the offset (half my width) towards the `reference` point.
              var translateFinal = svg.createSVGTransform();
              var finalPosition = g.point(position).move(reference, bbox.width/2);
              translateFinal.setTranslate(position.x + (position.x - finalPosition.x), position.y + (position.y - finalPosition.y));
  
              // 4. Apply transformations.
              var ctm = this.node.getTransformToElement(target);
              var transform = svg.createSVGTransform();
              transform.setMatrix(
                  translateFinal.matrix.multiply(
                      rotateAroundOrigin.matrix.multiply(
                          translateToOrigin.matrix.multiply(
                              ctm)))
              );
  
              // Instead of directly setting the `matrix()` transform on the element, first, decompose
              // the matrix into separate transforms. This allows us to use normal Vectorizer methods
              // as they don't work on matrices. An example of this is to retrieve a scale of an element.
              // this.node.transform.baseVal.initialize(transform);
  
              var decomposition = decomposeMatrix(transform.matrix);
  
              this.translate(decomposition.translateX, decomposition.translateY);
              this.rotate(decomposition.rotation);
              // Note that scale has been already applied, hence the following line stays commented. (it's here just for reference).
              //this.scale(decomposition.scaleX, decomposition.scaleY);
  
              return this;
          },
  
          animateAlongPath: function(attrs, path) {
  
              var animateMotion = V('animateMotion', attrs);
              var mpath = V('mpath', { 'xlink:href': '#' + V(path).node.id });
  
              animateMotion.append(mpath);
  
              this.append(animateMotion);
              try {
                  animateMotion.node.beginElement();
              } catch (e) {
                  // Fallback for IE 9.
  		// Run the animation programatically if FakeSmile (`http://leunen.me/fakesmile/`) present 
  		if (document.documentElement.getAttribute('smiling') === 'fake') {
  
  		    // Register the animation. (See `https://answers.launchpad.net/smil/+question/203333`)
  		    var animation = animateMotion.node;
  		    animation.animators = new Array();
  
  		    var animationID = animation.getAttribute('id');
  		    if (animationID) id2anim[animationID] = animation;
  
  		    _.each(getTargets(animation), function(target, index) {
  			var animator = new Animator(animation, target, index);
  			animators.push(animator);
  			animation.animators[index] = animator;
  		    });
  
  		    _.invoke(animation.animators, 'register');
  		}
              }
          }
      };
  
      var V = createElement;
  
      V.decomposeMatrix = decomposeMatrix;
  
      var svgDocument = V('svg').node;
      
      V.createSVGMatrix = function(m) {
          
          return _.extend(svgDocument.createSVGMatrix(), m);
      };
  
      V.createSVGTransform = function() {
  
          return svgDocument.createSVGTransform();
      };
  
      V.createSVGPoint = function(x, y) {
  
          var p = svgDocument.createSVGPoint();
          p.x = x;
          p.y = y;
          return p;
      };
  
      return V;
  
  }));
  
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      Geometry library.
  //      (c) 2011-2013 client IO
  
  
  (function(root, factory) {
  
      if (typeof define === 'function' && define.amd) {
          // AMD. Register as an anonymous module.
          define([], factory);
          
      } else {
          // Browser globals.
          root.g = factory();
      }
  
  }(this, function() {
  
  
      // Declare shorthands to the most used math functions.
      var math = Math;
      var abs = math.abs;
      var cos = math.cos;
      var sin = math.sin;
      var sqrt = math.sqrt;
      var mmin = math.min;
      var mmax = math.max;
      var atan = math.atan;
      var atan2 = math.atan2;
      var acos = math.acos;
      var round = math.round;
      var floor = math.floor;
      var PI = math.PI;
      var random = math.random;
      var toDeg = function(rad) { return (180*rad / PI) % 360; };
      var toRad = function(deg) { return (deg % 360) * PI / 180; };
      var snapToGrid = function(val, gridSize) { return gridSize * Math.round(val/gridSize); };
      var normalizeAngle = function(angle) { return (angle % 360) + (angle < 0 ? 360 : 0); };
  
      // Point
      // -----
  
      // Point is the most basic object consisting of x/y coordinate,.
  
      // Possible instantiations are:
  
      // * `point(10, 20)`
      // * `new point(10, 20)`
      // * `point('10 20')`
      // * `point(point(10, 20))`
      function point(x, y) {
          if (!(this instanceof point))
              return new point(x, y);
          var xy;
          if (y === undefined && Object(x) !== x) {
              xy = x.split(_.indexOf(x, "@") === -1 ? " " : "@");
              this.x = parseInt(xy[0], 10);
              this.y = parseInt(xy[1], 10);
          } else if (Object(x) === x) {
              this.x = x.x;
              this.y = x.y;
          } else {
              this.x = x;
              this.y = y;
          }
      }
  
      point.prototype = {
          toString: function() {
              return this.x + "@" + this.y;
          },
          // If point lies outside rectangle `r`, return the nearest point on the boundary of rect `r`,
          // otherwise return point itself.
          // (see Squeak Smalltalk, Point>>adhereTo:)
          adhereToRect: function(r) {
  	    if (r.containsPoint(this)){
  	        return this;
  	    }
  	    this.x = mmin(mmax(this.x, r.x), r.x + r.width);
  	    this.y = mmin(mmax(this.y, r.y), r.y + r.height);
  	    return this;
          },
          // Compute the angle between me and `p` and the x axis.
          // (cartesian-to-polar coordinates conversion)
          // Return theta angle in degrees.
          theta: function(p) {
              p = point(p);
              // Invert the y-axis.
  	    var y = -(p.y - this.y);
  	    var x = p.x - this.x;
              // Makes sure that the comparison with zero takes rounding errors into account.
              var PRECISION = 10;
              // Note that `atan2` is not defined for `x`, `y` both equal zero.
  	    var rad = (y.toFixed(PRECISION) == 0 && x.toFixed(PRECISION) == 0) ? 0 : atan2(y, x); 
  
              // Correction for III. and IV. quadrant.
  	    if (rad < 0) { 
  	        rad = 2*PI + rad;
  	    }
  	    return 180*rad / PI;
          },
          // Returns distance between me and point `p`.
          distance: function(p) {
  	    return line(this, p).length();
          },
          // Returns a manhattan (taxi-cab) distance between me and point `p`.
          manhattanDistance: function(p) {
              return abs(p.x - this.x) + abs(p.y - this.y);
          },
          // Offset me by the specified amount.
          offset: function(dx, dy) {
  	    this.x += dx || 0;
  	    this.y += dy || 0;
  	    return this;
          },
          magnitude: function() {
              return sqrt((this.x*this.x) + (this.y*this.y)) || 0.01;
          },
          update: function(x, y) {
              this.x = x || 0;
              this.y = y || 0;
              return this;
          },
          round: function(decimals) {
              this.x = decimals ? this.x.toFixed(decimals) : round(this.x);
              this.y = decimals ? this.y.toFixed(decimals) : round(this.y);
              return this;
          },
          // Scale the line segment between (0,0) and me to have a length of len.
          normalize: function(len) {
  	    var s = (len || 1) / this.magnitude();
  	    this.x = s * this.x;
  	    this.y = s * this.y;
  	    return this;
          },
          difference: function(p) {
              return point(this.x - p.x, this.y - p.y);
          },
          // Converts rectangular to polar coordinates.
          // An origin can be specified, otherwise it's 0@0.
          toPolar: function(o) {
              o = (o && point(o)) || point(0,0);
              var x = this.x;
              var y = this.y;
              this.x = sqrt((x-o.x)*(x-o.x) + (y-o.y)*(y-o.y));   // r
              this.y = toRad(o.theta(point(x,y)));
              return this;
          },
          // Rotate point by angle around origin o.
          rotate: function(o, angle) {
              angle = (angle + 360) % 360;
              this.toPolar(o);
              this.y += toRad(angle);
              var p = point.fromPolar(this.x, this.y, o);
              this.x = p.x;
              this.y = p.y;
              return this;
          },
          // Move point on line starting from ref ending at me by
          // distance distance.
          move: function(ref, distance) {
              var theta = toRad(point(ref).theta(this));
              return this.offset(cos(theta) * distance, -sin(theta) * distance);
          },
          // Returns change in angle from my previous position (-dx, -dy) to my new position
          // relative to ref point.
          changeInAngle: function(dx, dy, ref) {
              // Revert the translation and measure the change in angle around x-axis.
              return point(this).offset(-dx, -dy).theta(ref) - this.theta(ref);
          },
          equals: function(p) {
              return this.x === p.x && this.y === p.y;
          }
      };
      // Alternative constructor, from polar coordinates.
      // @param {number} r Distance.
      // @param {number} angle Angle in radians.
      // @param {point} [optional] o Origin.
      point.fromPolar = function(r, angle, o) {
          o = (o && point(o)) || point(0,0);
          var x = abs(r * cos(angle));
          var y = abs(r * sin(angle));
          var deg = normalizeAngle(toDeg(angle));
  
          if (deg < 90) y = -y;
          else if (deg < 180) { x = -x; y = -y; }
          else if (deg < 270) x = -x;
          
          return point(o.x + x, o.y + y);
      };
  
      // Create a point with random coordinates that fall into the range `[x1, x2]` and `[y1, y2]`.
      point.random = function(x1, x2, y1, y2) {
          return point(floor(random() * (x2 - x1 + 1) + x1), floor(random() * (y2 - y1 + 1) + y1));
      };
  
      // Line.
      // -----
      function line(p1, p2) {
          if (!(this instanceof line))
              return new line(p1, p2);
              this.start = point(p1);
          this.end = point(p2);
      }
      
      line.prototype = {
          toString: function() {
  	    return this.start.toString() + ' ' + this.end.toString();
          },
          // @return {double} length of the line
          length: function() {
              return sqrt(this.squaredLength());
          },
          // @return {integer} length without sqrt
          // @note for applications where the exact length is not necessary (e.g. compare only)
          squaredLength: function() {
  	    var x0 = this.start.x;
              var y0 = this.start.y;
  	    var x1 = this.end.x;
              var y1 = this.end.y;
  	    return (x0 -= x1)*x0 + (y0 -= y1)*y0;
          },
          // @return {point} my midpoint
          midpoint: function() {
  	    return point((this.start.x + this.end.x) / 2,
  		         (this.start.y + this.end.y) / 2);
          },
          // @return {point} Point where I'm intersecting l.
          // @see Squeak Smalltalk, LineSegment>>intersectionWith:
          intersection: function(l) {
  	    var pt1Dir = point(this.end.x - this.start.x, this.end.y - this.start.y);
  	    var pt2Dir = point(l.end.x - l.start.x, l.end.y - l.start.y);
  	    var det = (pt1Dir.x * pt2Dir.y) - (pt1Dir.y * pt2Dir.x);
  	    var deltaPt = point(l.start.x - this.start.x, l.start.y - this.start.y);
  	    var alpha = (deltaPt.x * pt2Dir.y) - (deltaPt.y * pt2Dir.x);
  	    var beta = (deltaPt.x * pt1Dir.y) - (deltaPt.y * pt1Dir.x);
  
  	    if (det === 0 ||
  	        alpha * det < 0 ||
  	        beta * det < 0) {
                  // No intersection found.
  	        return null;	
  	    }
  	    if (det > 0){
  	        if (alpha > det || beta > det){
  		    return null;
  	        }
  	    } else {
  	        if (alpha < det || beta < det){
  		    return null;
  	        }
  	    }
  	    return point(this.start.x + (alpha * pt1Dir.x / det),
  		         this.start.y + (alpha * pt1Dir.y / det));
          }
      };
  
      // Rectangle.
      // ----------
      function rect(x, y, w, h) {
          if (!(this instanceof rect))
              return new rect(x, y, w, h);
          if (y === undefined) {
              y = x.y;
              w = x.width;
              h = x.height;
              x = x.x;        
          }
          this.x = x;
          this.y = y;
          this.width = w;
          this.height = h;
      }
      
      rect.prototype = {
          toString: function() {
  	    return this.origin().toString() + ' ' + this.corner().toString();
          },
          origin: function() {
              return point(this.x, this.y);
          },
          corner: function() {
              return point(this.x + this.width, this.y + this.height);
          },
          topRight: function() {
              return point(this.x + this.width, this.y);
          },
          bottomLeft: function() {
              return point(this.x, this.y + this.height);
          },
          center: function() {
              return point(this.x + this.width/2, this.y + this.height/2);
          },
          // @return {boolean} true if rectangles intersect
          intersect: function(r) {
  	    var myOrigin = this.origin();
  	    var myCorner = this.corner();
  	    var rOrigin = r.origin();
  	    var rCorner = r.corner();
              
  	    if (rCorner.x <= myOrigin.x ||
  	        rCorner.y <= myOrigin.y ||
  	        rOrigin.x >= myCorner.x ||
  	        rOrigin.y >= myCorner.y) return false;
  	    return true;
          },
          // @return {string} (left|right|top|bottom) side which is nearest to point
          // @see Squeak Smalltalk, Rectangle>>sideNearestTo:
          sideNearestToPoint: function(p) {
              p = point(p);
  	    var distToLeft = p.x - this.x;
  	    var distToRight = (this.x + this.width) - p.x;
  	    var distToTop = p.y - this.y;
  	    var distToBottom = (this.y + this.height) - p.y;
  	    var closest = distToLeft;
  	    var side = 'left';
              
  	    if (distToRight < closest) {
  	        closest = distToRight;
  	        side = 'right';
  	    }
  	    if (distToTop < closest) {
  	        closest = distToTop;
  	        side = 'top';
  	    }
  	    if (distToBottom < closest) {
  	        closest = distToBottom;
  	        side = 'bottom';
  	    }
  	    return side;
          },
          // @return {bool} true if point p is insight me
          containsPoint: function(p) {
              p = point(p);
  	    if (p.x > this.x && p.x < this.x + this.width &&
  	        p.y > this.y && p.y < this.y + this.height) {
  	        return true;
  	    }
  	    return false;
          },
          // @return {point} a point on my boundary nearest to p
          // @see Squeak Smalltalk, Rectangle>>pointNearestTo:
          pointNearestToPoint: function(p) {
              p = point(p);
  	    if (this.containsPoint(p)) {
  	        var side = this.sideNearestToPoint(p);
  	        switch (side){
  	          case "right": return point(this.x + this.width, p.y);
  	          case "left": return point(this.x, p.y);
  	          case "bottom": return point(p.x, this.y + this.height);
  	          case "top": return point(p.x, this.y);
  	        }
  	    }
  	    return p.adhereToRect(this);
          },
          // Find point on my boundary where line starting
          // from my center ending in point p intersects me.
          // @param {number} angle If angle is specified, intersection with rotated rectangle is computed.
          intersectionWithLineFromCenterToPoint: function(p, angle) {
              p = point(p);
  	    var center = point(this.x + this.width/2, this.y + this.height/2);
              var result;
              if (angle) p.rotate(center, angle);
              
  	    // (clockwise, starting from the top side)
  	    var sides = [
  	        line(this.origin(), this.topRight()),
  	        line(this.topRight(), this.corner()),
  	        line(this.corner(), this.bottomLeft()),
  	        line(this.bottomLeft(), this.origin())
  	    ];
  	    var connector = line(center, p);
              
  	    for (var i = sides.length - 1; i >= 0; --i){
  	        var intersection = sides[i].intersection(connector);
  	        if (intersection !== null){
  		    result = intersection;
                      break;
  	        }
  	    }
              if (result && angle) result.rotate(center, -angle);
              return result;
          },
          // Move and expand me.
          // @param r {rectangle} representing deltas
          moveAndExpand: function(r) {
  	    this.x += r.x;
  	    this.y += r.y;
  	    this.width += r.width;
  	    this.height += r.height;
  	    return this;
          },
          round: function(decimals) {
              this.x = decimals ? this.x.toFixed(decimals) : round(this.x);
              this.y = decimals ? this.y.toFixed(decimals) : round(this.y);
              this.width = decimals ? this.width.toFixed(decimals) : round(this.width);
              this.height = decimals ? this.height.toFixed(decimals) : round(this.height);
              return this;
          }
      };
  
      // Ellipse.
      // --------
      function ellipse(c, a, b) {
          if (!(this instanceof ellipse))
              return new ellipse(c, a, b);
          c = point(c);
          this.x = c.x;
          this.y = c.y;
          this.a = a;
          this.b = b;
      }
  
      ellipse.prototype = {
          toString: function() {
              return point(this.x, this.y).toString() + ' ' + this.a + ' ' + this.b;
          },
          bbox: function() {
  	        return rect(this.x - this.a, this.y - this.b, 2*this.a, 2*this.b);
          },
          // Find point on me where line from my center to
          // point p intersects my boundary.
          // @param {number} angle If angle is specified, intersection with rotated ellipse is computed.
          intersectionWithLineFromCenterToPoint: function(p, angle) {
  	    p = point(p);
              if (angle) p.rotate(point(this.x, this.y), angle);
              var dx = p.x - this.x;
  	    var dy = p.y - this.y;
              var result;
  	    if (dx === 0) {
  	        result = this.bbox().pointNearestToPoint(p);
                  if (angle) return result.rotate(point(this.x, this.y), -angle);
                  return result;
  	    }
  	    var m = dy / dx;
  	    var mSquared = m * m;
  	    var aSquared = this.a * this.a;
  	    var bSquared = this.b * this.b;
  	    var x = sqrt(1 / ((1 / aSquared) + (mSquared / bSquared)));
  
              x = dx < 0 ? -x : x;
  	    var y = m * x;
  	    result = point(this.x + x, this.y + y);
              if (angle) return result.rotate(point(this.x, this.y), -angle);
              return result;
          }
      };
  
      // Bezier curve.
      // -------------
      var bezier = {
          // Cubic Bezier curve path through points.
          // Ported from C# implementation by Oleg V. Polikarpotchkin and Peter Lee (http://www.codeproject.com/KB/graphics/BezierSpline.aspx).
          // @param {array} points Array of points through which the smooth line will go.
          // @return {array} SVG Path commands as an array
          curveThroughPoints: function(points) {
              var controlPoints = this.getCurveControlPoints(points);
              var path = ['M', points[0].x, points[0].y];
  
              for (var i = 0; i < controlPoints[0].length; i++) {
                  path.push('C', controlPoints[0][i].x, controlPoints[0][i].y, controlPoints[1][i].x, controlPoints[1][i].y, points[i+1].x, points[i+1].y);        
              }
              return path;
          },
          
          // Get open-ended Bezier Spline Control Points.
          // @param knots Input Knot Bezier spline points (At least two points!).
          // @param firstControlPoints Output First Control points. Array of knots.length - 1 length.
          //  @param secondControlPoints Output Second Control points. Array of knots.length - 1 length.
          getCurveControlPoints: function(knots) {
              var firstControlPoints = [];
              var secondControlPoints = [];
              var n = knots.length - 1;
              var i;
  
              // Special case: Bezier curve should be a straight line.
              if (n == 1) { 
  	        // 3P1 = 2P0 + P3
  	        firstControlPoints[0] = point((2 * knots[0].x + knots[1].x) / 3,
  	                                      (2 * knots[0].y + knots[1].y) / 3);
  	        // P2 = 2P1 – P0
  	        secondControlPoints[0] = point(2 * firstControlPoints[0].x - knots[0].x,
  	                                       2 * firstControlPoints[0].y - knots[0].y);
  	        return [firstControlPoints, secondControlPoints];
              }
              
                  // Calculate first Bezier control points.
              // Right hand side vector.
              var rhs = [];
              
              // Set right hand side X values.
              for (i = 1; i < n - 1; i++) {
                  rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
              }
              rhs[0] = knots[0].x + 2 * knots[1].x;
              rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0;
              // Get first control points X-values.
              var x = this.getFirstControlPoints(rhs);
              
              // Set right hand side Y values.
              for (i = 1; i < n - 1; ++i) {
  	        rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
              }
              rhs[0] = knots[0].y + 2 * knots[1].y;
              rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0;
              // Get first control points Y-values.
              var y = this.getFirstControlPoints(rhs);
              
              // Fill output arrays.
              for (i = 0; i < n; i++) {
  	        // First control point.
  	        firstControlPoints.push(point(x[i], y[i]));
  	        // Second control point.
  	        if (i < n - 1) {
  	            secondControlPoints.push(point(2 * knots [i + 1].x - x[i + 1],
                                                     2 * knots[i + 1].y - y[i + 1]));
  	        } else {
  	            secondControlPoints.push(point((knots[n].x + x[n - 1]) / 2,
  					           (knots[n].y + y[n - 1]) / 2));
  	        }
              }
              return [firstControlPoints, secondControlPoints];
          },
  
          // Solves a tridiagonal system for one of coordinates (x or y) of first Bezier control points.
          // @param rhs Right hand side vector.
          // @return Solution vector.
          getFirstControlPoints: function(rhs) {
              var n = rhs.length;
              // `x` is a solution vector.
              var x = [];
              var tmp = [];
              var b = 2.0;
              
              x[0] = rhs[0] / b;
              // Decomposition and forward substitution.
              for (var i = 1; i < n; i++) { 
  	        tmp[i] = 1 / b;
  	        b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
  	        x[i] = (rhs[i] - x[i - 1]) / b;
              }
              for (i = 1; i < n; i++) {
                  // Backsubstitution.
  	        x[n - i - 1] -= tmp[n - i] * x[n - i]; 
              }
              return x;
          }
      };
  
      return {
  
          toDeg: toDeg,
          toRad: toRad,
          snapToGrid: snapToGrid,
  	normalizeAngle: normalizeAngle,
          point: point,
          line: line,
          rect: rect,
          ellipse: ellipse,
          bezier: bezier
      }
  }));
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS, the JavaScript diagramming library.
  //      (c) 2011-2013 client IO
  
  
  joint.dia.GraphCells = Backbone.Collection.extend({
  
      initialize: function() {
          
          // Backbone automatically doesn't trigger re-sort if models attributes are changed later when
          // they're already in the collection. Therefore, we're triggering sort manually here.
          this.on('change:z', this.sort, this);
      },
  
      model: function(attrs, options) {
  
          if (attrs.type === 'link') {
  
              return new joint.dia.Link(attrs, options);
          }
  
          var module = attrs.type.split('.')[0];
          var entity = attrs.type.split('.')[1];
  
          if (joint.shapes[module] && joint.shapes[module][entity]) {
  
              return new joint.shapes[module][entity](attrs, options);
          }
          
          return new joint.dia.Element(attrs, options);
      },
  
      // `comparator` makes it easy to sort cells based on their `z` index.
      comparator: function(model) {
  
          return model.get('z') || 0;
      },
  
      // Get all inbound and outbound links connected to the cell `model`.
      getConnectedLinks: function(model, opt) {
  
          opt = opt || {};
  
          if (_.isUndefined(opt.inbound) && _.isUndefined(opt.outbound)) {
              opt.inbound = opt.outbound = true;
          }
  
          var links = [];
          
          this.each(function(cell) {
  
              var source = cell.get('source');
              var target = cell.get('target');
  
              if (source && source.id === model.id && opt.outbound) {
                  
                  links.push(cell);
              }
  
              if (target && target.id === model.id && opt.inbound) {
  
                  links.push(cell);
              }
          });
  
          return links;
      }
  });
  
  
  joint.dia.Graph = Backbone.Model.extend({
  
      initialize: function() {
  
          this.set('cells', new joint.dia.GraphCells);
  
          // Make all the events fired in the `cells` collection available.
          // to the outside world.
          this.get('cells').on('all', this.trigger, this);
          
          this.get('cells').on('remove', this.removeCell, this);
      },
  
      fromJSON: function(json) {
  
          if (!json.cells) {
  
              throw new Error('Graph JSON must contain cells array.');
          }
  
          var attrs = json;
  
          // Cells are the only attribute that is being set differently, using `cells.add()`.
          var cells = json.cells;
          delete attrs.cells;
          
          this.set(attrs);
          
          this.resetCells(cells);
      },
  
      clear: function() {
  
          this.get('cells').remove(this.get('cells').models);
      },
  
      _prepareCell: function(cell) {
  
          if (cell instanceof Backbone.Model && _.isUndefined(cell.get('z'))) {
  
              cell.set('z', this.get('cells').length, { silent: true });
              
          } else if (_.isUndefined(cell.z)) {
  
              cell.z = this.get('cells').length;
          }
  
          return cell;
      },
      
      addCell: function(cell, options) {
  
          if (_.isArray(cell)) {
  
              return this.addCells(cell, options);
          }
  
          this.get('cells').add(this._prepareCell(cell), options || {});
  
          return this;
      },
  
      addCells: function(cells, options) {
  
          _.each(cells, function(cell) { this.addCell(this._prepareCell(cell), options || {}); }, this);
  
          return this;
      },
  
      // When adding a lot of cells, it is much more efficient to
      // reset the entire cells collection in one go.
      // Useful for bulk operations and optimizations.
      resetCells: function(cells) {
          
          this.get('cells').reset(_.map(cells, this._prepareCell, this));
  
          return this;
      },
  
      removeCell: function(cell, collection, options) {
  
          // Applications might provide a `disconnectLinks` option set to `true` in order to
          // disconnect links when a cell is removed rather then removing them. The default
          // is to remove all the associated links.
          if (options && options.disconnectLinks) {
              
              this.disconnectLinks(cell);
  
          } else {
  
              this.removeLinks(cell);
          }
  
          // Silently remove the cell from the cells collection. Silently, because
          // `joint.dia.Cell.prototype.remove` already triggers the `remove` event which is
          // then propagated to the graph model. If we didn't remove the cell silently, two `remove` events
          // would be triggered on the graph model.
          this.get('cells').remove(cell, { silent: true });
      },
  
      // Get a cell by `id`.
      getCell: function(id) {
  
          return this.get('cells').get(id);
      },
  
      getElements: function() {
  
          return this.get('cells').filter(function(cell) {
  
              return cell instanceof joint.dia.Element;
          });
      },
      
      getLinks: function() {
  
          return this.get('cells').filter(function(cell) {
  
              return cell instanceof joint.dia.Link;
          });
      },
  
      // Get all inbound and outbound links connected to the cell `model`.
      getConnectedLinks: function(model, opt) {
  
          return this.get('cells').getConnectedLinks(model, opt);
      },
  
      getNeighbors: function(el) {
  
          var links = this.getConnectedLinks(el);
          var neighbors = [];
          var cells = this.get('cells');
          
          _.each(links, function(link) {
  
              var source = link.get('source');
              var target = link.get('target');
  
              // Discard if it is a point.
              if (!source.x) {
                  var sourceElement = cells.get(source.id);
                  if (sourceElement !== el) {
  
                      neighbors.push(sourceElement);
                  }
              }
              if (!target.x) {
                  var targetElement = cells.get(target.id);
                  if (targetElement !== el) {
  
                      neighbors.push(targetElement);
                  }
              }
          });
  
          return neighbors;
      },
      
      // Disconnect links connected to the cell `model`.
      disconnectLinks: function(model) {
  
          _.each(this.getConnectedLinks(model), function(link) {
  
              link.set(link.get('source').id === model.id ? 'source' : 'target', g.point(0, 0));
          });
      },
  
      // Remove links connected to the cell `model` completely.
      removeLinks: function(model) {
  
          _.invoke(this.getConnectedLinks(model), 'remove');
      }
  });
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS.
  //      (c) 2011-2013 client IO
  
  
  // joint.dia.Cell base model.
  // --------------------------
  
  joint.dia.Cell = Backbone.Model.extend({
  
      // This is the same as Backbone.Model with the only difference that is uses _.deepExtend
      // instead of just _.extend. The reason is that we want to mixin attributes set in upper classes.
      constructor: function(attributes, options) {
  
          var defaults;
          var attrs = attributes || {};
          this.cid = _.uniqueId('c');
          this.attributes = {};
          if (options && options.collection) this.collection = options.collection;
          if (options && options.parse) attrs = this.parse(attrs, options) || {};
          if (defaults = _.result(this, 'defaults')) {
              //<custom code>
              // Replaced the call to _.defaults with _.deepExtend.
              attrs = _.deepExtend({}, defaults, attrs);
              //</custom code>
          }
          this.set(attrs, options);
          this.changed = {};
          this.initialize.apply(this, arguments);
      },
  
      toJSON: function() {
  
          var defaultAttrs = this.constructor.prototype.defaults.attrs || {};
          var attrs = this.attributes.attrs;
          var finalAttrs = {};
  
          // Loop through all the attributes and
          // omit the default attributes as they are implicitly reconstructable by the cell 'type'.
          _.each(attrs, function(attr, selector) {
  
              var defaultAttr = defaultAttrs[selector];
              
              _.each(attr, function(value, name) {
                  
                  // attr is mainly flat though it might have one more level (consider the `style` attribute).
                  // Check if the `value` is object and if yes, go one level deep.
                  if (_.isObject(value) && !_.isArray(value)) {
                      
                      _.each(value, function(value2, name2) {
  
                          if (!defaultAttr || !defaultAttr[name] || !_.isEqual(defaultAttr[name][name2], value2)) {
  
                              finalAttrs[selector] = finalAttrs[selector] || {};
                              (finalAttrs[selector][name] || (finalAttrs[selector][name] = {}))[name2] = value2;
                          }
                      });
  
                  } else if (!defaultAttr || !_.isEqual(defaultAttr[name], value)) {
                      // `value` is not an object, default attribute for such a selector does not exist
                      // or it is different than the attribute value set on the model.
  
                      finalAttrs[selector] = finalAttrs[selector] || {};
                      finalAttrs[selector][name] = value;
                  }
              });
          });
  
          var attributes = _.deepClone(_.omit(this.attributes, 'attrs'));
          attributes.attrs = finalAttrs;
          
          return attributes;
      },
  
      initialize: function(options) {
  
          if (!options || !options.id) {
  
              this.set('id', joint.util.uuid(), { silent: true });
          }
      },
  
      remove: function(options) {
  
  	var collection = this.collection;
  
  	if (collection) {
  	    collection.trigger('batch:start');
  	}
  
          // First, unembed this cell from its parent cell if there is one.
          var parentCellId = this.get('parent');
          if (parentCellId) {
              
              var parentCell = this.collection && this.collection.get(parentCellId);
              parentCell.unembed(this);
          }
          
          _.invoke(this.getEmbeddedCells(), 'remove', options);
          
          this.trigger('remove', this, this.collection, options);
  
  	if (collection) {
  	    collection.trigger('batch:stop');
  	}
      },
  
      toFront: function() {
  
          if (this.collection) {
  
              this.set('z', (this.collection.last().get('z') || 0) + 1);
          }
      },
      
      toBack: function() {
  
          if (this.collection) {
              
              this.set('z', (this.collection.first().get('z') || 0) - 1);
          }
      },
  
      embed: function(cell) {
  
  	this.trigger('batch:start');
  
          var cellId = cell.id;
          cell.set('parent', this.id);
  
          this.set('embeds', _.uniq((this.get('embeds') || []).concat([cellId])));
  
  	this.trigger('batch:stop');
      },
  
      unembed: function(cell) {
  
  	this.trigger('batch:start');
  
          var cellId = cell.id;
          cell.unset('parent');
  
          this.set('embeds', _.without(this.get('embeds'), cellId));
  
  	this.trigger('batch:stop');
      },
  
      getEmbeddedCells: function() {
  
          // Cell models can only be retrieved when this element is part of a collection.
          // There is no way this element knows about other cells otherwise.
          // This also means that calling e.g. `translate()` on an element with embeds before
          // adding it to a graph does not translate its embeds.
          if (this.collection) {
  
              return _.map(this.get('embeds') || [], function(cellId) {
  
                  return this.collection.get(cellId);
                  
              }, this);
          }
          return [];
      },
  
      clone: function(opt) {
  
          opt = opt || {};
  
          var clone = Backbone.Model.prototype.clone.apply(this, arguments);
          
          // We don't want the clone to have the same ID as the original.
          clone.set('id', joint.util.uuid(), { silent: true });
          clone.set('embeds', '');
  
          if (!opt.deep) return clone;
  
          // The rest of the `clone()` method deals with embeds. If `deep` option is set to `true`,
          // the return value is an array of all the embedded clones created.
  
          var embeds = this.getEmbeddedCells();
  
          var clones = [clone];
  
          // This mapping stores cloned links under the `id`s of they originals.
          // This prevents cloning a link more then once. Consider a link 'self loop' for example.
          var linkCloneMapping = {};
          
          _.each(embeds, function(embed) {
  
              var embedClones = embed.clone({ deep: true });
  
              // Embed the first clone returned from `clone({ deep: true })` above. The first
              // cell is always the clone of the cell that called the `clone()` method, i.e. clone of `embed` in this case.
              clone.embed(embedClones[0]);
  
              _.each(embedClones, function(embedClone) {
  
                  clones.push(embedClone);
  
                  // Skip links. Inbound/outbound links are not relevant for them.
                  if (embedClone instanceof joint.dia.Link) {
  
                      return;
                  }
  
                  // Collect all inbound links, clone them (if not done already) and set their target to the `embedClone.id`.
                  var inboundLinks = this.collection.getConnectedLinks(embed, { inbound: true });
  
                  _.each(inboundLinks, function(link) {
  
                      var linkClone = linkCloneMapping[link.id] || link.clone();
  
                      // Make sure we don't clone a link more then once.
                      linkCloneMapping[link.id] = linkClone;
  
                      var target = _.clone(linkClone.get('target'));
                      target.id = embedClone.id;
                      linkClone.set('target', target);
                  });
  
                  // Collect all inbound links, clone them (if not done already) and set their source to the `embedClone.id`.
                  var outboundLinks = this.collection.getConnectedLinks(embed, { outbound: true });
  
                  _.each(outboundLinks, function(link) {
  
                      var linkClone = linkCloneMapping[link.id] || link.clone();
  
                      // Make sure we don't clone a link more then once.
                      linkCloneMapping[link.id] = linkClone;
  
                      var source = _.clone(linkClone.get('source'));
                      source.id = embedClone.id;
                      linkClone.set('source', source);
                  });
  
              }, this);
              
          }, this);
  
          // Add link clones to the array of all the new clones.
          clones = clones.concat(_.values(linkCloneMapping));
  
          return clones;
      },
  
      // A convenient way to set nested attributes.
      attr: function(attrs, value) {
  
          var currentAttrs = this.get('attrs');
          var delim = '/';
          
          if (_.isString(attrs)) {
              // Get/set an attribute by a special path syntax that delimits
              // nested objects by the colon character.
  
              if (value) {
  
                  var attr = {};
                  joint.util.setByPath(attr, attrs, value, delim);
                  return this.set('attrs', _.deepExtend({}, currentAttrs, attr));
                  
              } else {
                  
                  return joint.util.getByPath(currentAttrs, attrs, delim);
              }
          }
          
          return this.set('attrs', _.deepExtend({}, currentAttrs, attrs));
      }
  });
  
  // joint.dia.CellView base view and controller.
  // --------------------------------------------
  
  // This is the base view and controller for `joint.dia.ElementView` and `joint.dia.LinkView`.
  
  joint.dia.CellView = Backbone.View.extend({
  
      initialize: function() {
  
          _.bindAll(this, 'remove', 'update');
  
          // Store reference to this to the <g> DOM element so that the view is accessible through the DOM tree.
          this.$el.data('view', this);
  
          this.model.on({
  
              'remove': this.remove,
              'change:attrs': this.update
          });
      },
  
      _configure: function(options) {
  
          // Make sure a global unique id is assigned to this view. Store this id also to the properties object.
          // The global unique id makes sure that the same view can be rendered on e.g. different machines and
          // still be associated to the same object among all those clients. This is necessary for real-time
          // collaboration mechanism.
          options.id = options.id || joint.util.guid(this);
          
          Backbone.View.prototype._configure.apply(this, arguments);
      },
  
      // Override the Backbone `_ensureElement()` method in order to create a `<g>` node that wraps
      // all the nodes of the Cell view.
      _ensureElement: function() {
  
          if (!this.el) {
  
              this.el = V('g', { id: this.id }).node;
          }
              
          this.setElement(this.el, false);
      },
      
      findBySelector: function(selector) {
  
          // These are either descendants of `this.$el` of `this.$el` itself. 
         // `.` is a special selector used to select the wrapping `<g>` element.
          var $selected = selector === '.' ? this.$el : this.$el.find(selector);
          return $selected;
      },
  
      notify: function(evt) {
  
          if (this.paper) {
  
              var args = Array.prototype.slice.call(arguments, 1);
  
              // Trigger the event on both the element itself and also on the paper.
              this.trigger.apply(this, [evt].concat(args));
              
              // Paper event handlers receive the view object as the first argument.
              this.paper.trigger.apply(this.paper, [evt, this].concat(args));
          }
      },
  
      getStrokeBBox: function(el) {
          // Return a bounding box rectangle that takes into account stroke.
          // Note that this is a naive and ad-hoc implementation that does not
          // works only in certain cases and should be replaced as soon as browsers will
          // start supporting the getStrokeBBox() SVG method.
          // @TODO any better solution is very welcome!
  
          var isMagnet = !!el;
          
          el = el || this.el;
          var bbox = V(el).bbox(false, this.paper.viewport);
  
          var strokeWidth;
          if (isMagnet) {
  
              strokeWidth = V(el).attr('stroke-width');
              
          } else {
  
              strokeWidth = this.model.attr('rect/stroke-width') || this.model.attr('circle/stroke-width') || this.model.attr('ellipse/stroke-width') || this.model.attr('path/stroke-width');
          }
  
          strokeWidth = parseFloat(strokeWidth) || 0;
  
          return g.rect(bbox).moveAndExpand({ x: -strokeWidth/2, y: -strokeWidth/2, width: strokeWidth, height: strokeWidth });
      },
      
      getBBox: function() {
  
          return V(this.el).bbox();
      },
  
      highlight: function(el) {
  
          el = !el ? this.el : this.$(el)[0] || this.el;
  
  	var attrClass = V(el).attr('class') || '';
  
  	if (!/\bhighlighted\b/.exec(attrClass)) V(el).attr('class', attrClass.trim() + ' highlighted');
      },
  
      unhighlight: function(el) {
  
          el = !el ? this.el : this.$(el)[0] || this.el;
  
  	V(el).attr('class', (V(el).attr('class') || '').replace(/\bhighlighted\b/, '').trim());
      },
  
      // Find the closest element that has the `magnet` attribute set to `true`. If there was not such
      // an element found, return the root element of the cell view.
      findMagnet: function(el) {
  
          var $el = this.$(el);
  
          if ($el.length === 0 || $el[0] === this.el) {
  
              // If the overall cell has set `magnet === false`, then return `undefined` to
              // announce there is no magnet found for this cell.
              // This is especially useful to set on cells that have 'ports'. In this case,
              // only the ports have set `magnet === true` and the overall element has `magnet === false`.
              var attrs = this.model.get('attrs');
              if (attrs['.'] && attrs['.']['magnet'] === false) {
  
                  return undefined;
              }
              return this.el;
          }
  
          if ($el.attr('magnet')) {
  
              return $el[0];
          }
  
          return this.findMagnet($el.parent());
      },
  
      // Construct a unique selector for the `el` element within this view.
      // `selector` is being collected through the recursive call. No value for `selector` is expected when using this method.
      getSelector: function(el, selector) {
  
          if (el === this.el) {
  
              return selector;
          }
  
          var index = $(el).index();
  
          selector = el.tagName + ':nth-child(' + (index + 1) + ')' + ' ' + (selector || '');
  
          return this.getSelector($(el).parent()[0], selector + ' ');
      },
  
      // Interaction. The controller part.
      // ---------------------------------
  
      // Interaction is handled by the paper and delegated to the view in interest.
      // `x` & `y` parameters passed to these functions represent the coordinates already snapped to the paper grid.
      // If necessary, real coordinates can be obtained from the `evt` event object.
  
      // These functions are supposed to be overriden by the views that inherit from `joint.dia.Cell`,
      // i.e. `joint.dia.Element` and `joint.dia.Link`.
  
      pointerdown: function(evt, x, y) {
  
  	if (this.model.collection) {
  	    this.model.trigger('batch:start');
  	    this._collection = this.model.collection;
  	}
  
          this.notify('cell:pointerdown', evt, x, y);
      },
      
      pointermove: function(evt, x, y) {
  
          this.notify('cell:pointermove', evt, x, y);
      },
      
      pointerup: function(evt, x, y) {
  
          this.notify('cell:pointerup', evt, x, y);
  
  	if (this._collection) {
  	    // we don't want to trigger event on model as model doesn't
  	    // need to be member of collection anymore (remove)
  	    this._collection.trigger('batch:stop');
  	    delete this._collection;
  	}
  
      }
  });
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS library.
  //      (c) 2011-2013 client IO
  
  
  // joint.dia.Element base model.
  // -----------------------------
  
  joint.dia.Element = joint.dia.Cell.extend({
  
      defaults: {
  	size: { width: 1, height: 1 }
      },
  
      position: function(x, y) {
  
          this.set('position', { x: x, y: y });
      },
      
      translate: function(tx, ty, opt) {
  
          ty = ty || 0;
  
          if (tx === 0 && ty === 0) {
              // Like nothing has happened.
              return this;
          }
  
          var position = this.get('position') || { x: 0, y: 0 };
          
          this.set('position', {
  
              x: position.x + tx || 0,
              y: position.y + ty || 0
              
          }, opt);
  
          // Recursively call `translate()` on all the embeds cells.
          _.invoke(this.getEmbeddedCells(), 'translate', tx, ty);
  
          return this;
      },
  
      resize: function(width, height) {
  
  	this.trigger('batch:start');
          this.set('size', { width: width, height: height });
  	this.trigger('batch:stop');
  
  	return this;
      },
  
      rotate: function(angle, absolute) {
  
          return this.set('angle', absolute ? angle : ((this.get('angle') || 0) + angle) % 360);
      },
  
      getBBox: function() {
  
  	var position = this.get('position');
  	var size = this.get('size');
  
  	return g.rect(position.x, position.y, size.width, size.height);
      }
  });
  
  // joint.dia.Element base view and controller.
  // -------------------------------------------
  
  joint.dia.ElementView = joint.dia.CellView.extend({
      
      initialize: function() {
  
          _.bindAll(this, 'translate', 'resize', 'rotate');
  
          joint.dia.CellView.prototype.initialize.apply(this, arguments);
          
          // Assign CSS class to the element based on the element type.
          V(this.el).attr({
              
              'class': 'element ' + this.model.get('type').split('.').join(' '),
              'model-id': this.model.id
          });
  
          this.model.on({
  
              'change:position': this.translate,
              'change:size': this.resize,
              'change:angle': this.rotate
          });
      },
  
      // Default is to process the `attrs` object and set attributes on subelements based on the selectors.
      update: function(cell, renderingOnlyAttrs) {
  
          var rotatable = V(this.$('.rotatable')[0]);
          if (rotatable) {
  
              var rotation = rotatable.attr('transform');
              rotatable.attr('transform', '');
          }
          
          var relativelyPositioned = [];
  
          var attrs = renderingOnlyAttrs || this.model.get('attrs');
          
          _.each(attrs, function(attrs, selector) {
  
              // Elements that should be updated.
              var $selected = this.findBySelector(selector);
  
              // No element matched by the `selector` was found. We're done then.
              if ($selected.length === 0) return;
  
              // Special attributes are treated by JointJS, not by SVG.
              var specialAttributes = ['style', 'text', 'ref-x', 'ref-y', 'ref-dx', 'ref-dy', 'ref', 'x-alignment', 'y-alignment'];
  
              // Set regular attributes on the `$selected` subelement. Note that we cannot use the jQuery attr()
              // method as some of the attributes might be namespaced (e.g. xlink:href) which fails with jQuery attr().
              var finalAttributes = _.omit(attrs, specialAttributes);
              $selected.each(function() {
                  
                  V(this).attr(finalAttributes);
              });
  
              // `style` attribute is special in the sense that it sets the CSS style of the subelement.
              if (attrs.style) {
  
                  $selected.css(attrs.style);
              }
              
              // Make special case for `text` attribute. So that we can set text content of the `<text>` element
              // via the `attrs` object as well.
              if (!_.isUndefined(attrs.text)) {
  
                  $selected.each(function() {
  
                      V(this).text(attrs.text + '');
                  });
              }
  
              // Special `ref-x` and `ref-y` attributes make it possible to set both absolute or
              // relative positioning of subelements.
              if (!_.isUndefined(attrs['ref-x']) ||
                  !_.isUndefined(attrs['ref-y']) ||
                  !_.isUndefined(attrs['ref-dx']) ||
                  !_.isUndefined(attrs['ref-dy']) ||
  		!_.isUndefined(attrs['x-alignment']) ||
  		!_.isUndefined(attrs['y-alignment'])
                 ) {
  
                  relativelyPositioned.push($selected);
              }
              
          }, this);
  
          // We don't want the sub elements to affect the bounding box of the root element when
          // positioning the sub elements relatively to the bounding box.
          //_.invoke(relativelyPositioned, 'hide');
          //_.invoke(relativelyPositioned, 'show');
  
          // Note that we're using the bounding box without transformation because we are already inside
          // a transformed coordinate system.
          var bbox = this.el.getBBox();        
  
          _.each(relativelyPositioned, function($el) {
  
              this.positionRelative($el, bbox, attrs);
              
          }, this);
  
          if (rotatable) {
  
              rotatable.attr('transform', rotation || '');
          }
      },
  
      positionRelative: function($el, bbox, attrs) {
  
          var elAttrs = attrs[$el.selector];
          
          var refX = parseFloat(elAttrs['ref-x']);
          var refY = parseFloat(elAttrs['ref-y']);
          var refDx = parseFloat(elAttrs['ref-dx']);
          var refDy = parseFloat(elAttrs['ref-dy']);
          var yAlignment = elAttrs['y-alignment'];
          var xAlignment = elAttrs['x-alignment'];
  
          // `ref` is the selector of the reference element. If no `ref` is passed, reference
          // element is the root element.
  
          var isScalable = _.contains(_.pluck(_.pluck($el.parents('g'), 'className'), 'baseVal'), 'scalable');
  
          var ref = elAttrs['ref'];
          if (ref) {
  
              // Get the bounding box of the reference element relative to the root `<g>` element.
              bbox = V(this.findBySelector(ref)[0]).bbox(false, this.el);
          }
  
          var vel = V($el[0]);
  
          // Remove the previous translate() from the transform attribute and translate the element
          // relative to the root bounding box following the `ref-x` and `ref-y` attributes.
          if (vel.attr('transform')) {
  
              vel.attr('transform', vel.attr('transform').replace(/translate\([^)]*\)/g, '') || '');
          }
  
          function isDefined(x) {
              return _.isNumber(x) && !_.isNaN(x);
          }
  
          // The final translation of the subelement.
          var tx = 0;
          var ty = 0;
  
          // `ref-dx` and `ref-dy` define the offset of the subelement relative to the right and/or bottom
          // coordinate of the reference element.
          if (isDefined(refDx)) {
  
              if (isScalable) {
  
                  // Compensate for the scale grid in case the elemnt is in the scalable group.
                  var scale = V(this.$('.scalable')[0]).scale();
                  tx = bbox.x + bbox.width + refDx / scale.sx;
                  
              } else {
                  
                  tx = bbox.x + bbox.width + refDx;
              }
          }
          if (isDefined(refDy)) {
  
              if (isScalable) {
                  
                  // Compensate for the scale grid in case the elemnt is in the scalable group.
                  var scale = V(this.$('.scalable')[0]).scale();
                  ty = bbox.y + bbox.height + refDy / scale.sy;
              } else {
                  
                  ty = bbox.y + bbox.height + refDy;
              }
          }
  
          // if `refX` is in [0, 1] then `refX` is a fraction of bounding box width
          // if `refX` is < 0 then `refX`'s absolute values is the right coordinate of the bounding box
          // otherwise, `refX` is the left coordinate of the bounding box
          // Analogical rules apply for `refY`.
          if (isDefined(refX)) {
  
              if (refX > 0 && refX < 1) {
  
                  tx = bbox.x + bbox.width * refX;
  
              } else if (isScalable) {
  
                  // Compensate for the scale grid in case the elemnt is in the scalable group.
                  var scale = V(this.$('.scalable')[0]).scale();
                  tx = bbox.x + refX / scale.sx;
                  
              } else {
  
                  tx = bbox.x + refX;
              }
          }
          if (isDefined(refY)) {
  
              if (refY > 0 && refY < 1) {
                  
                  ty = bbox.y + bbox.height * refY;
                  
              } else if (isScalable) {
  
                  // Compensate for the scale grid in case the elemnt is in the scalable group.
                  var scale = V(this.$('.scalable')[0]).scale();
                  ty = bbox.y + refY / scale.sy;
                  
              } else {
  
                  ty = bbox.y + refY;
              }
          }
  
  	var velbbox = vel.bbox(false, this.paper.viewport);
          // `y-alignment` when set to `middle` causes centering of the subelement around its new y coordinate.
          if (yAlignment === 'middle') {
  
              ty -= velbbox.height/2;
              
          } else if (isDefined(yAlignment)) {
  
              ty += (yAlignment > 0 && yAlignment < 1) ?  velbbox.height * yAlignment : yAlignment;
          }
  
          // `x-alignment` when set to `middle` causes centering of the subelement around its new x coordinate.
          if (xAlignment === 'middle') {
              
              tx -= velbbox.width/2;
              
          } else if (isDefined(xAlignment)) {
  
              tx += (xAlignment > 0 && xAlignment < 1) ?  velbbox.width * xAlignment : xAlignment;
          }
  
          vel.translate(tx, ty);
      },
  
      // `prototype.markup` is rendered by default. Set the `markup` attribute on the model if the
      // default markup is not desirable.
      render: function() {
  
          var markup = this.model.markup || this.model.get('markup');
          
          if (markup) {
  
              var nodes = V(markup);
              V(this.el).append(nodes);
              
          } else {
  
              throw new Error('properties.markup is missing while the default render() implementation is used.');
          }
  
          this.update();
  
          this.resize();
          this.rotate();
          this.translate();        
  
          return this;
      },
  
      // Scale the whole `<g>` group. Note the difference between `scale()` and `resize()` here.
      // `resize()` doesn't scale the whole `<g>` group but rather adjusts the `box.sx`/`box.sy` only.
      // `update()` is then responsible for scaling only those elements that have the `follow-scale`
      // attribute set to `true`. This is desirable in elements that have e.g. a `<text>` subelement
      // that is not supposed to be scaled together with a surrounding `<rect>` element that IS supposed
      // be be scaled.
      scale: function(sx, sy) {
  
          // TODO: take into account the origin coordinates `ox` and `oy`.
          V(this.el).scale(sx, sy);
      },
  
      resize: function() {
  
          var size = this.model.get('size') || { width: 1, height: 1 };
          var angle = this.model.get('angle') || 0;
          
          var scalable = V(this.$('.scalable')[0]);
          if (!scalable) {
              // If there is no scalable elements, than there is nothing to resize.
              return;
          }
          var scalableBbox = scalable.bbox(true);
          
          scalable.attr('transform', 'scale(' + (size.width / scalableBbox.width) + ',' + (size.height / scalableBbox.height) + ')');
  
          // Now the interesting part. The goal is to be able to store the object geometry via just `x`, `y`, `angle`, `width` and `height`
          // Order of transformations is significant but we want to reconstruct the object always in the order:
          // resize(), rotate(), translate() no matter of how the object was transformed. For that to work,
          // we must adjust the `x` and `y` coordinates of the object whenever we resize it (because the origin of the
          // rotation changes). The new `x` and `y` coordinates are computed by canceling the previous rotation
          // around the center of the resized object (which is a different origin then the origin of the previous rotation)
          // and getting the top-left corner of the resulting object. Then we clean up the rotation back to what it originally was.
          
          // Cancel the rotation but now around a different origin, which is the center of the scaled object.
          var rotatable = V(this.$('.rotatable')[0]);
          var rotation = rotatable && rotatable.attr('transform');
          if (rotation && rotation !== 'null') {
  
              rotatable.attr('transform', rotation + ' rotate(' + (-angle) + ',' + (size.width/2) + ',' + (size.height/2) + ')');
              var rotatableBbox = scalable.bbox(false, this.paper.viewport);
              
              // Store new x, y and perform rotate() again against the new rotation origin.
              this.model.set('position', { x: rotatableBbox.x, y: rotatableBbox.y });
              this.rotate();
          }
  
          // Update must always be called on non-rotated element. Otherwise, relative positioning
          // would work with wrong (rotated) bounding boxes.
          this.update();
      },
  
      translate: function(model, changes, opt) {
  
          var position = this.model.get('position') || { x: 0, y: 0 };
  
          if (opt && opt.animation) {
  
              var animateTransform = V('animateTransform', {
                  attributeName: 'transform',
                  attributeType: 'XML',
                  type: 'translate',
                  from: (this.model.previous('position').x || 0) + ' ' + (this.model.previous('position').y || 0),
                  to: position.x + ' ' + position.y,
                  dur: '100ms',
                  fill: 'freeze'
              });
              V(this.el).append(animateTransform);
              animateTransform.node.beginElement();
              
          } else {
  
              V(this.el).attr('transform', 'translate(' + position.x + ',' + position.y + ')');
          }
      },
  
      rotate: function() {
  
          var rotatable = V(this.$('.rotatable')[0]);
          if (!rotatable) {
              // If there is no rotatable elements, then there is nothing to rotate.
              return;
          }
          
          var angle = this.model.get('angle') || 0;
          var size = this.model.get('size') || { width: 1, height: 1 };
  
          var ox = size.width/2;
          var oy = size.height/2;
          
  
          rotatable.attr('transform', 'rotate(' + angle + ',' + ox + ',' + oy + ')');
      },
  
      // Interaction. The controller part.
      // ---------------------------------
  
      
      pointerdown: function(evt, x, y) {
  
          this._dx = x;
          this._dy = y;
  
          joint.dia.CellView.prototype.pointerdown.apply(this, arguments);
      },
  
      pointermove: function(evt, x, y) {
  
  	var grid = this.paper.options.gridSize;
          
          if (this.options.interactive !== false) {
  
  	    var position = this.model.get('position');
  
  	    // Make sure the new element's position always snaps to the current grid after
  	    // translate as the previous one could be calculated with a different grid size.
  	    this.model.translate(
  		g.snapToGrid(position.x, grid) - position.x + g.snapToGrid(x - this._dx, grid),
  		g.snapToGrid(position.y, grid) - position.y + g.snapToGrid(y - this._dy, grid)
  	    );
          }
          
          this._dx = g.snapToGrid(x, grid);
          this._dy = g.snapToGrid(y, grid);
  
          joint.dia.CellView.prototype.pointermove.apply(this, arguments);
      }
  });
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS diagramming library.
  //      (c) 2011-2013 client IO
  
  
  // joint.dia.Link base model.
  // --------------------------
  
  joint.dia.Link = joint.dia.Cell.extend({
  
      defaults: {
  
          type: 'link'
      },
  
      disconnect: function() {
  
          return this.set({ source: g.point(0, 0), target: g.point(0, 0) });
      },
  
      // A convenient way to set labels. Currently set values will be mixined with `value` if used as a setter.
      label: function(idx, value) {
  
          idx = idx || 0;
          
          var labels = this.get('labels');
          
          // Is it a getter?
          if (arguments.length === 0 || arguments.length === 1) {
              
              return labels && labels[idx];
          }
  
          var newValue = _.deepExtend({}, labels[idx], value);
  
          var newLabels = labels.slice();
          newLabels[idx] = newValue;
          
          return this.set({ labels: newLabels });
      }
  });
  
  
  // joint.dia.Link base view and controller.
  // ----------------------------------------
  
  joint.dia.LinkView = joint.dia.CellView.extend({
  
      // The default markup for links.
      markup: [
          '<path class="connection" stroke="black"/>',
          '<path class="marker-source" fill="black" stroke="black" />',
          '<path class="marker-target" fill="black" stroke="black" />',
          '<path class="connection-wrap"/>',
          '<g class="labels" />',
          '<g class="marker-vertices"/>',
          '<g class="marker-arrowheads"/>',
          '<g class="link-tools" />'
      ].join(''),
  
      labelMarkup: [
          '<g class="label">',
          '<rect />',
          '<text />',
          '</g>'
      ].join(''),
      
      toolMarkup: [
          '<g class="link-tool">',
          '<g class="tool-remove" event="remove">',
            '<circle r="11" />',
            '<path transform="scale(.8) translate(-16, -16)" d="M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z"/>',
            '<title>Remove link.</title>',
          '</g>',
          '<g class="tool-options" event="link:options">',
            '<circle r="11" transform="translate(25)"/>',
            '<path fill="white" transform="scale(.55) translate(29, -16)" d="M31.229,17.736c0.064-0.571,0.104-1.148,0.104-1.736s-0.04-1.166-0.104-1.737l-4.377-1.557c-0.218-0.716-0.504-1.401-0.851-2.05l1.993-4.192c-0.725-0.91-1.549-1.734-2.458-2.459l-4.193,1.994c-0.647-0.347-1.334-0.632-2.049-0.849l-1.558-4.378C17.165,0.708,16.588,0.667,16,0.667s-1.166,0.041-1.737,0.105L12.707,5.15c-0.716,0.217-1.401,0.502-2.05,0.849L6.464,4.005C5.554,4.73,4.73,5.554,4.005,6.464l1.994,4.192c-0.347,0.648-0.632,1.334-0.849,2.05l-4.378,1.557C0.708,14.834,0.667,15.412,0.667,16s0.041,1.165,0.105,1.736l4.378,1.558c0.217,0.715,0.502,1.401,0.849,2.049l-1.994,4.193c0.725,0.909,1.549,1.733,2.459,2.458l4.192-1.993c0.648,0.347,1.334,0.633,2.05,0.851l1.557,4.377c0.571,0.064,1.148,0.104,1.737,0.104c0.588,0,1.165-0.04,1.736-0.104l1.558-4.377c0.715-0.218,1.399-0.504,2.049-0.851l4.193,1.993c0.909-0.725,1.733-1.549,2.458-2.458l-1.993-4.193c0.347-0.647,0.633-1.334,0.851-2.049L31.229,17.736zM16,20.871c-2.69,0-4.872-2.182-4.872-4.871c0-2.69,2.182-4.872,4.872-4.872c2.689,0,4.871,2.182,4.871,4.872C20.871,18.689,18.689,20.871,16,20.871z"/>',
            '<title>Link options.</title>',
            '</g>',
          '</g>'
      ].join(''),
  
      // The default markup for showing/removing vertices. These elements are the children of the .marker-vertices element (see `this.markup`).
      // Only .marker-vertex and .marker-vertex-remove element have special meaning. The former is used for
      // dragging vertices (changin their position). The latter is used for removing vertices.
      vertexMarkup: [
          '<g class="marker-vertex-group" transform="translate(<%= x %>, <%= y %>)">',
          '<circle class="marker-vertex" idx="<%= idx %>" r="10"/>',
          '<path class="marker-vertex-remove-area" idx="<%= idx %>" d="M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z" transform="translate(5, -33)"/>',
          '<path class="marker-vertex-remove" idx="<%= idx %>" transform="scale(.8) translate(9.5, -37)" d="M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z">',
          '<title>Remove vertex.</title>',
          '</path>',
          '</g>'
      ].join(''),
  
      arrowheadMarkup: [
          '<g class="marker-arrowhead-group" transform="translate(<%= x %>, <%= y %>)">',
          '<path class="marker-arrowhead" end="<%= end %>" d="M 26 0 L 0 13 L 26 26 z" />',
          '</g>'
      ].join(''),
  
      options: {
  
          shortLinkLength: 100
      },
      
      initialize: function() {
  
          joint.dia.CellView.prototype.initialize.apply(this, arguments);
  
          _.bindAll(
              this,
              'update', 'updateEnds', 'render', 'renderVertexMarkers', 'renderLabels', 'renderTools',
              'onSourceModelChange', 'onTargetModelChange'
          );
          
          // Assign CSS class to the element based on the element type.
          V(this.el).attr({ 'class': 'link', 'model-id': this.model.id });
  
          this.model.on({
  
              'change:vertices change:smooth change:manhattan': this.update,
              'change:source change:target': this.updateEnds,
  	    'change:markup': this.render,
  	    'change:vertices change:vertexMarkup': this.renderVertexMarkers,
  	    'change:labels change:labelMarkup': _.bind(function() { this.renderLabels(); this.updateLabelPositions(); }, this),
              'change:toolMarkup': _.bind(function() { this.renderTools(); this.updateToolsPosition(); }, this)
          });
  
          // `_.labelCache` is a mapping of indexes of labels in the `this.get('labels')` array to
          // `<g class="label">` nodes wrapped by Vectorizer. This allows for quick access to the
          // nodes in `updateLabelPosition()` in order to update the label positions.
          this._labelCache = {};
  
          // These are the bounding boxes for the source/target elements.
          this._sourceBbox = undefined;
          this._targetBbox = undefined;
      },
  
      onSourceModelChange: function() {
  
          var source = this.model.get('source');
          
          if (!this._isPoint(source)) {
  
              var magnetEl = this.paper.$(this._makeSelector(source))[0];
              var cellView = this.paper.findView(magnetEl);
              this._sourceBbox = cellView.getStrokeBBox(source.selector ? magnetEl : undefined);
              
          } else {
  
              this._sourceBbox = _.extend({ width: 1, height: 1 }, source);
          }
      },
      onTargetModelChange: function() {
  
          var target = this.model.get('target');
          
          if (!this._isPoint(target)) {
  
              var magnetEl = this.paper.$(this._makeSelector(target))[0];
              var cellView = this.paper.findView(magnetEl);
              this._targetBbox = cellView.getStrokeBBox(target.selector ? magnetEl : undefined);
              
          } else {
  
              this._targetBbox = _.extend({ width: 1, height: 1 }, target);
          }
      },
  
      // Default is to process the `attrs` object and set attributes on subelements based on the selectors.
      update: function() {
  
          // Update attributes.
          _.each(this.model.get('attrs'), function(attrs, selector) {
  
              var $selected = this.findBySelector(selector);
  
              $selected.attr(attrs);
              
          }, this);
  
          var firstVertex = _.first(this.model.get('vertices'));
          var lastVertex = _.last(this.model.get('vertices'));
  
          // If manhattan routing is enabled, reference points for determining orientation of the arrowheads
          // might be different than vertices defined on the model.
          if (this.model.get('manhattan')) {
              
              var manhattanRoute = this.findManhattanRoute(this.model.get('vertices'));
              firstVertex = _.first(manhattanRoute);
              lastVertex = _.last(manhattanRoute);
          }
  
          var sourcePosition = this.getConnectionPoint('source', this.model.get('source'), firstVertex || this.model.get('target')).round();
          var targetPosition = this.getConnectionPoint('target', this.model.get('target'), lastVertex || sourcePosition);
  
          // Make the markers "point" to their sticky points being auto-oriented towards `targetPosition`/`sourcePosition`.
          this._markerSource.translateAndAutoOrient(sourcePosition, firstVertex || targetPosition, this.paper.viewport);
          this._markerTarget.translateAndAutoOrient(targetPosition, lastVertex || sourcePosition, this.paper.viewport);
  
          var pathData = this.getPathData(this.model.get('vertices'));
          this._connection.attr('d', pathData);
          this._connectionWrap.attr('d', pathData);
  
          this.renderArrowheadMarkers();
          
          this.updateLabelPositions();
  
          this.updateToolsPosition();
          
          return this;
      },
  
      render: function() {
  
          // A special markup can be given in the `properties.markup` property. This might be handy
          // if e.g. arrowhead markers should be `<image>` elements or any other element than `<path>`s.
          // `.connection`, `.connection-wrap`, `.marker-source` and `.marker-target` selectors
          // of elements with special meaning though. Therefore, those classes should be preserved in any
          // special markup passed in `properties.markup`.
          var markup = this.model.get('markup') || this.markup;
          
          var children = V(markup);
          
  	$(this.el).empty();
          V(this.el).append(children);
  
          // Cache some important elements for quicker access.
          this._markerSource = V(this.$('.marker-source')[0]);
          this._markerTarget = V(this.$('.marker-target')[0]);
          this._connection = V(this.$('.connection')[0]);
          this._connectionWrap = V(this.$('.connection-wrap')[0]);
  
          this.renderLabels();
          this.renderTools();
          
          // Note that `updateEnds()` calls `update()` internally.
          this.updateEnds();
  
          this.renderVertexMarkers();
  
          return this;
      },
  
      updateEnds: function() {
  
          this.onSourceModelChange();
          this.onTargetModelChange();
          
          var cell;
  
          // First, stop listening to `change` and `remove` events on previous `source` and `target` cells.
          if (this._isModel(this.model.previous('source'))) {
              
              cell = this.paper.getModelById(this.model.previous('source').id);
              this.stopListening(cell, 'change');
          }
          if (this._isModel(this.model.previous('target'))) {
              
              cell = this.paper.getModelById(this.model.previous('target').id);
              this.stopListening(cell, 'change');
          }
  
          // Listen on changes in `source` and `target` cells and update the link if any change happens.
          if (this._isModel(this.model.get('source'))) {
  
              cell = this.paper.getModelById(this.model.get('source').id);
              this.listenTo(cell, 'change', function() { this.onSourceModelChange(); this.update() });
          }
          if (this._isModel(this.model.get('target'))) {
  
              cell = this.paper.getModelById(this.model.get('target').id);
              this.listenTo(cell, 'change', function() { this.onTargetModelChange(); this.update() });
          }
  
          this.update();
      },
  
      updateLabelPositions: function() {
  
          // This method assumes all the label nodes are stored in the `this._labelCache` hash table
          // by their indexes in the `this.get('labels')` array. This is done in the `renderLabels()` method.
  
          var labels = this.model.get('labels') || [];
          if (!labels.length) return;
          
          var connectionElement = this._connection.node;
          var connectionLength = connectionElement.getTotalLength();
          
          _.each(labels, function(label, idx) {
  
              var position = label.position;
              position = (position > connectionLength) ? connectionLength : position; // sanity check
              position = (position < 0) ? connectionLength + position : position;
              position = position > 1 ? position : connectionLength * position;
  
              var labelCoordinates = connectionElement.getPointAtLength(position);
  
              this._labelCache[idx].attr('transform', 'translate(' + labelCoordinates.x + ', ' + labelCoordinates.y + ')');
              
          }, this);
      },
  
      renderLabels: function() {
  
          this._labelCache = {};
          var $labels = this.$('.labels').empty();
  
          var labels = this.model.get('labels') || [];
          if (!labels.length) return;
          
          var labelTemplate = _.template(this.model.get('labelMarkup') || this.labelMarkup);
          // This is a prepared instance of a vectorized SVGDOM node for the label element resulting from
          // compilation of the labelTemplate. The purpose is that all labels will just `clone()` this
          // node to create a duplicate.
          var labelNodeInstance = V(labelTemplate());
  
          _.each(labels, function(label, idx) {
  
              var labelNode = labelNodeInstance.clone().node;
              // Cache label nodes so that the `updateLabels()` can just update the label node positions.
              this._labelCache[idx] = V(labelNode);
  
              var $text = $(labelNode).find('text');
              var $rect = $(labelNode).find('rect');
  
              // Text attributes with the default `text-anchor` set.
              var textAttributes = _.extend({ 'text-anchor': 'middle' }, label.attrs.text);
              
              $text.attr(_.omit(textAttributes, 'text'));
                  
              if (!_.isUndefined(textAttributes.text)) {
  
                  V($text[0]).text(textAttributes.text + '');
              }
  
              // Note that we first need to append the `<text>` element to the DOM in order to
              // get its bounding box.
              $labels.append(labelNode);
  
              // `y-alignment` - center the text element around its y coordinate.
              var textBbox = V($text[0]).bbox(true, $labels[0]);
              V($text[0]).translate(0, -textBbox.height/2);
  
              // Add default values.
              var rectAttributes = _.extend({
  
                  fill: 'white',
                  rx: 3,
                  ry: 3
                  
              }, label.attrs.rect);
              
              $rect.attr(_.extend(rectAttributes, {
  
                  x: textBbox.x,
                  y: textBbox.y - textBbox.height/2,  // Take into account the y-alignment translation.
                  width: textBbox.width,
                  height: textBbox.height
              }));
              
          }, this);
      },
  
      renderTools: function() {
          // Tools are a group of clickable elements that manipulate the whole link.
          // A good example of this is the remove tool that removes the whole link.
          // Tools appear after hovering the link close to the `source` element/point of the link
          // but are offset a bit so that they don't cover the `marker-arrowhead`.
  
          var $tools = this.$('.link-tools').empty();
  
          var toolTemplate = _.template(this.model.get('toolMarkup') || this.toolMarkup);
          var tool = V(toolTemplate());
          $tools.append(tool.node);
  
          // Cache the tool node so that the `updateToolsPosition()` can update the tool position quickly.
          this._toolCache = tool;
      },
  
      updateToolsPosition: function() {
  
          // Move the tools a bit to the target position but don't cover the `sourceArrowhead` marker.
          // Note that the offset is hardcoded here. The offset should be always
          // more than the `this.$('.marker-arrowhead[end="source"]')[0].bbox().width` but looking
          // this up all the time would be slow.
          var scale = '';
          var offset = 40;
          // If the link is too short, make the tools half the size and the offset twice as low.
          if (this.getConnectionLength() < this.options.shortLinkLength) {
              scale = 'scale(.5)';
              offset /= 2;
          }
          var toolPosition = this.getPointAtLength(offset);
          
          this._toolCache.attr('transform', 'translate(' + toolPosition.x + ', ' + toolPosition.y + ') ' + scale);
      },
  
      renderVertexMarkers: function() {
  
          var $markerVertices = this.$('.marker-vertices').empty();
  
          // A special markup can be given in the `properties.vertexMarkup` property. This might be handy
          // if default styling (elements) are not desired. This makes it possible to use any
          // SVG elements for .marker-vertex and .marker-vertex-remove tools.
          var markupTemplate = _.template(this.model.get('vertexMarkup') || this.vertexMarkup);
          
          _.each(this.model.get('vertices'), function(vertex, idx) {
  
              $markerVertices.append(V(markupTemplate({ x: vertex.x, y: vertex.y, 'idx': idx })).node);
          });
          
          return this;
      },
  
      renderArrowheadMarkers: function() {
  
          var $markerArrowheads = this.$('.marker-arrowheads');
  
          // Custom markups might not have arrowhead markers. Therefore, jump of this function immediately if that's the case.
          if ($markerArrowheads.length === 0) return;
          
          $markerArrowheads.empty();
  
          // A special markup can be given in the `properties.vertexMarkup` property. This might be handy
          // if default styling (elements) are not desired. This makes it possible to use any
          // SVG elements for .marker-vertex and .marker-vertex-remove tools.
          var markupTemplate = _.template(this.model.get('arrowheadMarkup') || this.arrowheadMarkup);
  
          var firstVertex = _.first(this.model.get('vertices'));
          var lastVertex = _.last(this.model.get('vertices'));
  
          // If manhattan routing is enabled, reference points for determining orientation of the arrowheads
          // might be different than vertices defined on the model.
          if (this.model.get('manhattan')) {
              
              var manhattanRoute = this.findManhattanRoute(this.model.get('vertices'));
              firstVertex = _.first(manhattanRoute);
              lastVertex = _.last(manhattanRoute);
          }
  
          var sourcePosition = this.getConnectionPoint('source', this.model.get('source'), firstVertex || this.model.get('target')).round();
          var targetPosition = this.getConnectionPoint('target', this.model.get('target'), lastVertex || sourcePosition);
          
          var sourceArrowhead = V(markupTemplate({ x: sourcePosition.x, y: sourcePosition.y, 'end': 'source' })).node;
          var targetArrowhead = V(markupTemplate({ x: targetPosition.x, y: targetPosition.y, 'end': 'target' })).node;
  
          if (this.getConnectionLength() < this.options.shortLinkLength) {
  
              V(sourceArrowhead).scale(.5);
              V(targetArrowhead).scale(.5);
          }
          
          $markerArrowheads.append(sourceArrowhead);
          $markerArrowheads.append(targetArrowhead);
  
          // Make the markers "point" to their sticky points being auto-oriented towards `targetPosition`/`sourcePosition`.
          V(sourceArrowhead).translateAndAutoOrient(sourcePosition, firstVertex || targetPosition, this.paper.viewport);
          V(targetArrowhead).translateAndAutoOrient(targetPosition, lastVertex || sourcePosition, this.paper.viewport);
          
          return this;
      },
      
      removeVertex: function(idx) {
  
          var vertices = _.clone(this.model.get('vertices'));
          
          if (vertices && vertices.length) {
  
              vertices.splice(idx, 1);
              this.model.set('vertices', vertices);
          }
  
          return this;
      },
  
      // This method ads a new vertex to the `vertices` array of `.connection`. This method
      // uses a heuristic to find the index at which the new `vertex` should be placed at assuming
      // the new vertex is somewhere on the path.
      addVertex: function(vertex) {
  
          this.model.set('attrs', this.model.get('attrs') || {});
          var attrs = this.model.get('attrs');
          
          // As it is very hard to find a correct index of the newly created vertex,
          // a little heuristics is taking place here.
          // The heuristics checks if length of the newly created
          // path is lot more than length of the old path. If this is the case,
          // new vertex was probably put into a wrong index.
          // Try to put it into another index and repeat the heuristics again.
  
          var vertices = (this.model.get('vertices') || []).slice();
          // Store the original vertices for a later revert if needed.
          var originalVertices = vertices.slice();
  
          // A `<path>` element used to compute the length of the path during heuristics.
          var path = this._connection.node.cloneNode(false);
          
          // Length of the original path.        
          var originalPathLength = path.getTotalLength();
          // Current path length.
          var pathLength;
          // Tolerance determines the highest possible difference between the length
          // of the old and new path. The number has been chosen heuristically.
          var pathLengthTolerance = 20;
          // Total number of vertices including source and target points.
          var idx = vertices.length + 1;
  
          // Loop through all possible indexes and check if the difference between
          // path lengths changes significantly. If not, the found index is
          // most probably the right one.
          while (idx--) {
  
              vertices.splice(idx, 0, vertex);
              V(path).attr('d', this.getPathData(vertices));
  
              pathLength = path.getTotalLength();
  
              // Check if the path lengths changed significantly.
              if (pathLength - originalPathLength > pathLengthTolerance) {
  
                  // Revert vertices to the original array. The path length has changed too much
                  // so that the index was not found yet.
                  vertices = originalVertices.slice();
                  
              } else {
  
                  break;
              }
          }
  
          this.model.set('vertices', vertices);
  
          // In manhattan routing, if there are no vertices, the path length changes significantly
          // with the first vertex added. Shall we check vertices.length === 0? at beginning of addVertex()
          // in order to avoid the temporary path construction and other operations?
          return Math.max(idx, 0);
      },
  
      // Return the `d` attribute value of the `<path>` element representing the link between `source` and `target`.
      getPathData: function(vertices) {
  
          // If manhattan routing is enabled, find new vertices so that the link is orthogonally routed.
          if (this.model.get('manhattan')) {
  
              vertices = this.findManhattanRoute(vertices);
          }
          
          var firstVertex = _.first(vertices);
          var lastVertex = _.last(vertices);
  
          var sourcePoint = this.getConnectionPoint('source', this.model.get('source'), firstVertex || this.model.get('target')).round();
          var targetPoint = this.getConnectionPoint('target', this.model.get('target'), lastVertex || sourcePoint);
  
          // Move the source point by the width of the marker taking into account its scale around x-axis.
          // Note that scale is the only transform that makes sense to be set in `.marker-source` attributes object
          // as all other transforms (translate/rotate) will be replaced by the `translateAndAutoOrient()` function.
          var markerSourceBbox = this._markerSource.bbox(true);
          var markerSourceScaleX = this._markerSource.scale().sx;
  
          sourcePoint.move(firstVertex || targetPoint, -markerSourceBbox.width * markerSourceScaleX);
  
          var markerTargetBbox = this._markerTarget.bbox(true);
          var markerTargetScaleX = this._markerTarget.scale().sx;
  
          targetPoint.move(lastVertex || sourcePoint, -markerTargetBbox.width * markerTargetScaleX);
  
          var d;
          if (this.model.get('smooth')) {
  
              if (vertices && vertices.length) {
                  d = g.bezier.curveThroughPoints([sourcePoint].concat(vertices || []).concat([targetPoint]));
              } else {
                  // if we have no vertices use a default cubic bezier curve, cubic bezier requires two control points.
                  // the two control points are both defined with X as mid way between the source and target points.
                  // sourceControlPoint Y is equal to sourcePoint Y and targetControlPointY being equal to targetPointY.
                  // handle situation were sourcePointX is greater or less then targetPointX.
                  var controlPointX = (sourcePoint.x < targetPoint.x) 
                      ? targetPoint.x - ((targetPoint.x - sourcePoint.x) / 2)
                      : sourcePoint.x - ((sourcePoint.x - targetPoint.x) / 2);
                      d = ['M', sourcePoint.x, sourcePoint.y, 'C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y];
              }
              
          } else {
              
              // Construct the `d` attribute of the `<path>` element.
              d = ['M', sourcePoint.x, sourcePoint.y];
              _.each(vertices, function(vertex) {
  
                  d.push(vertex.x, vertex.y);
              });
              d.push(targetPoint.x, targetPoint.y);
          }
  
          return d.join(' ');
      },
  
      // Find a point that is the start of the connection.
      // If `selectorOrPoint` is a point, then we're done and that point is the start of the connection.
      // If the `selectorOrPoint` is an element however, we need to know a reference point (or element)
      // that the link leads to in order to determine the start of the connection on the original element.
      getConnectionPoint: function(end, selectorOrPoint, referenceSelectorOrPoint) {
  
          var spot;
  
          if (this._isPoint(selectorOrPoint)) {
              // If the source is a point, we don't need a reference point to find the sticky point of connection.
  
              spot = g.point(selectorOrPoint);
              
          } else {
              // If the source is an element, we need to find a point on the element boundary that is closest
              // to the reference point (or reference element).
  
              // Get the bounding box of the spot relative to the paper viewport. This is necessary
              // in order to follow paper viewport transformations (scale/rotate).
              var spotBbox;
              // If there are cached bounding boxes of source/target elements, use them. Otherwise,
              // find it.
              if (end === 'source' && this._sourceBbox) {
                  
                  spotBbox = this._sourceBbox;
                  
              } else if (end === 'target' && this._targetBbox) {
  
                  spotBbox = this._targetBbox;
                  
              } else {
                  
                  spotBbox = V(this.paper.$(this._makeSelector(selectorOrPoint))[0]).bbox(false, this.paper.viewport);
              }
              
              var reference;
              
              if (this._isPoint(referenceSelectorOrPoint)) {
                  // Reference was passed as a point, therefore, we're ready to find the sticky point of connection on the source element.
  
                  reference = g.point(referenceSelectorOrPoint);
                  
              } else {
                  // Reference was passed as an element, therefore we need to find a point on the reference
                  // element boundary closest to the source element.
  
                  // Get the bounding box of the spot relative to the paper viewport. This is necessary
                  // in order to follow paper viewport transformations (scale/rotate).
                  var referenceBbox;
  
                  if (end === 'source' && this._targetBbox) {
  
                      referenceBbox = this._targetBbox;
                      
                  } else if (end === 'target' && this._sourceBbox) {
  
                      referenceBbox = this._sourceBbox;
                      
                  } else {
                      
                      referenceBbox = V(this.paper.$(this._makeSelector(referenceSelectorOrPoint))[0]).bbox(false, this.paper.viewport);
                  }
                  
                  reference = g.rect(referenceBbox).intersectionWithLineFromCenterToPoint(g.rect(spotBbox).center());
                  reference = reference || g.rect(referenceBbox).center();
              }
  
              // If `perpendicularLinks` flag is set on the paper and there are vertices
              // on the link, then try to find a connection point that makes the link perpendicular
              // even though the link won't point to the center of the targeted object.
              if (this.paper.options.perpendicularLinks) {
  
                  var horizontalLineRect = g.rect(0, reference.y, this.paper.options.width, 1);
                  var verticalLineRect = g.rect(reference.x, 0, 1, this.paper.options.height);
                  var nearestSide;
  
                  if (horizontalLineRect.intersect(g.rect(spotBbox))) {
  
                      nearestSide = g.rect(spotBbox).sideNearestToPoint(reference);
                      switch (nearestSide) {
                        case 'left':
                          spot = g.point(spotBbox.x, reference.y);
                          break;
                        case 'right':
                          spot = g.point(spotBbox.x + spotBbox.width, reference.y);
                          break;
                      default:
                          spot = g.rect(spotBbox).center();
                          break;
                      }
                      
                  } else if (verticalLineRect.intersect(g.rect(spotBbox))) {
  
                      nearestSide = g.rect(spotBbox).sideNearestToPoint(reference);
                      switch (nearestSide) {
                        case 'top':
                          spot = g.point(reference.x, spotBbox.y);
                          break;
                        case 'bottom':
                          spot = g.point(reference.x, spotBbox.y + spotBbox.height);
                          break;
                      default:
                          spot = g.rect(spotBbox).center();
                          break;
                      }
                      
                  } else {
  
                      // If there is no intersection horizontally or vertically with the object bounding box,
                      // then we fall back to the regular situation finding straight line (not perpendicular)
                      // between the object and the reference point.
  
                      spot = g.rect(spotBbox).intersectionWithLineFromCenterToPoint(reference);
                      spot = spot || g.rect(spotBbox).center();
                  }
                  
              } else {
              
                  spot = g.rect(spotBbox).intersectionWithLineFromCenterToPoint(reference);
                  spot = spot || g.rect(spotBbox).center();
              }
          }
  
          return spot;
      },
  
      _isModel: function(end) {
  
          return end && end.id;
      },
  
      _isPoint: function(end) {
  
          return !this._isModel(end);
      },
  
      _makeSelector: function(end) {
  
          return '[model-id="' + end.id + '"]' + (end.selector ? ' ' + end.selector : '');
      },
  
      // Return points that one needs to draw a connection through in order to have a manhattan link routing from
      // source to target going through `vertices`.
      findManhattanRoute: function(vertices) {
  
          vertices = (vertices || []).slice();
          var manhattanVertices = [];
  
          // Return the direction that one would have to take traveling from `p1` to `p2`.
          // This function assumes the line between `p1` and `p2` is orthogonal.
          function direction(p1, p2) {
              
              if (p1.y < p2.y && p1.x === p2.x) {
                  return 'down';
              } else if (p1.y > p2.y && p1.x === p2.x) {
                  return 'up';
              } else if (p1.x < p2.x && p1.y === p2.y) {
                  return 'right';
              }
              return 'left';
          }
          
          function bestDirection(p1, p2, preferredDirection) {
  
              var directions;
  
              // This branching determines possible directions that one can take to travel
              // from `p1` to `p2`.
              if (p1.x < p2.x) {
                  
                  if (p1.y > p2.y) { directions = ['up', 'right']; }
                  else if (p1.y < p2.y) { directions = ['down', 'right']; }
                  else { directions = ['right']; }
                  
              } else if (p1.x > p2.x) {
                  
                  if (p1.y > p2.y) { directions = ['up', 'left']; }
                  else if (p1.y < p2.y) { directions = ['down', 'left']; }
                  else { directions = ['left']; }
                  
              } else {
                  
                  if (p1.y > p2.y) { directions = ['up']; }
                  else { directions = ['down']; }
              }
              
              if (_.contains(directions, preferredDirection)) {
                  return preferredDirection;
              }
              
              var direction = _.first(directions);
  
              // Should the direction be the exact opposite of the preferred direction,
              // try another one if such direction exists.
              switch (preferredDirection) {
                case 'down': if (direction === 'up') return _.last(directions); break;
                case 'up': if (direction === 'down') return _.last(directions); break;
                case 'left': if (direction === 'right') return _.last(directions); break;
                case 'right': if (direction === 'left') return _.last(directions); break;
              }
              return direction;
          }
          
          // Find a vertex in between the vertices `p1` and `p2` so that the route between those vertices
          // is orthogonal. Prefer going the direction determined by `preferredDirection`.
          function findMiddleVertex(p1, p2, preferredDirection) {
              
              var direction = bestDirection(p1, p2, preferredDirection);
              if (direction === 'down' || direction === 'up') {
                  return { x: p1.x, y: p2.y, d: direction };
              }
              return { x: p2.x, y: p1.y, d: direction };
          }
  
          var sourceCenter = g.rect(this._sourceBbox).center();
          var targetCenter = g.rect(this._targetBbox).center();
  
          vertices.unshift(sourceCenter);
          vertices.push(targetCenter);
  
          var manhattanVertex;
          var lastManhattanVertex;
          var vertex;
          var nextVertex;
  
          // For all the pairs of link model vertices...
          for (var i = 0; i < vertices.length - 1; i++) {
  
              vertex = vertices[i];
              nextVertex = vertices[i + 1];
              lastManhattanVertex = _.last(manhattanVertices);
              
              if (i > 0) {
                  // Push all the link vertices to the manhattan route.
                  manhattanVertex = vertex;
                  // Determine a direction between the last vertex and the new one.
                  // Therefore, each vertex contains the `d` property describing the direction that one
                  // would have to take to travel to that vertex.
                  manhattanVertex.d = lastManhattanVertex ? direction(lastManhattanVertex, vertex) : 'top';
                  manhattanVertices.push(manhattanVertex);
                  lastManhattanVertex = manhattanVertex;
              }
  
              // Make sure that we don't create a vertex that would go the opposite direction then that of the
              // previous one. Othwerwise, a 'spike' segment would be created which is not desirable.
              // Find a dummy vertex to keep the link orthogonal. Preferably, take the same direction
              // as the previous one.
              var d = lastManhattanVertex && lastManhattanVertex.d;
              manhattanVertex = findMiddleVertex(vertex, nextVertex, d);
  
              // Do not add a new vertex that is the same as one of the vertices already added.
              if (!g.point(manhattanVertex).equals(g.point(vertex)) && !g.point(manhattanVertex).equals(g.point(nextVertex))) {
  
                  manhattanVertices.push(manhattanVertex);
              }
          }
          return manhattanVertices;
      },
  
      // Public API
      // ----------
  
      getConnectionLength: function() {
  
          return this._connection.node.getTotalLength();
      },
  
      getPointAtLength: function(length) {
  
          return this._connection.node.getPointAtLength(length);
      },
  
  
      // Interaction. The controller part.
      // ---------------------------------
  
      pointerdown: function(evt, x, y) {
  
          joint.dia.CellView.prototype.pointerdown.apply(this, arguments);
  
          delete this._action;
          
          this._dx = x;
          this._dy = y;
  
          if (this.options.interactive === false) {
  
              return;
          }
          
          var targetClass = V(evt.target).attr('class');
          var targetParentEvent = V($(evt.target).parent()[0]).attr('event');
  
          if (targetClass === 'marker-vertex') {
  
              this._vertexIdx = $(evt.target).attr('idx');
              this._action = 'vertex-move';
  
          } else if (targetClass === 'marker-vertex-remove' ||
                     targetClass === 'marker-vertex-remove-area') {
  
              this.removeVertex($(evt.target).attr('idx'));
  
          } else if (targetClass === 'marker-arrowhead') {
  
              this._arrowheadEnd = $(evt.target).attr('end');
              this._action = 'arrowhead-move';
              this._originalZ = this.model.get('z');
              this.model.set('z', Number.MAX_VALUE);
              // Let the pointer propagate throught the link view elements so that
              // the `evt.target` is another element under the pointer, not the link itself.
              this.$el.css({ 'pointer-events': 'none' });
  
          } else if (targetParentEvent) {
  
              // `remove` event is built-in. Other custom events are triggered on the paper.
              if (targetParentEvent === 'remove') {
                  
                  this.model.remove();
                  
              } else {
  
                  this.paper.trigger(targetParentEvent, evt, this, x, y);
              }
              
          } else {
  
              // Store the index at which the new vertex has just been placed.
              // We'll be update the very same vertex position in `pointermove()`.
              this._vertexIdx = this.addVertex({ x: x, y: y });
              this._action = 'vertex-move';
          }
      },
  
      pointermove: function(evt, x, y) {
  
          joint.dia.CellView.prototype.pointermove.apply(this, arguments);
  
          if (this.options.interactive === false) {
  
              return;
          }
  
          switch (this._action) {
  
            case 'vertex-move':
              
              var vertices = _.clone(this.model.get('vertices'));
              vertices[this._vertexIdx] = { x: x, y: y };
              this.model.set('vertices', vertices);
              break;
  
            case 'arrowhead-move':
  
              // Unhighlight the previous view under pointer if there was one.
              if (this._viewUnderPointer) {
  
                  this._viewUnderPointer.unhighlight(this._magnetUnderPointer);
              }
              
              this._viewUnderPointer = this.paper.findView(evt.target);
              if (this._viewUnderPointer && this._viewUnderPointer.model instanceof joint.dia.Link) {
  
                  // Do not allow linking links with links.
                  this._viewUnderPointer = null;
              }
  
              // If we found a view that is under the pointer, we need to find the closest
              // magnet based on the real target element of the event.
              if (this._viewUnderPointer) {
                  
                  this._magnetUnderPointer = this._viewUnderPointer.findMagnet(evt.target);
                  if (!this._magnetUnderPointer) {
  
                      // If there was no magnet found, do not highlight anything and assume there
                      // is no view under pointer we're interested in reconnecting to.
                      // This can only happen if the overall element has the attribute `'.': { magnet: false }`.
                      this._viewUnderPointer = null;
                  }
              }
  
              if (this._viewUnderPointer) {
  
                  this._viewUnderPointer.highlight(this._magnetUnderPointer);
              }
              
              this.model.set(this._arrowheadEnd, { x: x, y: y });
              break;
          }
          
          this._dx = x;
          this._dy = y;
      },
  
      pointerup: function(evt) {
  
          joint.dia.CellView.prototype.pointerup.apply(this, arguments);
  
          if (this._action === 'arrowhead-move') {
  
              // Put `pointer-events` back to its original value. See `pointerdown()` for explanation.
  	    // Value `auto` doesn't work in IE9. We force to use `visiblePainted` instead.
  	    // See `https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events`.
              this.$el.css({ 'pointer-events': 'visiblePainted' });
  
              this.model.set('z', this._originalZ);
              delete this._originalZ;
  
              if (this._viewUnderPointer) {
  
                  // Find a unique `selector` of the element under pointer that is a magnet. If the
                  // `this._magnetUnderPointer` is the root element of the `this._viewUnderPointer` itself,
                  // the returned `selector` will be `undefined`. That means we can directly pass it to the
                  // `source`/`target` attribute of the link model below.
                  var selector = this._viewUnderPointer.getSelector(this._magnetUnderPointer);
                      
  		this.model.set(this._arrowheadEnd, { id: this._viewUnderPointer.model.id, selector: selector });
                  
                  this._viewUnderPointer.unhighlight(this._magnetUnderPointer);
                  delete this._viewUnderPointer;
                  delete this._magnetUnderPointer;
              }
          }
      }
  });
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS library.
  //      (c) 2011-2013 client IO
  
  
  joint.dia.Paper = Backbone.View.extend({
  
      options: {
  
          width: 800,
          height: 600,
          gridSize: 50,
          perpendicularLinks: false,
          elementView: joint.dia.ElementView,
          linkView: joint.dia.LinkView
      },
  
      events: {
  
          'mousedown': 'pointerdown',
          'touchstart': 'pointerdown',
          'mousemove': 'pointermove',
          'touchmove': 'pointermove'
      },
  
      initialize: function() {
  
          _.bindAll(this, 'addCell', 'sortCells', 'resetCells', 'pointerup');
  
          this.svg = V('svg').node;
          this.viewport = V('g').node;
  
          V(this.viewport).attr({ 'class': 'viewport' });
          
          V(this.svg).append(this.viewport);
  
          this.$el.append(this.svg);
  
          this.setDimensions();
  
          this.model.on({
  
              'add': this.addCell,
              'reset': this.resetCells,
              'sort': this.sortCells
          });
  
  	$(document).on('mouseup touchend', this.pointerup);
      },
  
      remove: function() {
  
  	$(document).off('mouseup touchend', this.pointerup);
  
  	Backbone.View.prototype.remove.call(this);
      },
  
      setDimensions: function(width, height) {
  
          if (width) this.options.width = width;
          if (height) this.options.height = height;
          
          V(this.svg).attr('width', this.options.width);
          V(this.svg).attr('height', this.options.height);
  
  	this.trigger('resize');
      },
  
      fitToContent: function(unitWidth, unitHeight) {
  
  	unitWidth = unitWidth || 1;
  	unitHeight = unitHeight || 1;
  
  	// Calculate the paper size to accomodate all the graph's elements.
  	var bbox = V(this.viewport).bbox(true, this.svg);
  
  	var calcWidth = Math.ceil((bbox.width + bbox.x) / unitWidth) * unitWidth;
  	var calcHeight = Math.ceil((bbox.height + bbox.y) / unitHeight) * unitHeight;
  
  	// Change the dimensions only if there is a size discrepency
  	if (calcWidth != this.options.width || calcHeight != this.options.height) {
  	    this.setDimensions(calcWidth || this.options.width , calcHeight || this.options.height);
  	}
      },
  
      createViewForModel: function(cell) {
          
          var view;
          
          var type = cell.get('type');
          var module = type.split('.')[0];
          var entity = type.split('.')[1];
  
          // If there is a special view defined for this model, use that one instead of the default `elementView`/`linkView`.
          if (joint.shapes[module] && joint.shapes[module][entity + 'View']) {
  
              view = new joint.shapes[module][entity + 'View']({ model: cell, interactive: this.options.interactive });
              
          } else if (cell instanceof joint.dia.Element) {
                  
              view = new this.options.elementView({ model: cell, interactive: this.options.interactive });
  
          } else {
  
              view = new this.options.linkView({ model: cell, interactive: this.options.interactive });
          }
  
          return view;
      },
      
      addCell: function(cell) {
  
          var view = this.createViewForModel(cell);
  
          V(this.viewport).append(view.el);
          view.paper = this;
          view.render();
  
          // This is the only way to prevent image dragging in Firefox that works.
          // Setting -moz-user-select: none, draggable="false" attribute or user-drag: none didn't help.
          $(view.el).find('image').on('dragstart', function() { return false; });
      },
  
      resetCells: function(cellsCollection) {
  
          $(this.viewport).empty();
  
          var cells = cellsCollection.models.slice();
  
          // Make sure links are always added AFTER elements.
          // They wouldn't find their sources/targets in the DOM otherwise.
          cells.sort(function(a, b) { return a instanceof joint.dia.Link ? 1 : -1; });
          
          _.each(cells, this.addCell, this);
  
          // Sort the cells in the DOM manually as we might have changed the order they
          // were added to the DOM (see above).
          this.sortCells();
      },
  
      sortCells: function() {
  
          // Run insertion sort algorithm in order to efficiently sort DOM elements according to their
          // associated model `z` attribute.
  
          var $cells = $(this.viewport).children('[model-id]');
          var cells = this.model.get('cells');
  
          // Using the jquery.sortElements plugin by Padolsey.
          // See http://james.padolsey.com/javascript/sorting-elements-with-jquery/.
          $cells.sortElements(function(a, b) {
  
              var cellA = cells.get($(a).attr('model-id'));
              var cellB = cells.get($(b).attr('model-id'));
              
              return (cellA.get('z') || 0) > (cellB.get('z') || 0) ? 1 : -1;
          });
      },
  
      scale: function(sx, sy, ox, oy) {
  
          if (!ox) {
  
              ox = 0;
              oy = 0;
          }
  
          // Remove previous transform so that the new scale is not affected by previous scales, especially
          // the old translate() does not affect the new translate if an origin is specified.
          V(this.viewport).attr('transform', '');
          
          // TODO: V.scale() doesn't support setting scale origin. #Fix        
          if (ox || oy) {
              V(this.viewport).translate(-ox * (sx - 1), -oy * (sy - 1));
          }
          
          V(this.viewport).scale(sx, sy);
  
  	this.trigger('scale', ox, oy);
  
          return this;
      },
  
      rotate: function(deg, ox, oy) {
          
          // If the origin is not set explicitely, rotate around the center. Note that
          // we must use the plain bounding box (`this.el.getBBox()` instead of the one that gives us
          // the real bounding box (`bbox()`) including transformations).
          if (_.isUndefined(ox)) {
  
              var bbox = this.viewport.getBBox();
              ox = bbox.width/2;
              oy = bbox.height/2;
          }
  
          V(this.viewport).rotate(deg, ox, oy);
      },
  
      zoom: function(level) {
  
  	level = level || 1;
  
          V(this.svg).attr('width', this.options.width * level);
          V(this.svg).attr('height', this.options.height * level);
      },
  
      // Find the first view climbing up the DOM tree starting at element `el`. Note that `el` can also
      // be a selector or a jQuery object.
      findView: function(el) {
  
          var $el = this.$(el);
  
          if ($el.length === 0 || $el[0] === this.el) {
  
              return undefined;
          }
  
          if ($el.data('view')) {
  
              return $el.data('view');
          }
  
          return this.findView($el.parent());
      },
  
      // Find a view for a model `cell`. `cell` can also be a string representing a model `id`.
      findViewByModel: function(cell) {
  
          var id = _.isString(cell) ? cell : cell.id;
          
          var $view = this.$('[model-id="' + id + '"]');
          if ($view.length) {
  
              return $view.data('view');
          }
          return undefined;
      },
  
      // Find all views at given point
      findViewsFromPoint: function(p) {
  
  	p = g.point(p);
  
  	var isNotALink = function(cell) {
  	    return !(cell instanceof joint.dia.Link);
  	};
  
  	var elements = this.model.get('cells').filter(isNotALink);
  
          var views = _.map(elements, this.findViewByModel);
  
  	return _.filter(views, function(view) {
  	    return g.rect(view.getBBox()).containsPoint(p);
  	}, this);
      },
  
      // Find all views at given point
      findViewsInArea: function(r) {
  
  	r = g.rect(r);
  
  	var isNotALink = function(cell) {
  	    return !(cell instanceof joint.dia.Link);
  	};
  
  	var elements = this.model.get('cells').filter(isNotALink);
  
          var elementViews = [];
          
  	_.each(elements, function(element) {
  
              var view = this.findViewByModel(element);
  	    if (r.containsPoint(g.point(view.getBBox()))) {
  
                  elementViews.push(view);
              }
  	}, this);
  
          return elementViews;
      },
      
      getModelById: function(id) {
  
          return this.model.getCell(id);
      },
  
      snapToGrid: function(p) {
  
          // Convert global coordinates to the local ones of the `viewport`. Otherwise,
          // improper transformation would be applied when the viewport gets transformed (scaled/rotated). 
          var localPoint = V(this.viewport).toLocalPoint(p.x, p.y);
  
          return {
              x: g.snapToGrid(localPoint.x, this.options.gridSize),
              y: g.snapToGrid(localPoint.y, this.options.gridSize)
          };
      },
  
      // Interaction.
      // ------------
  
      normalizeEvent: function(evt) {
  
          return (evt.originalEvent && evt.originalEvent.changedTouches && evt.originalEvent.changedTouches.length) ? evt.originalEvent.changedTouches[0] : evt;
      },
  
      pointerdown: function(evt) {
  
          evt.preventDefault();
          evt = this.normalizeEvent(evt);
          
          var view = this.findView(evt.target);
  
          var localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY });
          
          if (view) {
  
              this.sourceView = view;
  
              view.pointerdown(evt, localPoint.x, localPoint.y);
              
          } else {
  
              this.trigger('blank:pointerdown', evt, localPoint.x, localPoint.y);
          }
      },
  
      pointermove: function(evt) {
  
          evt.preventDefault();
          evt = this.normalizeEvent(evt);
  
          if (this.sourceView) {
  
              var localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY });
  
              this.sourceView.pointermove(evt, localPoint.x, localPoint.y);
          }
      },
  
      pointerup: function(evt) {
  
          evt = this.normalizeEvent(evt);
  
          var localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY });
          
          if (this.sourceView) {
  
              this.sourceView.pointerup(evt, localPoint.x, localPoint.y);
              delete this.sourceView;
              
          } else {
  
              this.trigger('blank:pointerup', evt, localPoint.x, localPoint.y);
          }
      }
  });
  
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  //      JointJS library.
  //      (c) 2011-2013 client IO
  
  
  joint.shapes.basic = {};
  
  
  joint.shapes.basic.Generic = joint.dia.Element.extend({
  
      defaults: joint.util.deepSupplement({
          
          type: 'basic.Generic',
          attrs: {
              '.': { fill: '#FFFFFF', stroke: 'none' }
          }
          
      }, joint.dia.Element.prototype.defaults)
  });
  
  joint.shapes.basic.Rect = joint.shapes.basic.Generic.extend({
  
      markup: '<g class="rotatable"><g class="scalable"><rect/></g><text/></g>',
      
      defaults: joint.util.deepSupplement({
      
          type: 'basic.Rect',
          attrs: {
              'rect': { fill: '#FFFFFF', stroke: 'black', width: 1, height: 1 },
              'text': { 'font-size': 14, text: '', 'ref-x': .5, 'ref-y': .5, ref: 'rect', 'y-alignment': 'middle', 'x-alignment': 'middle', fill: 'black', 'font-family': 'Arial, helvetica, sans-serif' }
          }
          
      }, joint.shapes.basic.Generic.prototype.defaults)
  });
  
  joint.shapes.basic.Text = joint.shapes.basic.Generic.extend({
  
      markup: '<g class="rotatable"><g class="scalable"><text/></g></g>',
      
      defaults: joint.util.deepSupplement({
          
          type: 'basic.Text',
          attrs: {
              'text': { 'font-size': 18, fill: 'black' }
          }
          
      }, joint.shapes.basic.Generic.prototype.defaults)
  });
  
  joint.shapes.basic.Circle = joint.shapes.basic.Generic.extend({
  
      markup: '<g class="rotatable"><g class="scalable"><circle/></g><text/></g>',
      
      defaults: joint.util.deepSupplement({
  
          type: 'basic.Circle',
          size: { width: 60, height: 60 },
          attrs: {
              'circle': { fill: '#FFFFFF', stroke: 'black', r: 30, transform: 'translate(30, 30)' },
              'text': { 'font-size': 14, text: '', 'text-anchor': 'middle', 'ref-x': .5, 'ref-y': .5, ref: 'circle', 'y-alignment': 'middle', fill: 'black', 'font-family': 'Arial, helvetica, sans-serif' }
          }
      }, joint.shapes.basic.Generic.prototype.defaults)
  });
  
  joint.shapes.basic.Image = joint.shapes.basic.Generic.extend({
  
      markup: '<g class="rotatable"><g class="scalable"><image/></g><text/></g>',
      
      defaults: joint.util.deepSupplement({
  
          type: 'basic.Image',
          attrs: {
              'text': { 'font-size': 14, text: '', 'text-anchor': 'middle', 'ref-x': .5, 'ref-dy': 20, ref: 'image', 'y-alignment': 'middle', fill: 'black', 'font-family': 'Arial, helvetica, sans-serif' }
          }
      }, joint.shapes.basic.Generic.prototype.defaults)
  });
  
  joint.shapes.basic.Path = joint.shapes.basic.Generic.extend({
  
      markup: '<g class="rotatable"><g class="scalable"><path/></g><text/></g>',
      
      defaults: joint.util.deepSupplement({
  
          type: 'basic.Path',
          size: { width: 60, height: 60 },
          attrs: {
              'path': { fill: '#FFFFFF', stroke: 'black' },
              'text': { 'font-size': 14, text: '', 'text-anchor': 'middle', 'ref-x': .5, 'ref-dy': 20, ref: 'path', 'y-alignment': 'middle', fill: 'black', 'font-family': 'Arial, helvetica, sans-serif' }
          }
      }, joint.shapes.basic.Generic.prototype.defaults)
  });
  