Added webservices forwarding script for UI
diff --git a/web/js/views/flow.js b/web/js/views/flow.js
new file mode 100644
index 0000000..65e0b71
--- /dev/null
+++ b/web/js/views/flow.js
@@ -0,0 +1,69 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+// not used for now
+window.FlowView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('flow'));
+        this.model.bind("change", this.render, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
+
+window.FlowListItemView = Backbone.View.extend({
+
+    tagName:"tr",
+
+    initialize:function () {
+        this.template = _.template(tpl.get('flow-list-item'));
+        this.model.bind("change", this.render, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
+
+// TODO throughput (bps) and pps sparklines would be nice here
+// TODO hovering over a MAC address could show a compact view of that host
+window.FlowListView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('flow-list'));
+        this.model.bind("change", this.render, this);
+        this.model.bind("add", this.render, this);
+    },
+
+    render:function (eventName) {
+        // console.log("rendering flow list view: " + this.model.models.length);
+        $(this.el).html(this.template({nflows:this.model.length}));
+        _.each(this.model.models, function (f) {
+            $(this.el).find('table.flow-table > tbody')
+                .append(new FlowListItemView({model:f}).render().el);
+        }, this);
+        return this;
+    },
+
+});
+
diff --git a/web/js/views/header.js b/web/js/views/header.js
new file mode 100644
index 0000000..65e49dd
--- /dev/null
+++ b/web/js/views/header.js
@@ -0,0 +1,49 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+window.HeaderView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('header'));
+        // this.searchResults = new HostCollection();
+        // this.searchresultsView = new SearchListView({model:this.searchResults, className:'dropdown-menu'});
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template());
+        $('#live-updates', this.el).change(function () {
+            updating = $(this).is(':checked');
+        })
+        // $('.navbar-search', this.el).append(this.searchresultsView.render().el);
+        return this;
+    },
+
+    events:{
+        "keyup .search-query":"search"
+    },
+
+    search:function (event) {
+//        var key = event.target.value;
+        var key = $('#searchText').val();
+        console.log('search ' + key);
+        // TODO search the host and switch lists
+        this.searchResults.findByName(key);
+        setTimeout(function () {
+            $('#searchForm').addClass('open');
+        });
+    }
+
+});
\ No newline at end of file
diff --git a/web/js/views/home.js b/web/js/views/home.js
new file mode 100644
index 0000000..0283002
--- /dev/null
+++ b/web/js/views/home.js
@@ -0,0 +1,41 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+window.HomeView = Backbone.View.extend({
+
+    initialize:function () {
+        // console.log('Initializing Home View');
+        this.template = _.template(tpl.get('home'));
+    },
+
+    events:{
+        "click #showMeBtn":"showMeBtnClick"
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template());
+        var stats = new Status();
+        $(this.el).find('#controller-status').html(new StatusView({model:stats}).render().el);
+        $(this.el).find('#switch-list').html(new SwitchListView({model:swl}).render().el);
+        $(this.el).find('#host-list').html(new HostListView({model:hl}).render().el);
+        return this;
+    },
+
+    showMeBtnClick:function () {
+        app.headerView.search();
+    }
+
+});
\ No newline at end of file
diff --git a/web/js/views/host.js b/web/js/views/host.js
new file mode 100644
index 0000000..705703a
--- /dev/null
+++ b/web/js/views/host.js
@@ -0,0 +1,67 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+window.HostView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('host'));
+        this.model.bind("change", this.render, this);
+        this.model.bind("add", this.render, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
+
+window.HostListView = Backbone.View.extend({
+
+    initialize:function () {
+        var self = this;
+        this.template = _.template(tpl.get('host-list'));
+        this.model.bind("change", this.render, this);
+        this.model.bind("add", this.render, this);
+        this.model.bind("remove", this.render, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template({nhosts:hl.length}));
+        _.each(this.model.models, function (h) {
+            $(this.el).find('table.host-table > tbody')
+                .append(new HostListItemView({model:h}).render().el);
+        }, this);
+        return this;
+    }
+});
+
+window.HostListItemView = Backbone.View.extend({
+
+    tagName:"tr",
+
+    initialize:function () {
+        this.template = _.template(tpl.get('host-list-item'));
+        this.model.bind("change", this.render, this);
+        this.model.bind("destroy", this.close, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
\ No newline at end of file
diff --git a/web/js/views/port.js b/web/js/views/port.js
new file mode 100644
index 0000000..e9aadb9
--- /dev/null
+++ b/web/js/views/port.js
@@ -0,0 +1,70 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+// not used for now
+window.PortView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('port'));
+        this.model.bind("change", this.render, this);
+        //this.model.bind("destroy", this.close, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
+
+window.PortListItemView = Backbone.View.extend({
+
+    tagName:"tr",
+
+    initialize:function () {
+        this.template = _.template(tpl.get('port-list-item'));
+        this.model.bind("change", this.render, this);
+        //this.model.bind("destroy", this.close, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
+
+// TODO throughput sparklines would be nice here
+window.PortListView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('port-list'));
+        this.model.bind("change", this.render, this);
+        this.model.bind("add", this.render, this);
+    },
+
+    render:function (eventName) {
+        // console.log("rendering port list view");
+        $(this.el).html(this.template({nports:this.model.length}));
+        _.each(this.model.models, function (p) {
+            $(this.el).find('table.port-table > tbody')
+                .append(new PortListItemView({model:p}).render().el);
+        }, this);
+        return this;
+    },
+
+});
+
diff --git a/web/js/views/status.js b/web/js/views/status.js
new file mode 100644
index 0000000..52c6c1c
--- /dev/null
+++ b/web/js/views/status.js
@@ -0,0 +1,30 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+window.StatusView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('status'));
+        this.model.bind("change", this.render, this);
+    },
+
+    render:function (eventName) {
+        // console.log("rendering status");
+        $(this.el).html(this.template(this.model.toJSON()));
+        //$(this.el).html(this.template());
+        return this;
+    }
+});
\ No newline at end of file
diff --git a/web/js/views/switch.js b/web/js/views/switch.js
new file mode 100644
index 0000000..d457633
--- /dev/null
+++ b/web/js/views/switch.js
@@ -0,0 +1,73 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+window.SwitchView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('switch'));
+        this.model.bind("change", this.render, this);
+        //this.model.bind("destroy", this.close, this);
+        
+        // some parts of the model are large and are only needed in detail view
+        this.model.loadPorts();
+        this.model.loadFlows();
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        $(this.el).find('#port-list').html(new PortListView({model:this.model.ports}).render().el);
+        $(this.el).find('#flow-list').html(new FlowListView({model:this.model.flows}).render().el);
+        return this;
+    }
+
+});
+
+window.SwitchListItemView = Backbone.View.extend({
+
+    tagName:"tr",
+
+    initialize:function () {
+        this.template = _.template(tpl.get('switch-list-item'));
+        this.model.bind("change", this.render, this);
+    //this.model.bind("destroy", this.close, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template(this.model.toJSON()));
+        return this;
+    }
+
+});
+
+window.SwitchListView = Backbone.View.extend({
+
+    initialize:function () {
+        this.template = _.template(tpl.get('switch-list'));
+        this.model.bind("change", this.render, this);
+        this.model.bind("remove", this.render, this);
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template({nswitches:swl.length}));
+        _.each(this.model.models, function (sw) {
+            $(this.el).find('table.switch-table > tbody')
+                .append(new SwitchListItemView({model:sw}).render().el);
+        }, this);
+        return this;
+    },
+
+});
+
diff --git a/web/js/views/topology.js b/web/js/views/topology.js
new file mode 100644
index 0000000..77129aa
--- /dev/null
+++ b/web/js/views/topology.js
@@ -0,0 +1,111 @@
+/*
+   Copyright 2012 IBM
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+window.TopologyView = Backbone.View.extend({
+    initialize:function () {
+        this.template = _.template(tpl.get('topology'));
+        this.model.bind("change", this.render, this);
+        this.hosts = this.options.hosts.models;
+        this.host_links = [];
+    },
+
+    render:function (eventName) {
+        $(this.el).html(this.template());
+        var width = 900,
+            height = 600;
+        var color = d3.scale.category20();
+        var force = d3.layout.force()
+                             .charge(-500)
+                             .linkDistance(200)
+                             .size([width, height]);
+        var svg = d3.select("#topology-graph").append("svg")
+                    .attr("width", width)
+                    .attr("height", height);
+        if(this.model.nodes) {
+            for (var i = 0; i < this.model.nodes.length; i++) {
+                this.model.nodes[i].group = 1;
+                this.model.nodes[i].id = this.model.nodes[i].name;
+            }
+            
+            for (var i = 0; i < this.hosts.length; i++) {
+                host = this.hosts[i];
+                if (host.attributes['ipv4'].length > 0) {
+                    host.name = host.attributes['ipv4'][0] + "\n" + host.id;
+                } else {
+                    host.name = host.id;
+                }
+                host.group = 2;
+                //console.log(host);
+            }
+            
+            var all_nodes = this.model.nodes.concat(this.hosts);
+            
+            var all_nodes_map = [];
+            
+            _.each(all_nodes, function(n) {
+                all_nodes_map[n.id] = n;
+            });
+            
+            for (var i = 0; i < this.hosts.length; i++) {
+                host = this.hosts[i];
+                //for (var j = 0; j < host.attributes['attachmentPoint'].length; j++) {
+                for (var j = 0; j < 1; j++) { // FIXME hack to ignore multiple APs
+                    var link = {source:all_nodes_map[host.id],
+                                target:all_nodes_map[host.attributes['attachmentPoint'][j]['switchDPID']],
+                                value:10};
+                    //console.log(link);
+                    if ( link.source && link.target) {
+                        this.host_links.push(link);
+                    } else {
+                        console.log("Error: skipping link with undefined stuff!")
+                    }
+                }
+            }
+            
+            var all_links = this.model.links.concat(this.host_links);
+            
+            force.nodes(all_nodes).links(all_links).start();
+            var link = svg.selectAll("line.link").data(all_links).enter()
+                          .append("line").attr("class", "link")
+                          .style("stroke", function (d) { return "black"; });
+            var node = svg.selectAll(".node").data(all_nodes)
+                          .enter().append("g")
+                          .attr("class", "node")
+                          .call(force.drag);
+                          
+            node.append("image")
+                .attr("xlink:href", function (d) {return d.group==1 ? "/ui/img/switch.png" : "/ui/img/server.png"})
+                .attr("x", -16).attr("y", -16)
+                .attr("width", 32).attr("height", 32);
+            node.append("text").attr("dx", 20).attr("dy", ".35em")
+                .text(function(d) { return d.name });
+            node.on("click", function (d) {
+                // TODO we could add some functionality here
+                console.log('clicked '+d.name);
+            });
+            node.append("title").text(function(d) { return d.name; });
+            force.on("tick", function() {
+                link.attr("x1", function(d) { return d.source.x; })
+                    .attr("y1", function(d) { return d.source.y; })
+                    .attr("x2", function(d) { return d.target.x; })
+                    .attr("y2", function(d) { return d.target.y; });
+                node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
+                
+            });
+        }
+        return this;
+    }
+});