From b30997a7cba1bb58706a79af407f06744820701c Mon Sep 17 00:00:00 2001 From: Forrest Oliphant Date: Wed, 2 Oct 2013 17:46:16 +0300 Subject: [PATCH 1/3] speeding up logs #67 #77 --- Gruntfile.js | 2 ++ debug.html | 9 +++-- libs/CircularBuffer.js | 35 ++++++++++++++++++ src/modules/edge-inspect-view.js | 61 +++++++++++++++----------------- src/modules/edge.js | 14 +------- 5 files changed, 72 insertions(+), 49 deletions(-) create mode 100644 libs/CircularBuffer.js diff --git a/Gruntfile.js b/Gruntfile.js index f727c21..e86ba97 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -12,6 +12,8 @@ }, dist: { src: [ + // Libs + 'libs/CircularBuffer.js', // Main 'src/dataflow.js', 'src/state.js', diff --git a/debug.html b/debug.html index 5130ee5..3a67ff6 100644 --- a/debug.html +++ b/debug.html @@ -14,6 +14,9 @@ + + + @@ -97,15 +100,15 @@ // Populate some fake log events setInterval(function () { - dataflow.graph.get('edges').at(1).get('log').add({ + dataflow.graph.get('edges').at(1).get('log').push({ type: 'begingroup', data: 'random' }); - dataflow.graph.get('edges').at(1).get('log').add({ + dataflow.graph.get('edges').at(1).get('log').push({ type: 'data', data: Math.random() }); - dataflow.graph.get('edges').at(1).get('log').add({ + dataflow.graph.get('edges').at(1).get('log').push({ type: 'endgroup', data: 'random' }); diff --git a/libs/CircularBuffer.js b/libs/CircularBuffer.js new file mode 100644 index 0000000..a3f8d71 --- /dev/null +++ b/libs/CircularBuffer.js @@ -0,0 +1,35 @@ +// Thanks bobnice http://stackoverflow.com/a/1583281/592125 + +// Circular buffer storage. Externally-apparent 'length' increases indefinitely +// while any items with indexes below length-n will be forgotten (undefined +// will be returned if you try to get them, trying to set is an exception). +// n represents the initial length of the array, not a maximum + +function CircularBuffer(n) { + this._array= new Array(n); + this.length= 0; +} +CircularBuffer.prototype.toString= function() { + return '[object CircularBuffer('+this._array.length+') length '+this.length+']'; +}; +CircularBuffer.prototype.get= function(i) { + if (i<0 || ithis.length) { + this._array[this.length%this._array.length] = undefined; + this.length++; + } + this._array[i%this._array.length] = v; + if (i==this.length) + this.length++; +}; +CircularBuffer.prototype.push = function(v) { + this._array[this.length%this._array.length] = v; + this.length++; +}; +CircularBuffer.IndexError= {}; diff --git a/src/modules/edge-inspect-view.js b/src/modules/edge-inspect-view.js index e427c69..2386485 100644 --- a/src/modules/edge-inspect-view.js +++ b/src/modules/edge-inspect-view.js @@ -10,14 +10,11 @@ '
'+ ''; - var logTemplate = '
  • <%- group %><%- data %>
  • '; - Edge.InspectView = Backbone.View.extend({ tagName: "div", className: "dataflow-edge-inspector", positions: null, template: _.template(template), - showLogs: 20, initialize: function() { var templateData = this.model.toJSON(); if (this.model.id) { @@ -47,10 +44,8 @@ this.listenTo(this.model, "change:route", this.render); this.listenTo(this.model, "remove", this.remove); - this.listenTo(this.model.get('log'), 'add', function () { - this.logDirty = true; - }.bind(this)); - this.renderLog(); + // Check if need to render logs + this.animate(); }, render: function(){ var route = this.model.get("route"); @@ -59,38 +54,38 @@ $choose.children(".route"+route).addClass("active"); return this; }, - logDirty: false, + showLogs: 20, + lastLog: 0, animate: function (timestamp) { // Called from dataflow.shownCards collection (card-view.js) - if (this.logDirty) { - this.logDirty = false; - this.renderLog(); - } - }, - renderLog: function () { - var frag = document.createDocumentFragment(); var logs = this.model.get('log'); - var logsToShow; - if (logs.length > this.showLogs) { - logsToShow = logs.rest(logs.length - this.showLogs); - } else { - logsToShow = logs.toArray(); + if (logs.length > this.lastLog) { + this.renderLogs(logs); + this.lastLog = logs.length; } - //JANK warning, already taking 14ms with 20 log items - _.each(logsToShow, function (item) { - this.renderLogItem(item, frag); - }, this); - this.$log.html(frag); - this.$log[0].scrollTop = this.$log[0].scrollHeight; }, - renderLogItem: function (item, fragment) { - var html = $(_.template(logTemplate, item.toJSON())); - if (fragment && fragment.appendChild) { - fragment.appendChild(html[0]); - } else { - this.$log.append(html); - this.$log[0].scrollTop = this.$log[0].scrollHeight; + renderLogs: function (logs) { + // Add new logs + var firstToShow = this.lastLog; + if (logs.length - this.lastLog > this.showLogs) { + firstToShow = logs.length - this.showLogs; } + for (var i=firstToShow; i") + .addClass(item.type) + .text( (item.group ? item.group + " " : "")+item.data); + this.$log.append(li); + } + } + // Trim list + while (this.$log.children().length > this.showLogs) { + this.$log.children().first().remove(); + } + console.log( this.$log.children().length ); + // Scroll list + this.$log[0].scrollTop = this.$log[0].scrollHeight; } }); diff --git a/src/modules/edge.js b/src/modules/edge.js index a9ea66a..4e813e0 100644 --- a/src/modules/edge.js +++ b/src/modules/edge.js @@ -2,18 +2,6 @@ var Edge = Dataflow.prototype.module("edge"); - var EdgeEvent = Backbone.Model.extend({ - defaults: { - "type": "data", - "data": "", - "group": "" - } - }); - - var EdgeEventLog = Backbone.Collection.extend({ - model: EdgeEvent - }); - Edge.Model = Backbone.Model.extend({ defaults: { "z": 0, @@ -25,7 +13,7 @@ var nodes, sourceNode, targetNode; var preview = this.get("preview"); this.parentGraph = this.get("parentGraph"); - this.attributes.log = new EdgeEventLog(); + this.attributes.log = new CircularBuffer(50); if (preview) { // Preview edge nodes = this.get("parentGraph").nodes; From 5d91b42d45f263440d5735cb12a9301d05b952f0 Mon Sep 17 00:00:00 2001 From: Forrest Oliphant Date: Wed, 2 Oct 2013 17:51:57 +0300 Subject: [PATCH 2/3] rebuild --- build/dataflow.build.js | 115 ++++++++++++++++++++++---------------- build/dataflow.min.js | 8 +-- build/dataflow.min.js.map | 2 +- 3 files changed, 72 insertions(+), 53 deletions(-) diff --git a/build/dataflow.build.js b/build/dataflow.build.js index a23fdd8..611952b 100644 --- a/build/dataflow.build.js +++ b/build/dataflow.build.js @@ -1,5 +1,41 @@ -/*! dataflow.js - v0.0.7 - 2013-10-02 (4:18:28 PM GMT+0200) +/*! dataflow.js - v0.0.7 - 2013-10-02 (5:51:40 PM GMT+0300) * Copyright (c) 2013 Forrest Oliphant; Licensed MIT, GPL */ +// Thanks bobnice http://stackoverflow.com/a/1583281/592125 + +// Circular buffer storage. Externally-apparent 'length' increases indefinitely +// while any items with indexes below length-n will be forgotten (undefined +// will be returned if you try to get them, trying to set is an exception). +// n represents the initial length of the array, not a maximum + +function CircularBuffer(n) { + this._array= new Array(n); + this.length= 0; +} +CircularBuffer.prototype.toString= function() { + return '[object CircularBuffer('+this._array.length+') length '+this.length+']'; +}; +CircularBuffer.prototype.get= function(i) { + if (i<0 || ithis.length) { + this._array[this.length%this._array.length] = undefined; + this.length++; + } + this._array[i%this._array.length] = v; + if (i==this.length) + this.length++; +}; +CircularBuffer.prototype.push = function(v) { + this._array[this.length%this._array.length] = v; + this.length++; +}; +CircularBuffer.IndexError= {}; + (function(){ var App = Backbone.Model.extend({ "$": function(query) { @@ -735,18 +771,6 @@ var Edge = Dataflow.prototype.module("edge"); - var EdgeEvent = Backbone.Model.extend({ - defaults: { - "type": "data", - "data": "", - "group": "" - } - }); - - var EdgeEventLog = Backbone.Collection.extend({ - model: EdgeEvent - }); - Edge.Model = Backbone.Model.extend({ defaults: { "z": 0, @@ -758,7 +782,7 @@ var nodes, sourceNode, targetNode; var preview = this.get("preview"); this.parentGraph = this.get("parentGraph"); - this.attributes.log = new EdgeEventLog(); + this.attributes.log = new CircularBuffer(50); if (preview) { // Preview edge nodes = this.get("parentGraph").nodes; @@ -2848,14 +2872,11 @@ '
    '+ '
      '; - var logTemplate = '
    • <%- group %><%- data %>
    • '; - Edge.InspectView = Backbone.View.extend({ tagName: "div", className: "dataflow-edge-inspector", positions: null, template: _.template(template), - showLogs: 20, initialize: function() { var templateData = this.model.toJSON(); if (this.model.id) { @@ -2885,10 +2906,8 @@ this.listenTo(this.model, "change:route", this.render); this.listenTo(this.model, "remove", this.remove); - this.listenTo(this.model.get('log'), 'add', function () { - this.logDirty = true; - }.bind(this)); - this.renderLog(); + // Check if need to render logs + this.animate(); }, render: function(){ var route = this.model.get("route"); @@ -2897,38 +2916,38 @@ $choose.children(".route"+route).addClass("active"); return this; }, - logDirty: false, + showLogs: 20, + lastLog: 0, animate: function (timestamp) { // Called from dataflow.shownCards collection (card-view.js) - if (this.logDirty) { - this.logDirty = false; - this.renderLog(); - } - }, - renderLog: function () { - var frag = document.createDocumentFragment(); var logs = this.model.get('log'); - var logsToShow; - if (logs.length > this.showLogs) { - logsToShow = logs.rest(logs.length - this.showLogs); - } else { - logsToShow = logs.toArray(); + if (logs.length > this.lastLog) { + this.renderLogs(logs); + this.lastLog = logs.length; + } + }, + renderLogs: function (logs) { + // Add new logs + var firstToShow = this.lastLog; + if (logs.length - this.lastLog > this.showLogs) { + firstToShow = logs.length - this.showLogs; + } + for (var i=firstToShow; i") + .addClass(item.type) + .text( (item.group ? item.group + " " : "")+item.data); + this.$log.append(li); + } } - //JANK warning, already taking 14ms with 20 log items - _.each(logsToShow, function (item) { - this.renderLogItem(item, frag); - }, this); - this.$log.html(frag); - this.$log[0].scrollTop = this.$log[0].scrollHeight; - }, - renderLogItem: function (item, fragment) { - var html = $(_.template(logTemplate, item.toJSON())); - if (fragment && fragment.appendChild) { - fragment.appendChild(html[0]); - } else { - this.$log.append(html); - this.$log[0].scrollTop = this.$log[0].scrollHeight; + // Trim list + while (this.$log.children().length > this.showLogs) { + this.$log.children().first().remove(); } + console.log( this.$log.children().length ); + // Scroll list + this.$log[0].scrollTop = this.$log[0].scrollHeight; } }); diff --git a/build/dataflow.min.js b/build/dataflow.min.js index 078db72..3bf51e2 100644 --- a/build/dataflow.min.js +++ b/build/dataflow.min.js @@ -1,5 +1,5 @@ -/*! dataflow.js - v0.0.7 - 2013-10-02 (4:18:28 PM GMT+0200) +/*! dataflow.js - v0.0.7 - 2013-10-02 (5:51:40 PM GMT+0300) * Copyright (c) 2013 Forrest Oliphant; Licensed MIT, GPL */ -!function(){var a=Backbone.Model.extend({$:function(a){return this.$el.find(a)},initialize:function(){this.el=document.createElement("div"),this.el.className="dataflow",this.$el=$(this.el),this.$el.data("dataflow",this);var a=Dataflow.prototype.module("card");if(this.shownCards=new a.Collection,this.shownCards.view=new a.CollectionView({collection:this.shownCards}),this.$el.append(this.shownCards.view.$el),this.debug=this.get("debug"),this.controls=this.get("controls"),this.controls!==!1&&(this.controls=!0),this.controls)for(var b in this.plugins)this.plugins[b].initialize&&this.plugins[b].initialize(this);this.inputs=this.get("inputs"),this.inputs!==!1&&(this.inputs=!0),this.editable=this.get("editable"),this.editable!==!1&&(this.editable=!0);var c=this.get("appendTo");c=c?c:"body","body"===c&&$("html, body").css({margin:"0px",padding:"0px",width:"100%",height:"100%"}),$(c).append(this.el),this.id||(this.id=$(c).attr("id")),this.loadState()},modules:{},module:function(a){return this.modules[a]?this.modules[a]:(this.modules[a]={},this.modules[a])},nodes:{},node:function(a){return this.nodes[a]?this.nodes[a]:(this.nodes[a]={description:""},this.nodes[a])},plugins:{},plugin:function(a){return this.plugins[a]?this.plugins[a]:(this.plugins[a]={},this.plugins[a])},addCard:function(a,b){b||this.hideCards(),this.shownCards.get(a)?this.shownCards.view.bringToTop(a):this.shownCards.add(a)},removeCard:function(a){this.shownCards.remove(a)},hideCards:function(){var a=this.shownCards.where({pinned:!1});this.shownCards.remove(a)},addPlugin:function(a){var b=this.plugins[a.id];if(b||(this.plugins[a.id]=b={}),b.info=a,b.enabled=!0,a.menu){var c=Dataflow.prototype.module("card"),d=new c.Model({dataflow:this,card:{el:a.menu},pinned:a.pinned?!0:!1});b.card=d,this.plugins.menu.addPlugin({id:a.id,icon:a.icon,label:a.label,showLabel:!1})}},showPlugin:function(a){this.plugins[a]&&this.plugins[a].card&&(this.addCard(this.plugins[a].card),"function"==typeof this.plugins[a].onShow&&this.plugins[a].onShow())},enablePlugin:function(a){var b=this.plugins[a];b&&this.addPlugin(b.info)},disablePlugin:function(a){this.plugins.menu.disablePlugin(a)},showContextBar:function(){this.contextBar.view.$el.show()},hideContextBar:function(){this.contextBar.view.$el.hide()},contexts:{},prepareContext:function(a){if(this.contexts[a])return this.contexts[a];var b=this.module("menucard");return this.contexts[a]=new b.Model({id:"context-"+a,dataflow:this,pinned:!0}),this.contexts[a].view=new b.View({model:this.contexts[a]}),this.contexts[a]},addContext:function(a){_.each(a.contexts,function(b){var c=this.prepareContext(b);c.menu.add(a)},this)},changeContext:function(a,b){var c=function(a,b){this.contexts[a]&&(this.contexts[a].set("label",b),this.shownCards.get("context-"+a)||this.shownCards.add(this.contexts[a]))}.bind(this),d=function(a){this.shownCards.get("context-"+a)&&this.shownCards.remove("context-"+a)}.bind(this);a.length>1?(c("nodes",a.length+" nodes"),d("node")):1===a.length?(c("node",a[0].get("label")),d("nodes")):(d("node"),d("nodes")),b.length>1?(c("edges",b.length+" edges"),d("edge")):1===b.length?(c("edge",b[0].id),d("edges")):(d("edge"),d("edges"))},loadGraph:function(a){this.graph&&(this.currentGraph.view&&this.currentGraph.view.remove(),this.graph.view&&this.graph.view.remove(),this.graph.remove());var b=this.module("graph");a.dataflow=this;var c=new b.Model(a);return c.view=new b.View({model:c}),this.$el.append(c.view.render().el),this.graph=this.currentGraph=c,c},showGraph:function(a){this.currentGraph.view.$el.detach(),this.$el.append(a.view.el),a.view.render(),this.currentGraph=a},debug:!1,log:function(a){this.trigger("log",a,arguments),this.debug&&console.log("Dataflow: ",arguments)},types:["all","canvas:2d","canvas:webgl","string","number","int","object","array"]});window.Dataflow=a,"object"==typeof exports&&(exports.Dataflow=a),Backbone.View.prototype.addEvents=function(a){this.delegateEvents(_.extend(_.clone(this.events),a))},Backbone.CollectionView=Backbone.Model.extend({prepend:!1,initialize:function(a){a.tagName&&(this.tagName=a.tagName),a.className&&(this.className=a.className),a.itemView&&(this.itemView=a.itemView),this.el=document.createElement(this.tagName),this.el.className=this.className,this.$el=$(this.el),this.parent=a.parent;var b=this.collection=this.get("collection");b.each(this.addItem,this),b.on("add",this.addItem,this),b.on("remove",this.removeItem,this)},addItem:function(a){a.view||(a.view=new this.itemView({model:a,parent:this.parent}),a.view.render()),this.prepend?this.$el.prepend(a.view.el):this.$el.append(a.view.el)},removeItem:function(a){a.view.remove()}})}(),function(a){var b=Backbone.Model.extend({});a.prototype.loadState=function(){var a="dataflow-"+(this.id?this.id:this.cid),c=JSON.parse(window.localStorage.getItem(a));c||(c={});var d=new b(c);this.set("state",d),d.on("change",function(b){window.localStorage.setItem(a,JSON.stringify(b.toJSON()))})}}(Dataflow),function(a){var b=a.prototype.module("graph"),c=a.prototype.module("node"),d=a.prototype.module("edge");b.Model=Backbone.Model.extend({defaults:{nodes:[],edges:[],panX:0,panY:0,zoom:1},initialize:function(){this.dataflow=this.get("dataflow");var a,b=this.nodes=new c.Collection;b.parentGraph=this,b.on("all",function(){this.trigger("change")},this),b.on("add",function(a){this.dataflow.trigger("node:add",this,a)},this),b.on("remove",function(a){a.remove(),this.dataflow.trigger("node:remove",this,a)},this);var e=this.get("nodes");for(a=0;a0;)this.nodes.remove(this.nodes.at(this.nodes.length-1))},toJSON:function(){return{nodes:this.nodes,edges:this.edges}}})}(Dataflow),function(a){var b=a.prototype.module("node"),c=a.prototype.module("input"),d=a.prototype.module("output");b.Model=Backbone.Model.extend({defaults:function(){return{label:"",description:"",type:"test",x:200,y:100,state:{},selected:!1}},initialize:function(){this.parentGraph=this.get("parentGraph"),this.type=this.get("type"),""===this.get("label")&&this.set({label:this.get("type")});var a=this.inputs;this.inputs=new c.Collection,this.inputs.parentNode=this;for(var b=0;b0;)this.connected[0].remove()}}),b.Collection=Backbone.Collection.extend({model:b.Model})}(Dataflow),function(a){var b=a.prototype.module("input"),c=a.prototype.module("output");c.Model=b.Model.extend({defaults:{id:"output",label:"",type:"all",description:""}}),c.Collection=Backbone.Collection.extend({model:c.Model})}(Dataflow),function(a){var b=a.prototype.module("edge"),c=Backbone.Model.extend({defaults:{type:"data",data:"",group:""}}),d=Backbone.Collection.extend({model:c});b.Model=Backbone.Model.extend({defaults:{z:0,route:0,selected:!1,log:null},initialize:function(){var a,b,c,e=this.get("preview");if(this.parentGraph=this.get("parentGraph"),this.attributes.log=new d,e){a=this.get("parentGraph").nodes;var f=this.get("source"),g=this.get("target");f?(b=a.get(this.get("source").node),this.source=b.outputs.get(this.get("source").port)):g&&(c=a.get(this.get("target").node),this.target=c.inputs.get(this.get("target").port))}else{a=this.parentGraph.nodes;try{b=a.get(this.get("source").node),this.source=b.outputs.get(this.get("source").port),c=a.get(this.get("target").node),this.target=c.inputs.get(this.get("target").port)}catch(h){}this.source.connect(this),this.target.connect(this),b.on("send:"+this.source.id,this.send,this),this.bringToTop(),this.on("select",this.select,this)}},select:function(){this.parentGraph.trigger("select:edge",this)},send:function(a){this.target.parentNode.recieve(this.target.id,a)},isConnectedToPort:function(a){return this.source===a||this.target===a},isConnectedToNode:function(a){return this.source.parentNode===a||this.target.parentNode===a},toString:function(){return this.id?this.id:this.get("source").node+":"+this.get("source").port+"::"+this.get("target").node+":"+this.get("target").port},toJSON:function(){return{source:this.get("source"),target:this.get("target"),route:this.get("route")}},bringToTop:function(){var a=0;this.parentGraph.edges.each(function(b){if(b!==this){var c=b.get("z");c>a&&(a=c),b.view&&b.view.unhighlight()}},this),this.set("z",a+1)},remove:function(){this.source.disconnect(this),this.target.disconnect(this),this.collection&&this.collection.remove(this),this.source.parentNode.off("send:"+this.source.id,this.send,this),this.trigger("remove")}}),b.Collection=Backbone.Collection.extend({model:b.Model,comparator:function(a){return a.get("z")}})}(Dataflow),function(a){var b=a.prototype.module("graph");a.prototype.module("node");var c=a.prototype.module("edge"),d=.2,e=1.1;document.createElement("div").style.hasOwnProperty("zoom");var f='
      ';b.View=Backbone.View.extend({template:_.template(f),className:"dataflow-g",events:{"click .dataflow-graph":"deselect","dragstart .dataflow-graph-panzoom":"panStart","drag .dataflow-graph-panzoom":"pan","dragstop .dataflow-graph-panzoom":"panStop","click .dataflow-graph-gotoparent":"gotoParent",mousewheel:"mouseWheel"},initialize:function(){this.$el.html(this.template(this.model.toJSON()));var a=this.model.get("nodes"),b=this.model.get("edges");this.nodes=a.view={},this.model.nodes.each(this.addNode,this),this.model.nodes.on("add",this.addNode,this),this.model.nodes.on("remove",this.removeNode,this),this.edges=b.view={},this.model.edges.each(this.addEdge,this),this.model.edges.on("add",this.addEdge,this),this.model.edges.on("remove",this.removeEdge,this);var c=this.model.get("parentNode");c||this.$(".dataflow-graph-controls").hide(),this.$(".dataflow-graph-panzoom").draggable({helper:function(){var a=$("
      ");return this.model.dataflow.$el.append(a),a}.bind(this)}),this.$graphEl=this.$(".dataflow-graph"),this.graphEl=this.$(".dataflow-graph")[0],this.$graphEl.css({transform:"translate3d(0, 0, 0) scale3d(1, 1, 1) ",transformOrigin:"left top"}),this.bindInteraction()},panStartOffset:null,panStart:function(a,b){b&&(this.panStartOffset=b.offset)},pan:function(a,b){if(b){var c=this.model.get("zoom"),d=b.offset.left-this.panStartOffset.left,e=b.offset.top-this.panStartOffset.top;this.$(".dataflow-graph").css({transform:"translate3d("+d/c+"px, "+e/c+"px, 0)"})}},panStop:function(a,b){this.$(".dataflow-graph").css({transform:"translate3d(0, 0, 0)"});var c=this.model.get("zoom"),d=b.offset.left-this.panStartOffset.left,e=b.offset.top-this.panStartOffset.top;this.model.set({panX:this.model.get("panX")+d/c,panY:this.model.get("panY")+e/c})},tempPanX:0,tempPanY:0,setPanDebounce:_.debounce(function(){this.$(".dataflow-graph").css({transform:"translate3d(0, 0, 0)"}),this.model.set({panX:this.model.get("panX")+this.tempPanX,panY:this.model.get("panY")+this.tempPanY}),this.tempPanX=0,this.tempPanY=0},250),mouseWheel:function(a){a.preventDefault();var b=a.originalEvent;this.tempPanX+=b.wheelDeltaX/6,this.tempPanY+=b.wheelDeltaY/6,this.$(".dataflow-graph").css({transform:"translate3d("+this.tempPanX+"px, "+this.tempPanY+"px, 0)"}),this.setPanDebounce()},gotoParent:function(){var a=this.model.get("parentNode");a&&this.model.dataflow.showGraph(a.parentGraph)},bindInteraction:function(){this.bindZoom(),this.bindScroll()},bindZoom:function(){if(window.Hammer){var a,b,c,f,g,h,i,j,k,l,m=this;Hammer(this.$(".dataflow-graph-panzoom")[0]).on("transformstart",function(d){a=m.model.get("zoom"),b=d.gesture.center.pageX,c=d.gesture.center.pageY,f=b/a,g=c/a;var e=m.$el.offset();k=f-e.left,l=g-e.top,m.$graphEl.css({transformOrigin:f+"px "+g+"px"})}).on("transform",function(f){h=Math.max(d/a,Math.min(f.gesture.scale,e/a)),i=(f.gesture.center.pageX-b)/a,j=(f.gesture.center.pageY-c)/a,m.$graphEl.css({transform:"translate3d("+i+"px,"+j+"px, 0) "+"scale3d("+h+","+h+", 1) "})}).on("transformend",function(){m.$graphEl.css({transform:"translate3d(0, 0, 0) scale3d(1, 1, 1) "});var b=a*h;b=Math.max(d,Math.min(b,e)),m.model.set("zoom",b),k*=b,l*=b,m.model.set({panX:m.model.get("panX")+i,panY:m.model.get("panY")+j}),console.log(m.model.attributes)});var n=function(){var a=m.model.get("zoom"),b=m.zoomClass;m.zoomClass=.5>a?"zoom-tiny":.8>a?"zoom-small":1.3>a?"zoom-normal":"zoom-big",m.$graphEl.removeClass(b).addClass(m.zoomClass),m.graphEl.style.zoom=m.model.get("zoom")};this.model.on("change:zoom",n),1!==this.model.get("zoom")&&n()}},zoomClass:1,zoomIn:function(){var a=this.model.get("zoom"),b=.9*a;b=Math.max(d,b),b!==a&&this.model.set("zoom",b)},zoomOut:function(){var a=this.model.get("zoom"),b=1.1*a;b=Math.min(e,b),b!==a&&this.model.set("zoom",b)},zoomCenter:function(){var a=this.model.get("zoom"),b=1;b!==a&&this.model.set("zoom",1)},bindScroll:function(){},render:function(){var a=this;return _.defer(function(){a.rerenderEdges()},this),this},addNode:function(a){var b=this.model.dataflow.nodes[a.type];if(b&&b.View)a.view=new b.View({model:a,graph:this});else{var c=this.model.dataflow.node("base");a.view=new c.View({model:a,graph:this})}this.nodes[a.id]=a.view,a.view.render(),this.$(".dataflow-nodes").append(a.view.el)},removeNode:function(a){a.view.remove(),this.nodes[a.id]=null,delete this.nodes[a.id]},addEdge:function(a){a.view=new c.View({model:a}),this.edges[a.id]=a.view,a.view.render(),this.$(".dataflow-svg-edges")[0].appendChild(a.view.el)},removeEdge:function(a){a.view&&a.view.remove(),this.edges[a.id]=null,delete this.edges[a.id]},rerenderEdges:function(){_.each(this.edges,function(a){a.render()},this)},sizeSVG:function(){try{var a=this.$(".dataflow-svg-edges")[0],b=a.getBBox(),c=Math.max(Math.round(b.x+b.width+50),50),d=Math.max(Math.round(b.y+b.height+50),50);a.setAttribute("width",c),a.setAttribute("height",d)}catch(e){}},deselect:function(){this.model.nodes.invoke("set",{selected:!1}),this.model.edges.invoke("set",{selected:!1}),this.model.trigger("selectionChanged"),this.unfade(),this.model.dataflow.hideCards()},fade:function(){this.model.nodes.each(function(a){a.view&&(a.get("selected")||a.view.fade())}),this.fadeEdges()},fadeEdges:function(){this.model.edges.each(function(a){a.get("selected")||a.source.parentNode.get("selected")||a.target.parentNode.get("selected")?a.view.unfade():a.view.fade()})},unfade:function(){this.model.nodes.each(function(a){a.view&&a.view.unfade()}),this.model.edges.each(function(a){a.view&&a.view.unfade()})}})}(Dataflow),function(a){var b,c=a.prototype.module("node"),d=a.prototype.module("input"),e=a.prototype.module("output"),f='

      <%- label %>

      ',g="";c.View=Backbone.View.extend({template:_.template(f),innerTemplate:_.template(g),className:"dataflow-node",events:function(){return{"click .dataflow-node-header":"select",dragstart:"dragStart",drag:"drag",dragstop:"dragStop"}},initialize:function(a){this.$el.html(this.template(this.model.toJSON())),this.graph=a.graph,this.$el.addClass(this.model.type),this.model.parentGraph.dataflow.editable||this.$(".dataflow-node-edit").hide(),this.inputs=this.model.inputs.view=new d.CollectionView({collection:this.model.inputs,parent:this}),this.outputs=this.model.outputs.view=new e.CollectionView({collection:this.model.outputs,parent:this}),this.$el.draggable({handle:"h1",helper:function(){return $("
      ")}}),this.$el.data("dataflow-node-view",this),this.$(".dataflow-node-inner").append(this.innerTemplate),this.listenTo(this.model.parentGraph,"change:panX change:panY",this.bumpPosition),this.listenTo(this.model,"change:selected",this.selectedChanged),this.listenTo(this.model,"change:label",this.changeLabel),this.listenTo(this.model,"remove",this.hideInspector),this.$inner=this.$(".dataflow-node-inner")},render:function(){return this.$el.css({left:this.model.get("x")+this.model.parentGraph.get("panX"),top:this.model.get("y")+this.model.parentGraph.get("panY")}),this.$(".dataflow-node-ins").html(this.inputs.el),this.$(".dataflow-node-outs").html(this.outputs.el),this.$(".dataflow-node-controls").hide(),this.$(".label-edit").hide(),this},_alsoDrag:[],_dragDelta:{},$dragHelpers:$('
      '),dragStart:function(a,c){c&&(this.model.get("selected")||this.select(a,!0),a.stopPropagation(),b=this.model.parentGraph.get("zoom"),this.$dragHelpers.css({transform:"translate3d(0,0,0)"}),this.$el.parent().append(this.$dragHelpers),this._alsoDrag=this.model.collection.where({selected:!0}),_.each(this._alsoDrag,function(a){var b=a.view.$el,c=$('
      ').css({width:b.width(),height:b.height(),left:parseInt(b.css("left"),10),top:parseInt(b.css("top"),10)});this.$dragHelpers.append(c)},this))},changeLabel:function(){var a=this.model.get("label"),b=this.model.get("type");this.$(".dataflow-node-title").text(a).attr("title",a+": "+b)},drag:function(a,c){if(c){a.stopPropagation();var d=(c.position.left-c.originalPosition.left)/b,e=(c.position.top-c.originalPosition.top)/b;this.$dragHelpers.css({transform:"translate3d("+d+"px,"+e+"px,0)"})}},dragStop:function(a,c){if(c){a.stopPropagation(),this.model.parentGraph.get("panX"),this.model.parentGraph.get("panY");var d=(c.position.left-c.originalPosition.left)/b,e=(c.position.top-c.originalPosition.top)/b;this._alsoDrag.length&&(_.each(this._alsoDrag,function(a){a.view.moveToPosition(a.get("x")+d,a.get("y")+e)},this),this._alsoDrag=[]),this.$dragHelpers.empty(),this.$dragHelpers.remove()}},bumpPosition:function(){this.$el.css({left:this.model.get("x")+this.model.parentGraph.get("panX"),top:this.model.get("y")+this.model.parentGraph.get("panY")}),this.model.trigger("change:x change:y")},moveToPosition:function(a,b){this.model.set({x:a,y:b},{silent:!0}),this.bumpPosition()},removeModel:function(){this.model.remove()},bringToTop:function(){var a=0;this.model.collection.each(function(b){var c=parseInt(b.view.el.style.zIndex,10);c>a&&(a=c)},this),this.el.style.zIndex=a+1},select:function(a){a&&a.stopPropagation();var b=!1,c=this.model.get("selected");a&&(a.ctrlKey||a.metaKey)?(b=!0,c=!c,this.model.set("selected",c),c||this.fade()):(this.model.parentGraph.edges.invoke("set",{selected:!1}),this.model.parentGraph.nodes.invoke("set",{selected:!1}),this.model.parentGraph.view.fade(),c=!0,this.model.set("selected",!0)),this.bringToTop(),this.model.parentGraph.view.fadeEdges(),this.model.parentGraph.trigger("selectionChanged")},inspector:null,getInspector:function(){if(!this.inspector){var b=new c.InspectView({model:this.model}),d=a.prototype.module("card");this.inspector=new d.Model({dataflow:this.model.parentGraph.dataflow,card:b})}return this.inspector},showInspector:function(a){this.model.parentGraph.dataflow.addCard(this.getInspector(),a)},hideInspector:function(){this.model.parentGraph.dataflow.removeCard(this.getInspector())},fade:function(){this.$el.addClass("fade"),this.$el.removeClass("ui-selected")},unfade:function(){this.$el.removeClass("fade")},selectedChanged:function(){this.model.get("selected")?(this.highlight(),this.showInspector()):(this.unhighlight(),this.hideInspector())},highlight:function(){this.$el.removeClass("fade"),this.$el.addClass("ui-selected")},unhighlight:function(){this.$el.removeClass("ui-selected")}})}(Dataflow),function(a){var b=a.prototype.module("input"),c=a.prototype.module("edge"),d='',e=1;b.View=Backbone.View.extend({template:_.template(d),tagName:"li",className:"dataflow-port dataflow-in",events:{click:"getTopEdge",drop:"connectEdge","dragstart .dataflow-port-hole":"newEdgeStart","drag .dataflow-port-hole":"newEdgeDrag","dragstop .dataflow-port-hole":"newEdgeStop","dragstart .dataflow-port-plug":"changeEdgeStart","drag .dataflow-port-plug":"changeEdgeDrag","dragstop .dataflow-port-plug":"changeEdgeStop"},$input:null,initialize:function(a){this.$el.html(this.template(this.model.toJSON())),this.$el.addClass(this.model.get("type")),this.parent=a.parent;var b=this.parent.model,c=b.parentGraph;this.listenTo(b,"change:x change:y",function(){this._holePosition=null}.bind(this)),this.listenTo(c,"change:panX change:panY",function(){this._holePosition=null}.bind(this));var d=b.get("state");if(d&&d[this.model.id]&&this.$el.addClass("hasvalue"),this.model.parentNode.parentGraph.dataflow.editable){var e=this;if(this.$(".dataflow-port-plug").draggable({cursor:"pointer",helper:function(){var a=$('');return e.parent.graph.$el.append(a),a},disabled:!0,distance:10,delay:100}),this.$(".dataflow-port-hole").draggable({cursor:"pointer",helper:function(){var a=$('').data({port:e.model});return e.parent.graph.$el.append(a),a}}),this.$el.droppable({accept:".dataflow-port-plug.in, .dataflow-port-hole.out",activeClassType:"droppable-hover",refreshPositions:!0}),this.model.parentNode.parentGraph.dataflow.inputs){var f=this.model.get("type"),g=this.model.parentNode.get("state");if(a=this.model.get("options"),void 0!==a&&(_.isString(a)&&(a=a.split(" "),this.model.set("options",a)),_.isArray(a))){for(var h={},i=0;i').append(k).prepend(""+this.model.get("label")+" ");this.$input=l,this.model.connected.length&&l.addClass("connected"),this.model.on("connected",function(){this.$input.addClass("connected")},this),this.model.on("disconnected",function(){this.$input.removeClass("connected")},this)}}},renderInput:function(a,b){var c;if(b){c=$('').attr(f).addClass("int"===a?"input-int":"input-float"),"int"==a?c.change(this.inputInt.bind(this)):c.change(this.inputFloat.bind(this)),c;case"boolean":return c=$('
      '),c.change(this.inputBoolean.bind(this)),c;case"object":return c=$(''),c.on("change, keyup",this.inputObject.bind(this)),c;case"bang":return c=$(''),c.click(this.inputBang.bind(this)),c;default:return c=$(''),c.change(this.inputString.bind(this)),c}},setInputValue:function(a,b,c){return a?"SELECT"===a[0].tagName?($("option",a).each(function(){var a=$(this).data("val");$(this).prop("selected",a==c)}),void 0):"boolean"===b?(a.prop("checked",c),void 0):"object"===b?(a.text(JSON.stringify(c,null,2)),void 0):(a.val(c),void 0):void 0},inputSelect:function(a){var b=$(a.target).find(":selected").data("val");this.model.parentNode.setState(this.model.id,b)},inputInt:function(a){this.model.parentNode.setState(this.model.id,parseInt($(a.target).val(),10))},inputFloat:function(a){this.model.parentNode.setState(this.model.id,parseFloat($(a.target).val()))},inputString:function(a){this.model.parentNode.setState(this.model.id,$(a.target).val())},inputBoolean:function(a){this.model.parentNode.setState(this.model.id,$(a.target).prop("checked"))},inputObject:function(a){try{var b=JSON.parse($(a.target).text());this.model.parentNode.setState(this.model.id,b)}catch(c){}},inputBang:function(){this.model.parentNode.setBang(this.model.id)},render:function(){return this},newEdgeStart:function(a,b){if(b){a.stopPropagation(),b.helper.data({route:this.topRoute}),this.previewEdgeNew=new c.Model({target:{node:this.model.parentNode.id,port:this.model.id},parentGraph:this.model.parentNode.parentGraph,preview:!0,route:this.topRoute}),this.previewEdgeNewView=new c.View({model:this.previewEdgeNew});var d=this.model.parentNode.parentGraph.view.$(".dataflow-svg-edges")[0];d.appendChild(this.previewEdgeNewView.el),e=this.model.parentNode.parentGraph.get("zoom")}},newEdgeDrag:function(a,b){if(this.previewEdgeNewView&&b){a.stopPropagation(),b.position.top=a.clientY/e,b.position.left=a.clientX/e;var c=this.model.parentNode.parentGraph.view.el;b.position.left+=c.scrollLeft,b.position.top+=c.scrollTop,this.previewEdgeNewView.render({left:b.position.left-c.scrollLeft,top:b.position.top-c.scrollTop}),this.model.parentNode.parentGraph.view.sizeSVG()}},newEdgeStop:function(a){a.stopPropagation(),this.previewEdgeNewView.remove(),delete this.previewEdgeNew,delete this.previewEdgeNewView},getTopEdge:function(){var a,b=-1;return this.isConnected&&(this.model.parentNode.parentGraph.edges.each(function(c){var d=c.get("z");c.target===this.model&&d>b&&(a=c,b=d),c.view&&c.view.unhighlight()},this),a&&a.view&&a.view.bringToTop()),a},changeEdgeStart:function(a,b){if(b&&(a.stopPropagation(),this.isConnected)){var d=this.getTopEdge();if(d){d.remove(),b&&b.helper.data({port:d.source,route:d.get("route")}),this.previewEdgeChange=new c.Model({source:d.get("source"),route:d.get("route"),parentGraph:this.model.parentNode.parentGraph,preview:!0}),this.previewEdgeChangeView=new c.View({model:this.previewEdgeChange});var f=this.model.parentNode.parentGraph.view.$(".dataflow-svg-edges")[0];f.appendChild(this.previewEdgeChangeView.el),e=this.model.parentNode.parentGraph.get("zoom")}}},changeEdgeDrag:function(a,b){b&&(a.stopPropagation(),this.previewEdgeChange&&(this.previewEdgeChangeView.render(b.offset),this.model.parentNode.parentGraph.view.sizeSVG()))},changeEdgeStop:function(a){a.stopPropagation(),this.previewEdgeChange&&(this.previewEdgeChangeView.remove(),delete this.previewEdgeChange,delete this.previewEdgeChangeView)},connectEdge:function(a,b){var c=b.helper.data("port"),d=this.model.parentNode.parentGraph.edges.length;if(c.parentNode.parentGraph.dataflow!==this.model.parentNode.parentGraph.dataflow)return!1;var e=0;void 0!==b.helper.data("route")&&(e=b.helper.data("route")),this.model.parentNode.parentGraph.edges.add({id:c.parentNode.id+":"+c.id+"::"+this.model.parentNode.id+":"+this.model.id,parentGraph:this.model.parentNode.parentGraph,source:{node:c.parentNode.id,port:c.id},target:{node:this.model.parentNode.id,port:this.model.id},route:e}),b.helper.data("removeChangeEdge",db&&(a=c,b=d)}},this),a)this.bringToTop(a);else{try{this.$(".dataflow-port-plug").draggable("disable")}catch(c){}this.$(".dataflow-port-plug, .dataflow-port-hole").removeClass("active"),this.isConnected=!1}},topRoute:0,bringToTop:function(a){var b=a.get("route");void 0!==b&&(this.$(".dataflow-port-hole, .dataflow-port-plug").removeClass("route"+this.topRoute),this.$(".dataflow-port-hole, .dataflow-port-plug").addClass("route"+b),this.topRoute=b)}}),b.CollectionView=Backbone.CollectionView.extend({tagName:"ul",itemView:b.View})}(Dataflow),function(a){var b=a.prototype.module("output"),c=a.prototype.module("edge"),d='<%= label %>',e=1; -b.View=Backbone.View.extend({template:_.template(d),tagName:"li",className:"dataflow-port dataflow-out",events:{click:"getTopEdge",drop:"connectEdge","dragstart .dataflow-port-hole":"newEdgeStart","drag .dataflow-port-hole":"newEdgeDrag","dragstop .dataflow-port-hole":"newEdgeStop","dragstart .dataflow-port-plug":"changeEdgeStart","drag .dataflow-port-plug":"changeEdgeDrag","dragstop .dataflow-port-plug":"changeEdgeStop"},initialize:function(a){this.$el.html(this.template(this.model.toJSON())),this.$el.addClass(this.model.get("type")),this.parent=a.parent;var b=this.parent.model,c=b.parentGraph;if(this.listenTo(b,"change:x change:y change:w",function(){this._holePosition=null}.bind(this)),this.listenTo(c,"change:panX change:panY",function(){this._holePosition=null}.bind(this)),this.model.parentNode.parentGraph.dataflow.editable){var d=this;this.$(".dataflow-port-plug").draggable({cursor:"pointer",helper:function(){var a=$('');return d.parent.graph.$el.append(a),a},disabled:!0,distance:10,delay:100}),this.$(".dataflow-port-hole").draggable({cursor:"pointer",helper:function(){var a=$('').data({port:d.model});return d.parent.graph.$el.append(a),a}}),this.$el.droppable({accept:".dataflow-port-plug.out, .dataflow-port-hole.in",activeClassType:"droppable-hover"})}},render:function(){return this},newEdgeStart:function(a,b){if(a.stopPropagation(),b){b.helper.data({route:this.topRoute}),this.previewEdge=new c.Model({source:{node:this.model.parentNode.id,port:this.model.id},parentGraph:this.model.parentNode.parentGraph,preview:!0,route:this.topRoute}),this.previewEdgeView=new c.View({model:this.previewEdge});var d=this.model.parentNode.parentGraph.view.$(".dataflow-svg-edges")[0];d.appendChild(this.previewEdgeView.el),e=this.model.parentNode.parentGraph.get("zoom")}},newEdgeDrag:function(a,b){if(a.stopPropagation(),this.previewEdgeView&&b){b.position.top=a.clientY/e,b.position.left=a.clientX/e;var c=this.model.parentNode.parentGraph.view.el;b.position.left+=c.scrollLeft,b.position.top+=c.scrollTop,this.previewEdgeView.render({left:b.position.left-c.scrollLeft,top:b.position.top-c.scrollTop})}},newEdgeStop:function(a){a.stopPropagation(),this.previewEdgeView.remove(),delete this.previewEdge,delete this.previewEdgeView},getTopEdge:function(){var a,b=-1;return this.isConnected&&(this.model.parentNode.parentGraph.edges.each(function(c){var d=c.get("z");c.source===this.model&&d>b&&(a=c,b=d),c.view&&c.view.unhighlight()},this),a&&a.view&&a.view.bringToTop()),a},changeEdgeStart:function(a,b){if(b&&(a.stopPropagation(),this.isConnected)){var d=this.getTopEdge();if(d){d.remove(),b&&b.helper.data({port:d.target,route:d.get("route")}),this.previewEdgeChange=new c.Model({target:d.get("target"),route:d.get("route"),parentGraph:this.model.parentNode.parentGraph,preview:!0}),this.previewEdgeChangeView=new c.View({model:this.previewEdgeChange});var f=this.model.parentNode.parentGraph.view.$(".dataflow-svg-edges")[0];f.appendChild(this.previewEdgeChangeView.el),e=this.model.parentNode.parentGraph.get("zoom")}}},changeEdgeDrag:function(a,b){b&&(a.stopPropagation(),this.previewEdgeChange&&(this.previewEdgeChangeView.render(b.offset),this.model.parentNode.parentGraph.view.sizeSVG()))},changeEdgeStop:function(a){a.stopPropagation(),this.previewEdgeChange&&(this.previewEdgeChangeView.remove(),delete this.previewEdgeChange,delete this.previewEdgeChangeView)},connectEdge:function(a,b){var c=b.helper.data("port"),d=this.model.parentNode.parentGraph.edges.length;if(c.parentNode.parentGraph.dataflow!==this.model.parentNode.parentGraph.dataflow)return!1;var e=0;void 0!==b.helper.data("route")&&(e=b.helper.data("route")),this.model.parentNode.parentGraph.edges.add({id:this.model.parentNode.id+":"+this.model.id+"::"+c.parentNode.id+":"+c.id,parentGraph:this.model.parentNode.parentGraph,source:{node:this.model.parentNode.id,port:this.model.id},target:{node:c.parentNode.id,port:c.id},route:e}),b.helper.data("removeChangeEdge",db&&(a=c,b=d)}},this),a)this.bringToTop(a);else{try{this.$(".dataflow-port-plug").draggable("disable")}catch(c){}this.$(".dataflow-port-plug, .dataflow-port-hole").removeClass("active"),this.isConnected=!1}},topRoute:0,bringToTop:function(a){var b=a.get("route");void 0!==b&&(this.$(".dataflow-port-hole").removeClass("route"+this.topRoute),this.$(".dataflow-port-hole").addClass("route"+b),this.topRoute=b)}}),b.CollectionView=Backbone.CollectionView.extend({tagName:"ul",itemView:b.View})}(Dataflow),function(a){var b=a.prototype.module("edge"),c=function(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)"xlink:href"===d?c.setAttributeNS("http://www.w3.org/1999/xlink","href",b[d]):c.setAttribute(d,b[d]);return c},d=function(a,b){a.classList?a.classList.add(b):a.className="dataflow-edge "+b},e=function(a,b){a.classList?a.classList.remove(b):a.className="dataflow-edge"};b.View=Backbone.View.extend({tagName:"div",className:"dataflow-edge",positions:null,initialize:function(){this.positions={from:null,to:null},this.model.source&&this.model.source.parentNode.on("change:x change:y change:w",this.render,this),this.model.target&&this.model.target.parentNode.on("change:x change:y",this.render,this),this.model.source&&this.model.source.view&&(this.model.source.view.plugSetActive(),this.model.source.view.bringToTop(this.model)),this.model.target&&this.model.target.view&&(this.model.target.view.plugSetActive(),this.model.target.view.bringToTop(this.model)),this.el=c("g",{"class":"dataflow-edge"}),this.elEdge=c("path",{"class":"dataflow-edge-wire"}),this.elShadow=c("path",{"class":"dataflow-edge-shadow"}),void 0!==this.model.get("route")&&this.elEdge.setAttribute("class","dataflow-edge-wire route"+this.model.get("route"));var a=this;this.model.on("change:route",function(){a.elEdge.setAttribute("class","dataflow-edge-wire route"+a.model.get("route")),a.bringToTop()}),this.el.appendChild(this.elShadow),this.el.appendChild(this.elEdge),this.el.addEventListener("click",function(b){a.click(b)}),this.listenTo(this.model,"change:selected",this.selectedChange),this.listenTo(this.model,"remove",this.hideInspector)},render:function(a){var b,c=this.model.source,d=this.model.target;c?this.positions.from=c.view.holePosition():(b=this.model.parentGraph.dataflow.$el.parent().position(),graph=this.model.parentGraph.view.$el,this.positions.from={left:graph.scrollLeft()+a.left-5-b.left,top:graph.scrollTop()+a.top+5-b.top}),d?this.positions.to=d.view.holePosition():(b=this.model.parentGraph.dataflow.$el.parent().position(),graph=this.model.parentGraph.view.$el,this.positions.to={left:graph.scrollLeft()+a.left+15-b.left,top:graph.scrollTop()+a.top+5-b.top});var e=this.edgePath(this.positions);this.elEdge.setAttribute("d",e),this.elShadow.setAttribute("d",e),this.model.parentGraph&&this.model.parentGraph.view&&this.model.parentGraph.view.sizeSVG()},fade:function(){this.model.source.parentNode.get("selected")||this.model.target.parentNode.get("selected")||d(this.el,"fade")},unfade:function(){e(this.el,"fade")},selectedChange:function(){this.model.get("selected")?(this.highlight(),this.showInspector()):(this.unhighlight(),this.hideInspector()),this.model.parentGraph.trigger("selectionChanged")},highlight:function(){d(this.el,"highlight")},unhighlight:function(){e(this.el,"highlight")},edgePath:function(a){var b=20,c=a.to.left-b-(a.from.left+b),d=Math.floor(c/2),e=c-d,f=a.to.top-a.from.top,g=Math.floor(f/2),h=f-g,i="",j="";return Math.abs(f)>Math.abs(c)?f>0?c>0?(i=" L "+(a.from.left+b+d)+" "+(a.from.top+d),j=" L "+(a.to.left-b-e)+" "+(a.to.top-e)):0>c&&(i=" L "+(a.from.left+b+d)+" "+(a.from.top-d),j=" L "+(a.to.left-b-e)+" "+(a.to.top+e)):0>f&&(c>0?(i=" L "+(a.from.left+b+d)+" "+(a.from.top-d),j=" L "+(a.to.left-b-e)+" "+(a.to.top+e)):0>c&&(i=" L "+(a.from.left+b+d)+" "+(a.from.top+d),j=" L "+(a.to.left-b-e)+" "+(a.to.top-e))):Math.abs(f)0?f>0?(i=" L "+(a.from.left+b+g)+" "+(a.from.top+g),j=" L "+(a.to.left-b-h)+" "+(a.to.top-h)):0>f&&(i=" L "+(a.from.left+b-g)+" "+(a.from.top+g),j=" L "+(a.to.left-b+h)+" "+(a.to.top-h)):0>c&&(f>0?(i=" L "+(a.from.left+b-g)+" "+(a.from.top+g),j=" L "+(a.to.left-b+h)+" "+(a.to.top-h)):0>f&&(i=" L "+(a.from.left+b+g)+" "+(a.from.top+g),j=" L "+(a.to.left-b-h)+" "+(a.to.top-h)))),"M "+a.from.left+" "+a.from.top+" L "+(a.from.left+b)+" "+a.from.top+i+j+" L "+(a.to.left-b)+" "+a.to.top+" L "+a.to.left+" "+a.to.top},remove:function(){var a=this.model.source,b=this.model.target;a&&a.parentNode.off(null,null,this),b&&b.parentNode.off(null,null,this),a&&a.view.plugCheckActive(),b&&b.view.plugCheckActive(),this.el.parentNode.removeChild(this.el)},click:function(a){a&&a.stopPropagation();var b;a&&(a.ctrlKey||a.metaKey)?(b=this.model.get("selected"),b=!b):(b=!0,this.model.parentGraph.nodes.invoke("set",{selected:!1}),this.model.collection.invoke("set",{selected:!1})),this.model.set({selected:b}),b&&(this.bringToTop(),this.model.trigger("select"),this.unfade()),this.model.parentGraph.view.fade()},bringToTop:function(){this.model.bringToTop();var a=this.el.parentNode;a&&a.appendChild(this.el),this.model.source.view.bringToTop(this.model),this.model.target.view.bringToTop(this.model)},inspector:null,getInspector:function(){if(!this.inspector){var c=new b.InspectView({model:this.model}),d=a.prototype.module("card");this.inspector=new d.Model({dataflow:this.model.parentGraph.dataflow,card:c})}return this.inspector},showInspector:function(a){this.model.parentGraph.dataflow.addCard(this.getInspector(),a)},hideInspector:function(){this.model.parentGraph.dataflow.removeCard(this.getInspector())}})}(Dataflow),function(a){var b=a.prototype.module("card");b.Model=Backbone.Model.extend({defaults:{pinned:!1},initialize:function(){this.dataflow=this.get("dataflow")},hide:function(){this.dataflow.shownCards.remove(this)}}),b.Collection=Backbone.Collection.extend({model:b.Model})}(Dataflow),function(a){var b=a.prototype.module("card"),c='
      ';b.View=Backbone.View.extend({tagName:"div",className:"dataflow-card",template:_.template(c),events:{"click .dataflow-card-pin":"togglePin","click .dataflow-card-close":"hide"},initialize:function(){this.$el.html(this.template()),this.card=this.model.get("card"),this.$el.append(this.card.el),this.listenTo(this.model,"change:pinned",this.pinnedChanged),this.pinnedChanged()},animate:function(a){"function"==typeof this.card.animate&&this.card.animate(a)},togglePin:function(){var a=!this.model.get("pinned");this.model.set("pinned",a),a||this.hide()},pinnedChanged:function(){this.model.get("pinned")?this.$(".dataflow-card-pin").addClass("active"):this.$(".dataflow-card-pin").removeClass("active")},hide:function(){this.model.hide()},remove:function(){this.$el.detach()}}),window.requestAnimationFrame||(window.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,50)}}()),b.CollectionView=Backbone.CollectionView.extend({tagName:"div",className:"dataflow-cards",itemView:b.View,prepend:!0,initialize:function(){Backbone.CollectionView.prototype.initialize.apply(this,arguments);var a=function(b){window.requestAnimationFrame(a),this.collection.each(function(a){a.view&&a.view.animate(b)})}.bind(this);a()},bringToTop:function(a){this.$el.prepend(a.view.el)}})}(Dataflow),function(a){var b=Backbone.Model.extend({defaults:{label:"",icon:"",action:null}}),c=Backbone.Collection.extend({model:b}),d=a.prototype.module("card"),e=a.prototype.module("menucard");e.Model=d.Model.extend({initialize:function(){this.menu=new c,d.Model.prototype.initialize.call(this)}})}(Dataflow),function(a){var b=a.prototype.module("card"),c=a.prototype.module("menucard"),d=Backbone.View.extend({tagName:"li",template:'',events:{click:"clicked"},render:function(){this.$el.html(_.template(this.template,this.model.toJSON()))},clicked:function(){this.model.get("action")&&this.model.get("action")()}});c.View=b.View.extend({initialize:function(){this.model.set("card",new Backbone.CollectionView({tagName:"ul",className:"dataflow-menu",collection:this.model.menu,itemView:d})),b.View.prototype.initialize.call(this)}})}(Dataflow),function(a){var b=a.prototype.module("node"),c='

      <%- label %>

      <%- type %>

      ',d=function(a,b,c){a[0].contentEditable=!0;var d=a.text(),e=function(){b.set(c,a.text())},f=function(){a.text(d)};a.focus(function(){d=a.text()}).blur(function(){e()}).keydown(function(b){27===b.which?(f(),a.blur()):13===b.which&&a.blur()})};b.InspectView=Backbone.View.extend({template:_.template(c),className:"dataflow-node-inspector",events:{},initialize:function(){this.$el.html(this.template(this.model.toJSON()));var a=this.$el.children(".dataflow-node-inspector-inputs");this.model.inputs.each(function(b){b.view&&b.view.$input&&a.append(b.view.$input)},this),d(this.$(".dataflow-node-inspector-label"),this.model,"label")},render:function(){return this},removeModel:function(){this.model.remove()}})}(Dataflow),function(a){var b=a.prototype.module("edge"),c='

      Edge

      <%= id %>

        ',d='
      • <%- group %><%- data %>
      • ';b.InspectView=Backbone.View.extend({tagName:"div",className:"dataflow-edge-inspector",positions:null,template:_.template(c),showLogs:20,initialize:function(){var a=this.model.toJSON();this.model.id&&(a.id=this.model.id.replace("->","→")),this.$el.html(this.template(a));var b=this.$el.children(".dataflow-edge-inspector-route-choose");this.$log=this.$el.children(".dataflow-edge-inspector-events");for(var c=function(a){var b=$(a.target).data("route");this.model.set("route",b)}.bind(this),d=0;12>d;d++){var e=$("



        ');h.children(".selectall").click(c),b.selectAll=c,b.removeSelected=function(){var b=a.currentGraph.nodes.where({selected:!0});_.each(b,function(a){a.remove()})},h.children(".cut").click(d),b.cut=d,b.removeEdge=e;var i={};h.children(".copy").click(f),b.copy=f,h.children(".paste").click(g),b.paste=g,a.addContext({id:"cut",icon:"cut",label:"cut",action:d,contexts:["node","nodes"]}),a.addContext({id:"copy",icon:"copy",label:"copy",action:f,contexts:["node","nodes"]}),a.addContext({id:"paste",icon:"paste",label:"paste",action:g,contexts:["node","nodes"]}),a.addContext({id:"edgeRemove",icon:"remove",label:"remove edge",action:e,contexts:["edge"]}),a.addContext({id:"edgeRemove",icon:"remove",label:"remove edges",action:e,contexts:["edges"]}),b.onSearch=function(b,c){if(a.currentGraph){var d=[];a.currentGraph.nodes.each(function(a){-1!==a.get("label").toLowerCase().indexOf(b.toLowerCase())&&d.push({source:"edit",icon:"sign-blank",label:a.get("label"),description:a.type,action:function(){a.view.select()}})}),c(d)}}}}(Dataflow),function(a){var b=a.prototype.plugin("elements");b.list=[{type:"div",attributes:["id","class","style"],events:["pointermove","pointerover","pointerout"]},{type:"button",attributes:["id","class","style"],events:["pointerdown","pointerup"]}]}(Dataflow),function(a){var b=a.prototype.plugin("library");b.initialize=function(a){var c=$('
        '),d=$('
          ');c.append(d),b.excluded=["base","base-resizable"];var e=function(b,c,d){return function(){a.currentGraph.view.$(".dataflow-node").removeClass("ui-selected"),zoom=a.currentGraph.get("zoom");for(var e=1;a.currentGraph.nodes.get(e);)e++;c=void 0===c?200:c,d=void 0===d?200:d,c=c/zoom-a.currentGraph.get("panX"),d=d/zoom-a.currentGraph.get("panY");var f=new b.Model({id:e,x:c,y:d,parentGraph:a.currentGraph});a.currentGraph.nodes.add(f),f.view.select()}},f='
        • <%- name %><%-description %>
        • ',g=function(b,c){var g=$(_.template(f,{name:b,description:c.description}));$(".button",g).attr("title","click or drag").draggable({helper:function(){var c=$('
          '+b+"
          ");return a.$el.append(c),c},stop:function(a,b){e(c,b.position.left,b.position.top).call()}}).click(e(c)),d.append(g)},h=function(c){c=c?c:{},b.excluded=c.exclude?c.exclude:b.excluded,d.empty();var e=_.sortBy(Object.keys(a.nodes),function(a){return a});_.each(e,function(c){-1===b.excluded.indexOf(c)&&g(c,a.nodes[c])})};h(),a.addPlugin({id:"library",label:"library",name:"",menu:c,icon:"plus",pinned:!1}),b.update=h,b.onSearch=function(c,d){var f=[];_.each(a.nodes,function(a,d){-1===b.excluded.indexOf(d)&&-1!==d.toLowerCase().indexOf(c.toLowerCase())&&f.push({source:"library",icon:"plus",action:function(){e(a).call()},label:d,description:a.description})}),d(f)}}}(Dataflow),function(a){var b=a.prototype.plugin("source");b.updateAllowed=!0,b.initialize=function(a){var c=$('

          '),d=c.find(".code");a.addPlugin({id:"source",label:"view source",name:"",menu:c,icon:"code",pinned:!0}),b.show=function(a){var b=d.prop("scrollTop");d.val(a),d.scrollTop(b)};var e=function(){a.graph&&b.show(JSON.stringify(a.graph.toJSON(),null," "))};b.listeners=function(b){b?a.on("change",e):a.off("change",e)},b.listeners(!0),b.allowUpdate=function(a){var e=c.find(".apply");return a?(b.updateAllowed=!0,e.show(),d.removeAttr("readonly"),void 0):(b.updateAllowed=!1,e.hide(),d.attr("readonly","readonly"),void 0)},c.submit(function(){return b.updateGraph(d,a),!1})},b.updateGraph=function(a,c){if(b.updateAllowed){var d;try{d=JSON.parse(a.val())}catch(e){return c.log("Invalid JSON"),!1}if(d){var f=c.loadGraph(d);f.trigger("change")}}}}(Dataflow),function(a){var b=a.prototype.plugin("log");b.initialize=function(a){function c(a){a=_.escape(a),d.children(".loglist").append("
        • "+a+"
        • "),d.scrollTop(d.prop("scrollHeight"))}var d=$('
            ');a.addPlugin({id:"log",label:"log",name:"",menu:d,icon:"th-list",pinned:!0}),b.add=c;var e=function(a){c("log: "+a)},f=function(a,b){c("node added: "+b.toString())},g=function(a,b){c("node removed: "+b.toString())},h=function(a,b){c("edge added: "+b.toString())},i=function(a,b){c("edge removed: "+b.toString())};b.listeners=function(b){b?(a.on("log",e),a.on("node:add",f),a.on("node:remove",g),a.on("edge:add",h),a.on("edge:remove",i)):(a.off("log",e),a.off("node:add",f),a.off("node:remove",g),a.off("edge:add",h),a.off("edge:remove",i))},b.listeners(!0)}}(Dataflow),function(a){var b=a.prototype.plugin("inspector");b.initialize=function(a){function b(){var b=a.currentGraph.nodes.where({selected:!0});b.forEach(function(b){var c=b.view.getInspector();c.set("pinned",!0),a.addCard(c)});var c=a.currentGraph.edges.where({selected:!0});c.forEach(function(b){var c=b.view.getInspector();c.set("pinned",!0),a.addCard(c)})}a.addContext({id:"inspector",icon:"info-sign",label:"inspect",action:b,contexts:["one","twoplus"]})}}(Dataflow),function(a){var b=a.prototype.plugin("keybinding"),c=a.prototype.plugin("edit");b.initialize=function(a){function d(){a&&a.currentGraph&&a.currentGraph.view&&a.currentGraph.view.zoomIn()}function e(){a&&a.currentGraph&&a.currentGraph.view&&a.currentGraph.view.zoomOut()}function f(){a&&a.currentGraph&&a.currentGraph.view&&a.currentGraph.view.zoomCenter()}function g(a){if("TEXTAREA"!==a.target.tagName&&"INPUT"!==a.target.tagName&&"true"!==a.target.contentEditable&&(a.ctrlKey||a.metaKey))switch(a.which){case 189:a.preventDefault(),d();break;case 187:a.preventDefault(),e();break;case 48:a.preventDefault(),f();break;case 65:c.selectAll();break;case 88:c.cut();break;case 67:c.copy();break;case 86:c.paste();break;case 90:}}b.listeners=function(a){a?$(document).on("keydown",g):$(document).off("keydown",g)},b.listeners(!0)}}(Dataflow),function(a){var b=a.prototype.plugin("notification"),c=window.webkitNotifications?!0:!1;b.requestPermission=function(){c&&(b.hasPermission()||window.webkitNotifications.requestPermission())},b.hasPermission=function(){return c?0!==window.webkitNotifications.checkPermission()?!1:!0:!1},b.notify=function(a,c,d){if(!b.hasPermission()){if(!console||!console.log)return;return console.log(c+": "+d),void 0}var e=window.webkitNotifications.createNotification(a,c,d);e.show()}}(Dataflow),function(a){var b=a.prototype.plugin("search"),c=Backbone.Model.extend({defaults:{source:"",icon:"",action:null,label:"",description:""}}),d=Backbone.Collection.extend({model:c,initialize:function(a,b){b||(b={}),this.search=b.search}}),e=Backbone.View.extend({tagName:"li",template:'<%- label %><%- description %>',events:{click:"clicked"},render:function(){this.$el.html(_.template(this.template,this.model.toJSON()))},clicked:function(){this.model.get("action")&&this.model.get("action")()}});b.initialize=function(a){var c=$(''),d=c.find("input"),e=c.find("button");a.$el.prepend(c),d.on("keyup search webkitspeechchange",function(){return d.val()?(b.search(d.val(),a),void 0):(a.removeCard("searchresults"),void 0)}),e.on("click",function(){a.showPlugin("menu")})},b.search=function(c,f){var g=a.prototype.module("card"),h=new d([],{search:c}),i=new Backbone.CollectionView({tagName:"ul",className:"dataflow-plugin-search-results",collection:h});i.itemView=e;var j=new g.Model({id:"searchresults",dataflow:f,card:i,pinned:!1});h.on("add",function(){f.addCard(j)}),b.results=h,_.each(f.plugins,function(a){a.onSearch&&b.searchPlugin(h,c,a)})},b.searchPlugin=function(a,c,d){d.onSearch(c,function(d){c===b.results.search&&d.forEach(function(b){a.add(b)})})}}(Dataflow),function(a){var b=a.prototype.module("node"),c=a.prototype.node("base");c.Model=b.Model.extend({defaults:function(){var a=b.Model.prototype.defaults.call(this);return a.type="base",a},initialize:function(){b.Model.prototype.initialize.call(this)},unload:function(){},inputs:[],outputs:[]}),c.View=b.View.extend({})}(Dataflow),function(a){var b=a.prototype.node("base"),c=a.prototype.node("base-resizable");c.Model=b.Model.extend({defaults:function(){var a=b.Model.prototype.defaults.call(this);return a.type="base-resizable",a.w=200,a.h=200,a},initialize:function(){b.Model.prototype.initialize.call(this)},unload:function(){},toJSON:function(){var a=b.Model.prototype.toJSON.call(this);return a.w=this.get("w"),a.h=this.get("h"),a},inputs:[],outputs:[]}),c.View=b.View.extend({initialize:function(a){b.View.prototype.initialize.call(this,a),this.$el.css({width:this.model.get("w"),height:this.model.get("h")});var c=this;this.$el.resizable({helper:"dataflow-node helper",minHeight:100,minWidth:120,stop:function(a,b){c.resizeStop(a,b)}})},resizeStop:function(a,b){this.model.set({w:b.size.width,h:b.size.height})}})}(Dataflow),function(a){var b=a.prototype.node("base-resizable"),c=a.prototype.node("dataflow-subgraph"),d=a.prototype.module("graph"),e=a.prototype.module("input"),f=a.prototype.module("output");c.Model=b.Model.extend({defaults:function(){var a=b.Model.prototype.defaults.call(this);return a.label="subgraph",a.type="dataflow-subgraph",a.graph={nodes:[{id:"1",label:"in",type:"dataflow-input",x:180,y:15},{id:"99",label:"out",type:"dataflow-output",x:975,y:500}]},a},initialize:function(){b.Model.prototype.initialize.call(this);var a=this.get("graph");a.parentNode=this,a.dataflow=this.parentGraph.dataflow,this.graph=new d.Model(a);var c=this.graph.nodes.filter(function(a){return"dataflow-input"===a.type});_.each(c,this.addInput,this);var e=this.graph.nodes.filter(function(a){return"dataflow-output"===a.type});_.each(e,this.addOutput,this),this.graph.nodes.on("add",function(a){"dataflow-input"===a.type?this.addInput(a):"dataflow-output"===a.type&&this.addOutput(a)},this),this.graph.nodes.on("remove",function(a){"dataflow-input"===a.type?this.removeInput(a):"dataflow-output"===a.type&&this.removeOutput(a)},this)},addInput:function(a){var b=new e.Model({id:a.id,label:a.get("label"),type:a.get("input-type"),parentNode:this,inputNode:a});this.inputs.add(b)},recieve:function(a,b){var c=this.inputs.get(a).get("inputNode");c&&c.send("data",b)},addOutput:function(a){var b=new f.Model({id:a.id,label:a.get("label"),type:a.get("output-type"),parentNode:this,outputNode:a});this.outputs.add(b),a.set("parentNode",this)},removeInput:function(a){var b=this.inputs.get(a.id);b.remove(),this.inputs.remove(b)},removeOutput:function(a){var b=this.outputs.get(a.id);b.remove(),this.outputs.remove(b)},toJSON:function(){var a=b.Model.prototype.toJSON.call(this);return a.graph=this.graph,a},remove:function(){b.Model.prototype.remove.call(this),this.graph.remove()},inputs:[],outputs:[]});var g='';c.View=b.View.extend({events:function(){var a=b.View.prototype.events.call(this);return a["click .show-subgraph"]="showSubgraph",a},innerTemplate:_.template(g),initialize:function(a){b.View.prototype.initialize.call(this,a),this.model.graph.view=new d.View({model:this.model.graph}),this.model.inputs.each(this.addInput,this),this.model.inputs.on("add",this.addInput,this),this.model.outputs.each(this.addOutput,this),this.model.outputs.on("add",this.addOutput,this)},addInput:function(a){a.get("inputNode")&&a.get("inputNode").on("change:label",function(b){a.view.$(".label").text(b.get("label"))},this)},addOutput:function(a){a.get("outputNode")&&a.get("outputNode").on("change:label",function(b){a.view.$(".label").text(b.get("label"))},this)},showSubgraph:function(){this.model.graph.dataflow.showGraph(this.model.graph)}})}(Dataflow); -//# sourceMappingURL=dataflow.min.js.map \ No newline at end of file +function CircularBuffer(e){this._array=Array(e),this.length=0}CircularBuffer.prototype.toString=function(){return"[object CircularBuffer("+this._array.length+") length "+this.length+"]"},CircularBuffer.prototype.get=function(e){return 0>e||this.length-this._array.length>e?void 0:this._array[e%this._array.length]},CircularBuffer.prototype.set=function(e,t){if(0>e||this.length-this._array.length>e)throw CircularBuffer.IndexError;for(;e>this.length;)this._array[this.length%this._array.length]=void 0,this.length++;this._array[e%this._array.length]=t,e==this.length&&this.length++},CircularBuffer.prototype.push=function(e){this._array[this.length%this._array.length]=e,this.length++},CircularBuffer.IndexError={},function(){var e=Backbone.Model.extend({$:function(e){return this.$el.find(e)},initialize:function(){this.el=document.createElement("div"),this.el.className="dataflow",this.$el=$(this.el),this.$el.data("dataflow",this);var e=Dataflow.prototype.module("card");if(this.shownCards=new e.Collection,this.shownCards.view=new e.CollectionView({collection:this.shownCards}),this.$el.append(this.shownCards.view.$el),this.debug=this.get("debug"),this.controls=this.get("controls"),this.controls!==!1&&(this.controls=!0),this.controls)for(var t in this.plugins)this.plugins[t].initialize&&this.plugins[t].initialize(this);this.inputs=this.get("inputs"),this.inputs!==!1&&(this.inputs=!0),this.editable=this.get("editable"),this.editable!==!1&&(this.editable=!0);var o=this.get("appendTo");o=o?o:"body","body"===o&&$("html, body").css({margin:"0px",padding:"0px",width:"100%",height:"100%"}),$(o).append(this.el),this.id||(this.id=$(o).attr("id")),this.loadState()},modules:{},module:function(e){return this.modules[e]?this.modules[e]:(this.modules[e]={},this.modules[e])},nodes:{},node:function(e){return this.nodes[e]?this.nodes[e]:(this.nodes[e]={description:""},this.nodes[e])},plugins:{},plugin:function(e){return this.plugins[e]?this.plugins[e]:(this.plugins[e]={},this.plugins[e])},addCard:function(e,t){t||this.hideCards(),this.shownCards.get(e)?this.shownCards.view.bringToTop(e):this.shownCards.add(e)},removeCard:function(e){this.shownCards.remove(e)},hideCards:function(){var e=this.shownCards.where({pinned:!1});this.shownCards.remove(e)},addPlugin:function(e){var t=this.plugins[e.id];if(t||(this.plugins[e.id]=t={}),t.info=e,t.enabled=!0,e.menu){var o=Dataflow.prototype.module("card"),i=new o.Model({dataflow:this,card:{el:e.menu},pinned:e.pinned?!0:!1});t.card=i,this.plugins.menu.addPlugin({id:e.id,icon:e.icon,label:e.label,showLabel:!1})}},showPlugin:function(e){this.plugins[e]&&this.plugins[e].card&&(this.addCard(this.plugins[e].card),"function"==typeof this.plugins[e].onShow&&this.plugins[e].onShow())},enablePlugin:function(e){var t=this.plugins[e];t&&this.addPlugin(t.info)},disablePlugin:function(e){this.plugins.menu.disablePlugin(e)},showContextBar:function(){this.contextBar.view.$el.show()},hideContextBar:function(){this.contextBar.view.$el.hide()},contexts:{},prepareContext:function(e){if(this.contexts[e])return this.contexts[e];var t=this.module("menucard");return this.contexts[e]=new t.Model({id:"context-"+e,dataflow:this,pinned:!0}),this.contexts[e].view=new t.View({model:this.contexts[e]}),this.contexts[e]},addContext:function(e){_.each(e.contexts,function(t){var o=this.prepareContext(t);o.menu.add(e)},this)},changeContext:function(e,t){var o=function(e,t){this.contexts[e]&&(this.contexts[e].set("label",t),this.shownCards.get("context-"+e)||this.shownCards.add(this.contexts[e]))}.bind(this),i=function(e){this.shownCards.get("context-"+e)&&this.shownCards.remove("context-"+e)}.bind(this);e.length>1?(o("nodes",e.length+" nodes"),i("node")):1===e.length?(o("node",e[0].get("label")),i("nodes")):(i("node"),i("nodes")),t.length>1?(o("edges",t.length+" edges"),i("edge")):1===t.length?(o("edge",t[0].id),i("edges")):(i("edge"),i("edges"))},loadGraph:function(e){this.graph&&(this.currentGraph.view&&this.currentGraph.view.remove(),this.graph.view&&this.graph.view.remove(),this.graph.remove());var t=this.module("graph");e.dataflow=this;var o=new t.Model(e);return o.view=new t.View({model:o}),this.$el.append(o.view.render().el),this.graph=this.currentGraph=o,o},showGraph:function(e){this.currentGraph.view.$el.detach(),this.$el.append(e.view.el),e.view.render(),this.currentGraph=e},debug:!1,log:function(e){this.trigger("log",e,arguments),this.debug&&console.log("Dataflow: ",arguments)},types:["all","canvas:2d","canvas:webgl","string","number","int","object","array"]});window.Dataflow=e,"object"==typeof exports&&(exports.Dataflow=e),Backbone.View.prototype.addEvents=function(e){this.delegateEvents(_.extend(_.clone(this.events),e))},Backbone.CollectionView=Backbone.Model.extend({prepend:!1,initialize:function(e){e.tagName&&(this.tagName=e.tagName),e.className&&(this.className=e.className),e.itemView&&(this.itemView=e.itemView),this.el=document.createElement(this.tagName),this.el.className=this.className,this.$el=$(this.el),this.parent=e.parent;var t=this.collection=this.get("collection");t.each(this.addItem,this),t.on("add",this.addItem,this),t.on("remove",this.removeItem,this)},addItem:function(e){e.view||(e.view=new this.itemView({model:e,parent:this.parent}),e.view.render()),this.prepend?this.$el.prepend(e.view.el):this.$el.append(e.view.el)},removeItem:function(e){e.view.remove()}})}(),function(e){var t=Backbone.Model.extend({});e.prototype.loadState=function(){var e="dataflow-"+(this.id?this.id:this.cid),o=JSON.parse(window.localStorage.getItem(e));o||(o={});var i=new t(o);this.set("state",i),i.on("change",function(t){window.localStorage.setItem(e,JSON.stringify(t.toJSON()))})}}(Dataflow),function(e){var t=e.prototype.module("graph"),o=e.prototype.module("node"),i=e.prototype.module("edge");t.Model=Backbone.Model.extend({defaults:{nodes:[],edges:[],panX:0,panY:0,zoom:1},initialize:function(){this.dataflow=this.get("dataflow");var e,t=this.nodes=new o.Collection;t.parentGraph=this,t.on("all",function(){this.trigger("change")},this),t.on("add",function(e){this.dataflow.trigger("node:add",this,e)},this),t.on("remove",function(e){e.remove(),this.dataflow.trigger("node:remove",this,e)},this);var n=this.get("nodes");for(e=0;n.length>e;e++){var a=n[e];a.parentGraph=this,a.type&&this.dataflow.nodes[a.type]?(a=new this.dataflow.nodes[a.type].Model(a),t.add(a)):this.dataflow.log("node "+a.id+" not added: node type ("+a.type+") not found",a)}var s=this.edges=new i.Collection;s.parentGraph=this,s.on("all",function(){this.trigger("change")},this),s.on("add",function(e){this.dataflow.trigger("edge:add",this,e)},this),s.on("remove",function(e){this.dataflow.trigger("edge:remove",this,e)},this);var r=this.get("edges");for(e=0;r.length>e;e++){var d=r[e];d.parentGraph=this,d.id=d.source.node+":"+d.source.port+"::"+d.target.node+":"+d.target.port;var l=t.get(d.source.node),h=t.get(d.target.node);l&&h&&l.outputs.get(d.source.port)&&h.inputs.get(d.target.port)?(d=new i.Model(d),s.add(d)):this.dataflow.log("edge "+d.id+" not added: node or port not found",d)}this.set({nodes:t,edges:s}),this.on("selectionChanged",this.selectionChanged,this),this.on("select:node",this.selectNode,this),this.on("select:edge",this.selectEdge,this),this.on("change",function(){this.dataflow.trigger("change",this)},this)},selectNode:function(e){this.dataflow.trigger("select:node",this,e)},selectEdge:function(e){this.dataflow.trigger("select:edge",this,e)},selectionChanged:function(){var e=this.nodes.where({selected:!0}),t=this.edges.where({selected:!0});this.dataflow.changeContext(e,t)},remove:function(){for(;this.nodes.length>0;)this.nodes.remove(this.nodes.at(this.nodes.length-1))},toJSON:function(){return{nodes:this.nodes,edges:this.edges}}})}(Dataflow),function(e){var t=e.prototype.module("node"),o=e.prototype.module("input"),i=e.prototype.module("output");t.Model=Backbone.Model.extend({defaults:function(){return{label:"",description:"",type:"test",x:200,y:100,state:{},selected:!1}},initialize:function(){this.parentGraph=this.get("parentGraph"),this.type=this.get("type"),""===this.get("label")&&this.set({label:this.get("type")});var e=this.inputs;this.inputs=new o.Collection,this.inputs.parentNode=this;for(var t=0;e.length>t;t++){var n=e[t],a=this.get("state");void 0!==n.value&&void 0===a[n.id]&&(a[n.id]=n.value),n.parentNode=this,n=new o.Model(n),this.inputs.add(n)}var s=this.outputs;for(this.outputs=new i.Collection,this.outputs.parentNode=this,t=0;s.length>t;t++){var r=s[t];r.parentNode=this,r=new i.Model(r),this.outputs.add(r)}this.on("change:selected",this.changeSelected,this)},changeSelected:function(){this.get("selected")&&this.parentGraph.trigger("select:node",this)},setState:function(e,t){var o=this.get("state");o[e]!==t&&(o[e]=t,this["input"+e]&&this["input"+e](t),this.trigger("change:state",e,t))},setBang:function(e){this["input"+e]&&this["input"+e](),this.trigger("bang",e)},send:function(e,t){var o=this;_.defer(function(){o.trigger("send:"+e,t)})},recieve:function(e,t){"function"==typeof this["input"+e]?this["input"+e](t):this["_"+e]=t},remove:function(){this.inputs.each(function(e){e.remove()}),this.outputs.each(function(e){e.remove()}),this.unload(),this.collection.remove(this),this.trigger("remove")},unload:function(){},toString:function(){return this.id+" ("+this.type+")"},toJSON:function(){return{id:this.get("id"),label:this.get("label"),type:this.get("type"),x:this.get("x"),y:this.get("y"),state:this.get("state")}},inputs:[],outputs:[]}),t.Collection=Backbone.Collection.extend({model:t.Model,comparator:function(e){return e.get("x")}})}(Dataflow),function(e){var t=e.prototype.module("input");t.Model=Backbone.Model.extend({defaults:{id:"input",description:"",label:"",type:"all"},initialize:function(){this.parentNode=this.get("parentNode"),""===this.get("label")&&this.set({label:this.id}),this.connected=[]},connect:function(e){this.connected.push(e),this.connected=_.uniq(this.connected),this.trigger("connected")},disconnect:function(e){this.connected=_.without(this.connected,e),0===this.connected.length&&this.trigger("disconnected")},remove:function(){for(;this.connected.length>0;)this.connected[0].remove()}}),t.Collection=Backbone.Collection.extend({model:t.Model})}(Dataflow),function(e){var t=e.prototype.module("input"),o=e.prototype.module("output");o.Model=t.Model.extend({defaults:{id:"output",label:"",type:"all",description:""}}),o.Collection=Backbone.Collection.extend({model:o.Model})}(Dataflow),function(e){var t=e.prototype.module("edge");t.Model=Backbone.Model.extend({defaults:{z:0,route:0,selected:!1,log:null},initialize:function(){var e,t,o,i=this.get("preview");if(this.parentGraph=this.get("parentGraph"),this.attributes.log=new CircularBuffer(50),i){e=this.get("parentGraph").nodes;var n=this.get("source"),a=this.get("target");n?(t=e.get(this.get("source").node),this.source=t.outputs.get(this.get("source").port)):a&&(o=e.get(this.get("target").node),this.target=o.inputs.get(this.get("target").port))}else{e=this.parentGraph.nodes;try{t=e.get(this.get("source").node),this.source=t.outputs.get(this.get("source").port),o=e.get(this.get("target").node),this.target=o.inputs.get(this.get("target").port)}catch(s){}this.source.connect(this),this.target.connect(this),t.on("send:"+this.source.id,this.send,this),this.bringToTop(),this.on("select",this.select,this)}},select:function(){this.parentGraph.trigger("select:edge",this)},send:function(e){this.target.parentNode.recieve(this.target.id,e)},isConnectedToPort:function(e){return this.source===e||this.target===e},isConnectedToNode:function(e){return this.source.parentNode===e||this.target.parentNode===e},toString:function(){return this.id?this.id:this.get("source").node+":"+this.get("source").port+"::"+this.get("target").node+":"+this.get("target").port},toJSON:function(){return{source:this.get("source"),target:this.get("target"),route:this.get("route")}},bringToTop:function(){var e=0;this.parentGraph.edges.each(function(t){if(t!==this){var o=t.get("z");o>e&&(e=o),t.view&&t.view.unhighlight()}},this),this.set("z",e+1)},remove:function(){this.source.disconnect(this),this.target.disconnect(this),this.collection&&this.collection.remove(this),this.source.parentNode.off("send:"+this.source.id,this.send,this),this.trigger("remove")}}),t.Collection=Backbone.Collection.extend({model:t.Model,comparator:function(e){return e.get("z")}})}(Dataflow),function(e){var t=e.prototype.module("graph");e.prototype.module("node");var o=e.prototype.module("edge"),i=.2,n=1.1;document.createElement("div").style.hasOwnProperty("zoom");var a='
            ';t.View=Backbone.View.extend({template:_.template(a),className:"dataflow-g",events:{"click .dataflow-graph":"deselect","dragstart .dataflow-graph-panzoom":"panStart","drag .dataflow-graph-panzoom":"pan","dragstop .dataflow-graph-panzoom":"panStop","click .dataflow-graph-gotoparent":"gotoParent",mousewheel:"mouseWheel"},initialize:function(){this.$el.html(this.template(this.model.toJSON()));var e=this.model.get("nodes"),t=this.model.get("edges");this.nodes=e.view={},this.model.nodes.each(this.addNode,this),this.model.nodes.on("add",this.addNode,this),this.model.nodes.on("remove",this.removeNode,this),this.edges=t.view={},this.model.edges.each(this.addEdge,this),this.model.edges.on("add",this.addEdge,this),this.model.edges.on("remove",this.removeEdge,this);var o=this.model.get("parentNode");o||this.$(".dataflow-graph-controls").hide(),this.$(".dataflow-graph-panzoom").draggable({helper:function(){var e=$("
            ");return this.model.dataflow.$el.append(e),e}.bind(this)}),this.$graphEl=this.$(".dataflow-graph"),this.graphEl=this.$(".dataflow-graph")[0],this.$graphEl.css({transform:"translate3d(0, 0, 0) scale3d(1, 1, 1) ",transformOrigin:"left top"}),this.bindInteraction()},panStartOffset:null,panStart:function(e,t){t&&(this.panStartOffset=t.offset)},pan:function(e,t){if(t){var o=this.model.get("zoom"),i=t.offset.left-this.panStartOffset.left,n=t.offset.top-this.panStartOffset.top;this.$(".dataflow-graph").css({transform:"translate3d("+i/o+"px, "+n/o+"px, 0)"})}},panStop:function(e,t){this.$(".dataflow-graph").css({transform:"translate3d(0, 0, 0)"});var o=this.model.get("zoom"),i=t.offset.left-this.panStartOffset.left,n=t.offset.top-this.panStartOffset.top;this.model.set({panX:this.model.get("panX")+i/o,panY:this.model.get("panY")+n/o})},tempPanX:0,tempPanY:0,setPanDebounce:_.debounce(function(){this.$(".dataflow-graph").css({transform:"translate3d(0, 0, 0)"}),this.model.set({panX:this.model.get("panX")+this.tempPanX,panY:this.model.get("panY")+this.tempPanY}),this.tempPanX=0,this.tempPanY=0},250),mouseWheel:function(e){e.preventDefault();var t=e.originalEvent;this.tempPanX+=t.wheelDeltaX/6,this.tempPanY+=t.wheelDeltaY/6,this.$(".dataflow-graph").css({transform:"translate3d("+this.tempPanX+"px, "+this.tempPanY+"px, 0)"}),this.setPanDebounce()},gotoParent:function(){var e=this.model.get("parentNode");e&&this.model.dataflow.showGraph(e.parentGraph)},bindInteraction:function(){this.bindZoom(),this.bindScroll()},bindZoom:function(){if(window.Hammer){var e,t,o,a,s,r,d,l,h,p,c=this;Hammer(this.$(".dataflow-graph-panzoom")[0]).on("transformstart",function(i){e=c.model.get("zoom"),t=i.gesture.center.pageX,o=i.gesture.center.pageY,a=t/e,s=o/e;var n=c.$el.offset();h=a-n.left,p=s-n.top,c.$graphEl.css({transformOrigin:a+"px "+s+"px"})}).on("transform",function(a){r=Math.max(i/e,Math.min(a.gesture.scale,n/e)),d=(a.gesture.center.pageX-t)/e,l=(a.gesture.center.pageY-o)/e,c.$graphEl.css({transform:"translate3d("+d+"px,"+l+"px, 0) "+"scale3d("+r+","+r+", 1) "})}).on("transformend",function(){c.$graphEl.css({transform:"translate3d(0, 0, 0) scale3d(1, 1, 1) "});var t=e*r;t=Math.max(i,Math.min(t,n)),c.model.set("zoom",t),h*=t,p*=t,c.model.set({panX:c.model.get("panX")+d,panY:c.model.get("panY")+l}),console.log(c.model.attributes)});var u=function(){var e=c.model.get("zoom"),t=c.zoomClass;c.zoomClass=.5>e?"zoom-tiny":.8>e?"zoom-small":1.3>e?"zoom-normal":"zoom-big",c.$graphEl.removeClass(t).addClass(c.zoomClass),c.graphEl.style.zoom=c.model.get("zoom")};this.model.on("change:zoom",u),1!==this.model.get("zoom")&&u()}},zoomClass:1,zoomIn:function(){var e=this.model.get("zoom"),t=.9*e;t=Math.max(i,t),t!==e&&this.model.set("zoom",t)},zoomOut:function(){var e=this.model.get("zoom"),t=1.1*e;t=Math.min(n,t),t!==e&&this.model.set("zoom",t)},zoomCenter:function(){var e=this.model.get("zoom"),t=1;t!==e&&this.model.set("zoom",1)},bindScroll:function(){},render:function(){var e=this;return _.defer(function(){e.rerenderEdges()},this),this},addNode:function(e){var t=this.model.dataflow.nodes[e.type];if(t&&t.View)e.view=new t.View({model:e,graph:this});else{var o=this.model.dataflow.node("base");e.view=new o.View({model:e,graph:this})}this.nodes[e.id]=e.view,e.view.render(),this.$(".dataflow-nodes").append(e.view.el)},removeNode:function(e){e.view.remove(),this.nodes[e.id]=null,delete this.nodes[e.id]},addEdge:function(e){e.view=new o.View({model:e}),this.edges[e.id]=e.view,e.view.render(),this.$(".dataflow-svg-edges")[0].appendChild(e.view.el)},removeEdge:function(e){e.view&&e.view.remove(),this.edges[e.id]=null,delete this.edges[e.id]},rerenderEdges:function(){_.each(this.edges,function(e){e.render()},this)},sizeSVG:function(){try{var e=this.$(".dataflow-svg-edges")[0],t=e.getBBox(),o=Math.max(Math.round(t.x+t.width+50),50),i=Math.max(Math.round(t.y+t.height+50),50);e.setAttribute("width",o),e.setAttribute("height",i)}catch(n){}},deselect:function(){this.model.nodes.invoke("set",{selected:!1}),this.model.edges.invoke("set",{selected:!1}),this.model.trigger("selectionChanged"),this.unfade(),this.model.dataflow.hideCards()},fade:function(){this.model.nodes.each(function(e){e.view&&(e.get("selected")||e.view.fade())}),this.fadeEdges()},fadeEdges:function(){this.model.edges.each(function(e){e.get("selected")||e.source.parentNode.get("selected")||e.target.parentNode.get("selected")?e.view.unfade():e.view.fade()})},unfade:function(){this.model.nodes.each(function(e){e.view&&e.view.unfade()}),this.model.edges.each(function(e){e.view&&e.view.unfade()})}})}(Dataflow),function(e){var t,o=e.prototype.module("node"),i=e.prototype.module("input"),n=e.prototype.module("output"),a='

            <%- label %>

            ',s="";o.View=Backbone.View.extend({template:_.template(a),innerTemplate:_.template(s),className:"dataflow-node",events:function(){return{"click .dataflow-node-header":"select",dragstart:"dragStart",drag:"drag",dragstop:"dragStop"}},initialize:function(e){this.$el.html(this.template(this.model.toJSON())),this.graph=e.graph,this.$el.addClass(this.model.type),this.model.parentGraph.dataflow.editable||this.$(".dataflow-node-edit").hide(),this.inputs=this.model.inputs.view=new i.CollectionView({collection:this.model.inputs,parent:this}),this.outputs=this.model.outputs.view=new n.CollectionView({collection:this.model.outputs,parent:this}),this.$el.draggable({handle:"h1",helper:function(){return $("
            ")}}),this.$el.data("dataflow-node-view",this),this.$(".dataflow-node-inner").append(this.innerTemplate),this.listenTo(this.model.parentGraph,"change:panX change:panY",this.bumpPosition),this.listenTo(this.model,"change:selected",this.selectedChanged),this.listenTo(this.model,"change:label",this.changeLabel),this.listenTo(this.model,"remove",this.hideInspector),this.$inner=this.$(".dataflow-node-inner")},render:function(){return this.$el.css({left:this.model.get("x")+this.model.parentGraph.get("panX"),top:this.model.get("y")+this.model.parentGraph.get("panY")}),this.$(".dataflow-node-ins").html(this.inputs.el),this.$(".dataflow-node-outs").html(this.outputs.el),this.$(".dataflow-node-controls").hide(),this.$(".label-edit").hide(),this},_alsoDrag:[],_dragDelta:{},$dragHelpers:$('
            '),dragStart:function(e,o){o&&(this.model.get("selected")||this.select(e,!0),e.stopPropagation(),t=this.model.parentGraph.get("zoom"),this.$dragHelpers.css({transform:"translate3d(0,0,0)"}),this.$el.parent().append(this.$dragHelpers),this._alsoDrag=this.model.collection.where({selected:!0}),_.each(this._alsoDrag,function(e){var t=e.view.$el,o=$('
            ').css({width:t.width(),height:t.height(),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)});this.$dragHelpers.append(o)},this))},changeLabel:function(){var e=this.model.get("label"),t=this.model.get("type");this.$(".dataflow-node-title").text(e).attr("title",e+": "+t)},drag:function(e,o){if(o){e.stopPropagation();var i=(o.position.left-o.originalPosition.left)/t,n=(o.position.top-o.originalPosition.top)/t;this.$dragHelpers.css({transform:"translate3d("+i+"px,"+n+"px,0)"})}},dragStop:function(e,o){if(o){e.stopPropagation(),this.model.parentGraph.get("panX"),this.model.parentGraph.get("panY");var i=(o.position.left-o.originalPosition.left)/t,n=(o.position.top-o.originalPosition.top)/t;this._alsoDrag.length&&(_.each(this._alsoDrag,function(e){e.view.moveToPosition(e.get("x")+i,e.get("y")+n)},this),this._alsoDrag=[]),this.$dragHelpers.empty(),this.$dragHelpers.remove()}},bumpPosition:function(){this.$el.css({left:this.model.get("x")+this.model.parentGraph.get("panX"),top:this.model.get("y")+this.model.parentGraph.get("panY")}),this.model.trigger("change:x change:y")},moveToPosition:function(e,t){this.model.set({x:e,y:t},{silent:!0}),this.bumpPosition()},removeModel:function(){this.model.remove()},bringToTop:function(){var e=0;this.model.collection.each(function(t){var o=parseInt(t.view.el.style.zIndex,10);o>e&&(e=o)},this),this.el.style.zIndex=e+1},select:function(e){e&&e.stopPropagation();var t=!1,o=this.model.get("selected");e&&(e.ctrlKey||e.metaKey)?(t=!0,o=!o,this.model.set("selected",o),o||this.fade()):(this.model.parentGraph.edges.invoke("set",{selected:!1}),this.model.parentGraph.nodes.invoke("set",{selected:!1}),this.model.parentGraph.view.fade(),o=!0,this.model.set("selected",!0)),this.bringToTop(),this.model.parentGraph.view.fadeEdges(),this.model.parentGraph.trigger("selectionChanged")},inspector:null,getInspector:function(){if(!this.inspector){var t=new o.InspectView({model:this.model}),i=e.prototype.module("card");this.inspector=new i.Model({dataflow:this.model.parentGraph.dataflow,card:t})}return this.inspector},showInspector:function(e){this.model.parentGraph.dataflow.addCard(this.getInspector(),e)},hideInspector:function(){this.model.parentGraph.dataflow.removeCard(this.getInspector())},fade:function(){this.$el.addClass("fade"),this.$el.removeClass("ui-selected")},unfade:function(){this.$el.removeClass("fade")},selectedChanged:function(){this.model.get("selected")?(this.highlight(),this.showInspector()):(this.unhighlight(),this.hideInspector())},highlight:function(){this.$el.removeClass("fade"),this.$el.addClass("ui-selected")},unhighlight:function(){this.$el.removeClass("ui-selected")}})}(Dataflow),function(e){var t=e.prototype.module("input"),o=e.prototype.module("edge"),i='',n=1;t.View=Backbone.View.extend({template:_.template(i),tagName:"li",className:"dataflow-port dataflow-in",events:{click:"getTopEdge",drop:"connectEdge","dragstart .dataflow-port-hole":"newEdgeStart","drag .dataflow-port-hole":"newEdgeDrag","dragstop .dataflow-port-hole":"newEdgeStop","dragstart .dataflow-port-plug":"changeEdgeStart","drag .dataflow-port-plug":"changeEdgeDrag","dragstop .dataflow-port-plug":"changeEdgeStop"},$input:null,initialize:function(e){this.$el.html(this.template(this.model.toJSON())),this.$el.addClass(this.model.get("type")),this.parent=e.parent;var t=this.parent.model,o=t.parentGraph;this.listenTo(t,"change:x change:y",function(){this._holePosition=null}.bind(this)),this.listenTo(o,"change:panX change:panY",function(){this._holePosition=null}.bind(this));var i=t.get("state");if(i&&i[this.model.id]&&this.$el.addClass("hasvalue"),this.model.parentNode.parentGraph.dataflow.editable){var n=this;if(this.$(".dataflow-port-plug").draggable({cursor:"pointer",helper:function(){var e=$('');return n.parent.graph.$el.append(e),e},disabled:!0,distance:10,delay:100}),this.$(".dataflow-port-hole").draggable({cursor:"pointer",helper:function(){var e=$('').data({port:n.model});return n.parent.graph.$el.append(e),e}}),this.$el.droppable({accept:".dataflow-port-plug.in, .dataflow-port-hole.out",activeClassType:"droppable-hover",refreshPositions:!0}),this.model.parentNode.parentGraph.dataflow.inputs){var a=this.model.get("type"),s=this.model.parentNode.get("state");if(e=this.model.get("options"),void 0!==e&&(_.isString(e)&&(e=e.split(" "),this.model.set("options",e)),_.isArray(e))){for(var r={},d=0;e.length>d;d++)r[e[d]]=e[d];e=r,this.model.set("options",e)}var l,h=this.renderInput(a,e);s&&void 0!==s[this.model.id]?l=s[this.model.id]:void 0!==this.model.get("value")&&(l=this.model.get("value")),this.setInputValue(h,a,l),this.model.parentNode.on("change:state",function(){var e=this.model.parentNode.get("state");return e&&void 0!==e[this.model.id]?(this.setInputValue(h,a,e[this.model.id]),this.$el.addClass("hasvalue"),void 0):(this.$el.removeClass("hasvalue"),void 0)}.bind(this));var p=$('