From 37c7213d0b1ca33c5750fdb7404984b1d302d2af Mon Sep 17 00:00:00 2001
From: Estók Dániel <estok.daniel@cloud.bme.hu>
Date: Mon, 25 Jan 2016 14:12:09 +0100
Subject: [PATCH] Improved dashboard-integration and refactor.

---
 circle/setty/models.py                                   |   4 ++--
 circle/setty/static/setty/setty.js                       | 386 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------------------------------
 circle/setty/templates/setty/index.html                  |  65 +++++++++++++++++++++++++++++++++++++++++++++++++++++------------
 circle/setty/templates/setty/service_confirm_delete.html |   5 -----
 circle/setty/urls.py                                     |   8 +++++++-
 circle/setty/views.py                                    | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------
 6 files changed, 339 insertions(+), 274 deletions(-)
 delete mode 100644 circle/setty/templates/setty/service_confirm_delete.html

diff --git a/circle/setty/models.py b/circle/setty/models.py
index 3ddcf73..a23bb39 100644
--- a/circle/setty/models.py
+++ b/circle/setty/models.py
@@ -53,7 +53,7 @@ class Element(Model):
     anchors = models.PositiveSmallIntegerField()
 
     def __unicode__(self):
-        return self.service.name + ", id: " + self.display_id
+        return "%s (%s)" % (self.service.name, self.display_id)
 
 
 class ElementConnection(Model):
@@ -70,4 +70,4 @@ class ElementConnection(Model):
     parameters = models.TextField()
 
     def __unicode__(self):
-        return self.target.service.name + ", " + str(self.id)
+        return "%s (%d)" % (self.target.service.name, self.id)
diff --git a/circle/setty/static/setty/setty.js b/circle/setty/static/setty/setty.js
index 2451bb7..4e35b8e 100644
--- a/circle/setty/static/setty/setty.js
+++ b/circle/setty/static/setty/setty.js
@@ -1,5 +1,34 @@
-(function($){
+function getCookie(name) {
+    var cookieValue = null;
+    if (document.cookie && document.cookie !== '') {
+        var cookies = document.cookie.split(';');
+        for (var i = 0; i < cookies.length; i++) {
+            var cookie = jQuery.trim(cookies[i]);
+            if (cookie.substring(0, name.length + 1) == (name + '=')) {
+                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+                break;
+            }
+        }
+    }
+    return cookieValue;
+}
+
+function csrfSafeMethod(method) {
+    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
+}
+
 
+var csrftoken = getCookie('csrftoken');
+
+$.ajaxSetup({
+    beforeSend: function(xhr, settings) {
+        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+            xhr.setRequestHeader("X-CSRFToken", csrftoken);
+        }
+    }
+});
+
+(function($){
   $.event.special.doubletap = {
     bindType: 'touchend',
     delegateType: 'touchend',
@@ -18,7 +47,6 @@
           event[property] = event.originalEvent.changedTouches[0][property];
         });
 
-        // let jQuery handle the triggering of "doubletap" event handlers
         handleObj.handler.apply(this, arguments);
       } else {
         targetData.lastTouch = now;
@@ -72,28 +100,27 @@ jsPlumb.ready(function() {
 
     var stackIndexer = 0;
     var stackSize = 0;
-
     var objectStack = [];
     var undoStack = [];
     var redoStack = [];
 
-    $("#dropContainer").attr('unselectable', 'on')
-        .css({
+    $("#dropContainer").attr('unselectable', 'on').css({
             'user-select': 'none',
-            'MozUserSelect': 'none'
-        })
+            'MozUserSelect': 'none'})
         .on('selectstart', false)
         .on('mousedown', false);
 
+
     setServiceStatus = function(status) {
         if (status == "unsaved") {
             $("#serviceStatus").text("Unsaved");
         }
-        if (status == "saved") {
+        else {
             $("#serviceStatus").empty();
         }
     };
 
+
     addInfo = function(title, info, type, object) {
         $("#informationContainer").empty();
 
@@ -167,67 +194,14 @@ jsPlumb.ready(function() {
 
         $("#informationContainer").append(div);
 
-        $("#infoInput").val(info).keyup(function() {
-            setServiceStatus("unsaved");
-            newParams = $("#infoInput").val();
-
-            if (type == "connection") object.parameters = newParams;
-            if (type == "element") object.attr("parameters", newParams);
-        });
-
-        $("#addEndpoint").click(function() {
-            addEndpoint(object);
-            undoStack.splice(stackIndexer, 0, removeEndoint);
-            redoStack.splice(stackIndexer, 0, addEndpoint);
-            objectStack.splice(stackIndexer, 0, object);
-            stackIndexer++;
-            stackSize++;
-        });
-
-        $("#removeEndpoint").click(function() {
-            removeEndoint(object);
-            undoStack.splice(stackIndexer, 0, addEndpoint);
-            redoStack.splice(stackIndexer, 0, removeEndoint);
-            objectStack.splice(stackIndexer, 0, object);
-            stackIndexer++;
-            stackSize++;
-        });
-
-        $("#removeFromWorkspace").click(function() {
-            $('.element').removeClass('elementSelected');
-            removeElement(object);
-
-            $("#informationPanel").hide();
-            $("#dragPanel").show();
-
-            undoStack.splice(stackIndexer, 0, addElement);
-            redoStack.splice(stackIndexer, 0, removeElement);
-            objectStack.splice(stackIndexer, 0, object);
-            stackSize++;
-            stackIndexer++;
-        });
-
-        $("#removeConnection").click(function() {
-            jsPlumbInstance.detach(object);
-            $("#informationPanel").hide();
-            $("#dragPanel").show();
-        });
-
-        $("#addElementToWorkspace").click(function() {
-            addElement(object.attr("id"), (++elementIndex) + "_" + object.attr("id"), (elementIndex % 21) * 30, 4, "", (elementIndex % 21) * 30);
-
-            undoStack.splice(stackIndexer, 0, removeElement);
-            redoStack.splice(stackIndexer, 0, addElement);
-            objectStack.splice(stackIndexer, 0, newInstance);
-            stackSize++;
-            stackIndexer++;
-        });
+        $("#infoInput").val(info);
 
         $("#dragPanel").hide();
 
         $("#informationPanel").show();
     };
 
+
     updateConnections = function(connection, remove) {
         if (!remove) {
             elementConnections.push(connection);
@@ -277,6 +251,7 @@ jsPlumb.ready(function() {
         return true;
     };
 
+
     checkSourceTargetEquality = function(connection) {
         if (connection.targetId == connection.sourceId) {
             addMessage("Connecting element to itself is forbidden.", "danger");
@@ -285,6 +260,7 @@ jsPlumb.ready(function() {
         return true;
     };
 
+
     getAnchorCoordinate = function(rate) {
         x = Math.cos(2.0 * Math.PI * rate) / 2;
         y = Math.sin(2.0 * Math.PI * rate) / 2;
@@ -317,6 +293,7 @@ jsPlumb.ready(function() {
         return [y + 0.5, -x + 0.5, dy, -dx];
     };
 
+
     isConnected = function(anchorId) {
         returnValue = false;
         $.each(elementConnections, function(index) {
@@ -329,6 +306,7 @@ jsPlumb.ready(function() {
         return returnValue;
     };
 
+
     getConnectionparamAndAnchor = function(anchorId) {
         parameters = "";
         otherAnchor = "";
@@ -350,6 +328,7 @@ jsPlumb.ready(function() {
         return [otherAnchor, parameters];
     };
 
+
     addEndpoint = function(element) {
         anchors = element.attr("anchors");
 
@@ -357,37 +336,35 @@ jsPlumb.ready(function() {
 
         anchors++;
 
-        jsPlumbInstance.addEndpoint(document.getElementById(
-                element.attr("id")), {
-                anchor: getAnchorCoordinate((anchors - 1) / anchors),
+        jsPlumbInstance.addEndpoint(document.getElementById(element.attr("id")), {
                 uuid: (anchors - 1) + "_" + element.attr("id")
             },
             jsPlumbEndpoint);
 
-        for (i = 0; i < anchors; i++) jsPlumbInstance.getEndpoint(i + "_" + element.attr("id")).setAnchor(getAnchorCoordinate(i / (anchors)));
+        for (i = 0; i < anchors; i++) {
+            jsPlumbInstance.getEndpoint(i + "_" + element.attr("id")).setAnchor(getAnchorCoordinate(i / (anchors)));
+        }
 
         element.attr("anchors", anchors);
 
         jsPlumbInstance.repaintEverything();
     };
 
+
     removeEndoint = function(element) {
         anchors = element.attr("anchors");
 
         if (anchors == 4) return;
 
-        i = anchors - 1;
-        anchors--;
+        i = --anchors;
 
-        while (isConnected(i + "_" + element.attr("id")) &&
-            i >= 0) i--;
+        while (isConnected(i + "_" + element.attr("id")) && i >= 0) i--;
 
         if (i == -1) {
             addMessage("Removing anchors is obstructed.", "danger");
             return;
         } else if (i == anchors) {
-            jsPlumbInstance.deleteEndpoint(
-                jsPlumbInstance.getEndpoint(anchors + "_" + element.attr("id")));
+            jsPlumbInstance.deleteEndpoint(jsPlumbInstance.getEndpoint(anchors + "_" + element.attr("id")));
         } else {
             newId = i + "_" + element.attr("id");
             oldId = anchors + "_" + element.attr("id");
@@ -408,6 +385,7 @@ jsPlumb.ready(function() {
         jsPlumbInstance.repaintEverything();
     };
 
+
     connectEndpoints = function(data) {
         connectionObject =
             jsPlumbInstance.connect({
@@ -419,6 +397,7 @@ jsPlumb.ready(function() {
         setServiceStatus("unsaved");
     };
 
+
     disconnectEndpoints = function(data) {
         for (var i = 0; i < elementConnections.length; i++) {
             if (elementConnections[i].endpoints[0].getUuid() == data[0] &&
@@ -430,6 +409,7 @@ jsPlumb.ready(function() {
         return;
     };
 
+
     addElement = function(idOrInstance, newId, newPositionY, endpoints, parameters, newPositionX) {
         newInstance = "";
 
@@ -460,47 +440,51 @@ jsPlumb.ready(function() {
             containment: $("#dropContainer")
         });
 
-        newInstance.on('dblclick doubletap', function() {
-            element = $(this);
-            $('.element').removeClass('elementSelected');
-            jsPlumbInstance.select().setPaintStyle({strokeStyle:'#9932cc', lineWidth: 8});
-            element.addClass("elementSelected");
-            addInfo(element.attr("alt"), element.attr("parameters"), "element", element);
-        }).mousedown(function(e) {
-            if (e.button == 2) {
-                setServiceStatus("unsaved");
-                $("#informationPanel").hide();
-                $("#dragPanel").show();
-                
-                $('.element').removeClass('elementSelected');
-                jsPlumbInstance.select().setPaintStyle({strokeStyle:'#9932cc', lineWidth: 8});
-                
-                removeElement($(this));
-
-                undoStack.splice(stackIndexer, 0, addElement);
-                redoStack.splice(stackIndexer, 0, removeElement);
-                objectStack.splice(stackIndexer, 0, $(this));
-                stackSize++;
-                stackIndexer++;
-                return false;
-            }
-            return true;
-        });
-
         setServiceStatus("unsaved");
 
         jsPlumbInstance.repaintEverything();
     };
 
+
     removeElement = function(object) {
         jsPlumbInstance.detachAllConnections(object);
         jsPlumbInstance.remove(object.attr("id"));
     };
 
+
+    scrollContainer = function(direction) {
+        dragContainerScroll += direction;
+
+        if (dragContainerScroll == $(".elementTemplate").length - 2) dragContainerScroll--;
+        if (dragContainerScroll == -1) dragContainerScroll++;
+
+        $("#dragContainer").scrollTop(
+            dragContainerScroll * $("#elementTemplatePanel").height()
+        );
+    };
+
+
+    mouseScrollContainer = function(event) {
+        var e = window.event || event;
+        var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
+
+        $('body').addClass("noScroll");
+
+        scrollContainer(-delta);
+
+        $('body').removeClass("noScroll");
+    };
+
+
     jsPlumbInstance.bind("connection", function(info) {
         updateConnections(info.connection);
         info.connection.parameters = "";
 
+        // For right click on a connection.
+        $("path").on('doubletap', function() {
+            //Todo
+        });
+
         if (clickEvent === 0) {
             undoStack.splice(stackIndexer, 0, disconnectEndpoints);
             redoStack.splice(stackIndexer, 0, connectEndpoints);
@@ -513,11 +497,15 @@ jsPlumb.ready(function() {
             stackSize++;
         }
     });
+
+
     jsPlumbInstance.bind("beforeDrop", function(info) {
         return checkDuplicateConnection(info.connection) &&
             checkSourceTargetEquality(info.connection) &&
             checkCompatibility(info.connection.sourceId, info.connection.targetId);
     });
+
+
     jsPlumbInstance.bind("connectionDetached", function(info) {
         updateConnections(info.connection, true);
 
@@ -533,14 +521,20 @@ jsPlumb.ready(function() {
             stackSize++;
         }
     });
+
+
     jsPlumbInstance.bind("connectionMoved", function(info) {
         updateConnections(info.connection, true);
     });
+
+
     jsPlumbInstance.bind("contextmenu", function(info) {
         jsPlumbInstance.detach(info);
         $("#informationPanel").hide();
         $("#dragPanel").show();
     });
+
+
     jsPlumbInstance.bind("dblclick", function(info) {
         $('.element').removeClass('elementSelected');
         jsPlumbInstance.select().setPaintStyle({strokeStyle:'#9932cc', lineWidth: 8});
@@ -550,11 +544,14 @@ jsPlumb.ready(function() {
             "connection",
             info);
     });
+
+
     jsPlumbInstance.draggable(jsPlumb.getSelector(".element"), {
         containment: $("#dropContainer")
     });
 
-    $(".elementTemplate").click(function() {
+
+    $('body').on('click', '.elementTemplate', function() {
         addElement($(this).attr("id"), (++elementIndex) + "_" + $(this).attr("id"), (elementIndex % 21) * 30, 4, "", (elementIndex % 21) * 30);
 
         undoStack.splice(stackIndexer, 0, removeElement);
@@ -564,25 +561,115 @@ jsPlumb.ready(function() {
         stackIndexer++;
     });
 
-    $("#closeInfoPanel").click(function(){
+
+    $('body').on('dblclick doubletap', '.element', function() {
+        element = $(this);
+        $('.element').removeClass('elementSelected');
+        jsPlumbInstance.select().setPaintStyle({strokeStyle:'#9932cc', lineWidth: 8});
+        element.addClass("elementSelected");
+        addInfo(element.attr("alt"), element.attr("parameters"), "element", element);
+        $(document).scrollTop(0);
+    });
+
+
+    $('body').on('contextmenu', '.element', function(event) {
+        setServiceStatus("unsaved");
+        $("#informationPanel").hide();
+        $("#dragPanel").show();
+
+        $('.element').removeClass('elementSelected');
+        jsPlumbInstance.select().setPaintStyle({strokeStyle:'#9932cc', lineWidth: 8});
+
+        removeElement($(this));
+
+        undoStack.splice(stackIndexer, 0, addElement);
+        redoStack.splice(stackIndexer, 0, removeElement);
+        objectStack.splice(stackIndexer, 0, $(this));
+        stackSize++;
+        stackIndexer++;
+    });
+
+
+    $('body').on('click', '#closeInfoPanel', function() {
         $('#informationPanel').hide();
         $('#dragPanel').show();
         $('.element').removeClass('elementSelected');
         jsPlumbInstance.select().setPaintStyle({strokeStyle:'#9932cc', lineWidth: 8});
     });
 
-    $("#clearService").click(function() {
-        jsPlumbInstance.detachEveryConnection();
-        jsPlumbInstance.deleteEveryEndpoint();
-        $(".element").remove();
+
+    $('body').on('keyUp', '#infoInput', function() {
         setServiceStatus("unsaved");
+        newParams = $("#infoInput").val();
+
+        if (type == "connection") object.parameters = newParams;
+        if (type == "element") object.attr("parameters", newParams);
+    });
+
+
+    $('body').on('click', '#addEndpoint', function() {
+        addEndpoint(object);
+        undoStack.splice(stackIndexer, 0, removeEndoint);
+        redoStack.splice(stackIndexer, 0, addEndpoint);
+        objectStack.splice(stackIndexer, 0, object);
+        stackIndexer++;
+        stackSize++;
+    });
+
+
+    $('body').on('click', '#removeEndpoint', function() {
+        removeEndoint(object);
+        undoStack.splice(stackIndexer, 0, addEndpoint);
+        redoStack.splice(stackIndexer, 0, removeEndoint);
+        objectStack.splice(stackIndexer, 0, object);
+        stackIndexer++;
+        stackSize++;
+    });
+
+
+    $('body').on('click', '#removeFromWorkspace', function() {
+        $('.element').removeClass('elementSelected');
+        removeElement(object);
+
+        $("#informationPanel").hide();
+        $("#dragPanel").show();
+
+        undoStack.splice(stackIndexer, 0, addElement);
+        redoStack.splice(stackIndexer, 0, removeElement);
+        objectStack.splice(stackIndexer, 0, object);
+        stackSize++;
+        stackIndexer++;
+    });
+
+
+    $('body').on('click', '#removeConnection', function() {
+        jsPlumbInstance.detach(object);
+        $("#informationPanel").hide();
+        $("#dragPanel").show();
+    });
+
+
+    $('body').on('click', '#addElementToWorkspace', function() {
+        addElement(object.attr("id"), (++elementIndex) + "_" + object.attr("id"), (elementIndex % 21) * 30, 4, "", (elementIndex % 21) * 30);
+
+        undoStack.splice(stackIndexer, 0, removeElement);
+        redoStack.splice(stackIndexer, 0, addElement);
+        objectStack.splice(stackIndexer, 0, newInstance);
+        stackSize++;
+        stackIndexer++;
+    });
 
-        jsPlumbInstance.repaintEverything();
+
+    $('body').on('click', '#clearService', function() {
+        jsPlumbInstance.reset();
+        $(".element").remove();
+        setServiceStatus("unsaved");
 
         elementIndex = 0;
     });
 
-    $("#undoMovement").click(function() {
+
+    $('body').on('click', '#undoMovement', function() {
         if (stackIndexer <= 0) return;
         stackIndexer--;
         clickEvent = 1;
@@ -591,7 +678,8 @@ jsPlumb.ready(function() {
         clickEvent = 0;
     });
 
-    $("#redoMovement").click(function() {
+
+    $('body').on('click', '#redoMovement', function() {
         if (stackIndexer >= stackSize) return;
         clickEvent = 1;
         object = objectStack[stackIndexer];
@@ -599,19 +687,32 @@ jsPlumb.ready(function() {
         clickEvent = 0;
     });
 
-    $(".elementTemplateInfo").click(function() {
-        id = $(this).attr("element");
 
+    $('body').on('click', '.elementTemplateInfo', function() {
+        id = $(this).attr("element");
         addInfo($("#" + id).attr("alt"), $("#" + id).attr("desc"), "elementTemplate", $("#" + id));
     });
 
 
-    $("#serviceName").keydown(function() {
+    $('body').on('click', '#serviceName', function() {
+        $(this).replaceWith('<input type="text" id="serviceName" class="form-control form-control-sm" style="margin-top: -4px !important; margin-bottom: -4px !important;" value="' + $(this).html() + '" />');
+        document.getElementById("serviceName").select();
         setServiceStatus("unsaved");
     });
 
-    $("#saveService").click(function() {
-        serviceName = $("#serviceName").text();
+
+    $('body').on('click', '#dragContainerScrollUp', function() {
+        scrollContainer(-1);
+    });
+
+
+    $('body').on('click', '#dragContainerScrollDown', function() {
+        scrollContainer(1);
+    });
+
+
+    $('body').on('click', '#saveService', function() {
+        serviceName = $("#serviceName").val() === ''?$("#serviceName").text():$("#serviceName").val();
         connectionSet = [];
         instanceSet = [];
 
@@ -621,8 +722,7 @@ jsPlumb.ready(function() {
                 "sourceEndpoint": elementConnections[index].endpoints[0].getUuid(),
                 "targetId": elementConnections[index].targetId,
                 "targetEndpoint": elementConnections[index].endpoints[1].getUuid(),
-                "parameters": elementConnections[index].parameters
-            });
+                "parameters": elementConnections[index].parameters});
         });
 
         $.each($(".element"), function() {
@@ -631,8 +731,7 @@ jsPlumb.ready(function() {
                 "posX": Math.floor($(this).position().left),
                 "posY": Math.floor($(this).position().top),
                 "anchors": $(this).attr("anchors"),
-                "parameters": $(this).attr("parameters")
-            });
+                "parameters": $(this).attr("parameters")});
         });
 
         $.post("", {
@@ -640,17 +739,10 @@ jsPlumb.ready(function() {
             data: JSON.stringify({
                 "serviceName": serviceName,
                 "elementConnections": connectionSet,
-                "elements": instanceSet
-            })
-        }, function(resultValue) {
-            if (window.location.href.indexOf(
-                    "/create") >= 0) {
-                window.location = "../" +
-                    resultValue;
-            } else {
-                addMessage("Saved successfully.","success");
-                setServiceStatus("saved");
-            }
+                "elements": instanceSet})
+        }, function(result) {
+            addMessage(result.serviceName + " saved successfully.","success");
+            setServiceStatus("saved");
         });
     });
 
@@ -665,37 +757,6 @@ jsPlumb.ready(function() {
     });
 
 
-    scrollContainer = function(direction) {
-        dragContainerScroll += direction;
-
-        if (dragContainerScroll == $(".elementTemplate").length - 2) dragContainerScroll--;
-        if (dragContainerScroll == -1) dragContainerScroll++;
-
-        $("#dragContainer").scrollTop(
-            dragContainerScroll * $("#elementTemplatePanel").height()
-        );
-    };
-
-
-    mouseScrollContainer = function(ev) {
-        var e = window.event || ev;
-        var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
-
-        $('body').addClass("noScroll");
-
-        scrollContainer(-delta);
-
-        $('body').removeClass("noScroll");
-    };
-
-    $("#dragContainerScrollUp").click(function() {
-        scrollContainer(-1);
-    });
-
-    $("#dragContainerScrollDown").click(function() {
-        scrollContainer(1);
-    });
-
     var dragContainer = document.getElementById("dragContainer");
 
     if (dragContainer.addEventListener) {
@@ -716,10 +777,9 @@ jsPlumb.ready(function() {
     $(document).ready(function() {
         $.post("", {
             event: "loadService"
-        }, function(resultValue) {
-            if (resultValue === "") return;
+        }, function(result) {
+            if (result === "") return;
 
-            result = jQuery.parseJSON(resultValue);
             $("#serviceName").text(result.serviceName);
 
             $.each(result.elements, function(i, element) {
diff --git a/circle/setty/templates/setty/index.html b/circle/setty/templates/setty/index.html
index 86db7ba..33b7df0 100644
--- a/circle/setty/templates/setty/index.html
+++ b/circle/setty/templates/setty/index.html
@@ -7,6 +7,8 @@
 
 {% block content %}
 
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+
 <link type="text/css" rel="stylesheet" href="{% static 'setty/style.css' %}">
 
 <div class="row" id="workspace">
@@ -14,16 +16,14 @@
         <div class="panel panel-default initHidden" id="informationPanel">
             <div class="panel-heading text-center">
                 <div class="row">
-                    <div class="col-xs-2 text-left">
+                    <div class="col-xs-10 text-left">
+                        <h3 class="no-margin"><i class="fa fa-info"></i>&nbsp;{% trans 'Information' %}</h3>
+                    </div>
+                    <div class="col-xs-2">
                         <button class="btn btn-danger btn-xs" id="closeInfoPanel">
                             <i class="fa fa-times"></i>
                         </button>
                     </div>
-                    <div class="col-xs-8">
-                        <h3 class="no-margin">{% trans 'Information' %}</h3>
-                    </div>
-                    <div class="col-xs-2">
-                    </div>
                 </div>
             </div>
             <div class="panel-body" id="informationContainer">
@@ -36,7 +36,9 @@
         <div class="panel panel-default text-center" id="dragPanel">
             <div class="panel-heading">
                 <div class="row">
-                   <h3 class="no-margin">{% trans 'Elements' %}</h3>
+                   <div class="col-xs-12 text-left">
+                    <h3 class="no-margin"><i class="fa fa-outdent"></i> {% trans 'Elements' %}</h3>
+                   </div>
                 </div>
             </div>
             <div class="panel-heading text-center">
@@ -90,17 +92,17 @@
         <div class="panel panel-default">
             <div class="panel-heading text-center">
                 <div class="row">
-                    <div class="col-xs-2">
+                    <div class="col-xs-2 text-left">
                         <button class="btn btn-info btn-xs" id="undoMovement" title="{% trans 'Undo' %}"><i class="fa fa-undo"></i></button>
                     </div>
-                    <div class="col-xs-2">
+                    <div class="col-xs-2 text-left">
                         <button class="btn btn-info btn-xs" id="redoMovement" title="{% trans 'Redo' %}"><i class="fa fa-repeat"></i></button>
                     </div>
                     <div class="col-xs-4">
-                        <h3 class="no-margin" id="serviceName" contenteditable="true">Service</h3>
+                        <h3 class="no-margin" id="serviceName">Service</h3>
                     </div>
                     <div class="col-xs-2 text-right">
-                        <button class="btn btn-info btn-xs" id="clearService" title="{% trans 'Clear workspace' %}"><i class="fa fa-trash-o"></i></button>
+                        <button class="btn btn-info btn-xs" id="clearService" title="{% trans 'Clear workspace' %}"><i class="fa fa-eraser"></i></button>
                     </div>
                     <div class="col-xs-2 text-right">
                         <button class="btn btn-success btn-xs" id="saveService" title="{% trans 'Save workspace' %}"><i class="fa fa-floppy-o"></i></button>
@@ -109,12 +111,51 @@
             </div>
             <div class="panel-body" id="dropContainer" oncontextmenu="return false;"></div>
             <div class="panel-footer no-margin text-left">
-                <label class="no-margin" id="serviceStatus"></label>
+                <div class="row">
+                    <div class="col-xs-2 text-left">
+                            <label class="no-margin" id="serviceStatus"></label>
+                    </div>
+                    <div class="col-xs-2 col-xs-push-8 text-right">
+                            <button class="btn btn-danger btn-xs" id="deteleService" title="{% trans 'Delete service' %}" data-toggle="modal" data-target="#deleteServiceDialog"><i class="fa fa-trash-o"></i></button>
+                    </div>
+                </div>
             </div>
         </div>
     </div>
 
 </div>
 
+{% if actualId %}
+
+<!-- Modal -->
+<div id="deleteServiceDialog" class="modal fade" role="dialog">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal">&times;</button>
+                <h4 class="modal-title"><i class="fa fa-trash-o"></i> {% trans 'Deleting service' %}</h4>
+            </div>
+            <div class="modal-body">
+                <p>{% trans 'Are you sure you want to delete this service?' %}</p>
+            </div>
+            <div class="modal-footer">
+                <div class="row">
+                    <div class="col-xs-2 col-xs-push-8">
+                        <form method="post" action="{% url 'setty.views.service-delete' actualId %}">
+                        {% csrf_token %}
+                            <input type="submit" class="btn btn-danger btn-sm" value="{% trans 'Delete' %}" />
+                        </form>
+                    </div>
+                    <div class="col-xs-2 col-xs-push-8">
+                        <button type="button" class="btn btn-primary btn-sm" data-dismiss="modal">{% trans 'Close' %}</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+{% endif %}
+
 {% endblock %}
 
diff --git a/circle/setty/templates/setty/service_confirm_delete.html b/circle/setty/templates/setty/service_confirm_delete.html
deleted file mode 100644
index cddb18f..0000000
--- a/circle/setty/templates/setty/service_confirm_delete.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html>
-<form method="post">{% csrf_token %}
-    Are you sure you want to delete "{{ object }}" ?
-    <input type="submit" value="Submit" />
-</form>
\ No newline at end of file
diff --git a/circle/setty/urls.py b/circle/setty/urls.py
index f888ba4..2850187 100644
--- a/circle/setty/urls.py
+++ b/circle/setty/urls.py
@@ -20,7 +20,7 @@ from . import views
 
 urlpatterns = [
     url(r'^create/$',
-        views.IndexView.as_view(),
+        views.CreateView.as_view(),
         name='setty.views.service-create'),
     url(r'^delete/(?P<pk>\d+)$',
         views.DeleteView.as_view(),
@@ -28,6 +28,12 @@ urlpatterns = [
     url(r'^start/(?P<pk>\d+)$',
         views.StartView.as_view(),
         name='setty.views.service-start'),
+    url(r'^stop/(?P<pk>\d+)$',
+        views.StopView.as_view(),
+        name='setty.views.service-start'),
+    url(r'^stop/(?P<pk>\d+)$',
+        views.StartView.as_view(),
+        name='setty.views.service-stop'),
     url(r'^list/$',
         views.ListView.as_view(),
         name='setty.views.service-list'),
diff --git a/circle/setty/views.py b/circle/setty/views.py
index b241a5f..fcd9aee 100644
--- a/circle/setty/views.py
+++ b/circle/setty/views.py
@@ -15,67 +15,46 @@
 # You should have received a copy of the GNU General Public License along
 # with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
 
-from django.template import RequestContext
-from django.http import HttpResponse
-from django.views.decorators.csrf import csrf_exempt
-from django.views.generic import TemplateView, DeleteView
+from django.core.exceptions import PermissionDenied
 from django.core.urlresolvers import reverse_lazy
 from django.db.models import Q
-from django.http import HttpResponseForbidden, Http404
-from django.shortcuts import redirect
-from django.contrib import auth
-from .models import (
-    Element,
-    ElementTemplate,
-    ElementConnection,
-    Service
-)
+from django.http import JsonResponse
+from braces.views import LoginRequiredMixin
+from django.views.generic import TemplateView, DeleteView, CreateView
+from .models import Element, ElementTemplate, ElementConnection, Service
 import json
 
 
-class IndexView(TemplateView):
+class DetailView(LoginRequiredMixin, TemplateView):
     template_name = "setty/index.html"
 
-    def get(self, request, *args, **kwargs):
-        if self.request.user.is_authenticated():
-            return TemplateView.get(self, request, *args, **kwargs)
-        else:
-            return redirect(auth.views.login)
-
     def get_context_data(self, **kwargs):
-        elementTemplateList = ElementTemplate.objects.all()
-        context = RequestContext(
-            self.request,
-            {'elementTemplateList': elementTemplateList})
+        context = super(DetailView, self).get_context_data(**kwargs)
+        context['elementTemplateList'] = ElementTemplate.objects.all()
         return context
 
-    @csrf_exempt
     def post(self, request, *args, **kwargs):
-        if not self.request.user.is_authenticated():
-            return redirect(auth.views.login)
-
         if self.request.POST.get('event') == "saveService":
-            jsonData = json.loads(self.request.POST.get('data'))
-
-            serviceName = jsonData['serviceName']
+            data = json.loads(self.request.POST.get('data'))
+            service_name = data['serviceName']
 
             if 'pk' in kwargs:
-                serviceObject = Service.objects.get(id=kwargs['pk'])
-                serviceObject.name = serviceName
-                serviceObject.save()
+                service = Service.objects.get(id=kwargs['pk'])
+                service.name = service_name
+                service.save()
 
-                Element.objects.filter(service=serviceObject).delete()
+                Element.objects.filter(service=service).delete()
 
             else:
-                serviceObject = Service(
-                    name=serviceName,
+                service = Service(
+                    name=service_name,
                     user=self.request.user
                 )
-                serviceObject.save()
+                service.save()
 
-            for element in jsonData['elements']:
+            for element in data['elements']:
                 elementObject = Element(
-                    service=serviceObject,
+                    service=service,
                     parameters=element['parameters'],
                     display_id=element['displayId'],
                     pos_x=element['posX'],
@@ -84,7 +63,7 @@ class IndexView(TemplateView):
                 )
                 elementObject.save()
 
-            for elementConnection in jsonData['elementConnections']:
+            for elementConnection in data['elementConnections']:
                 sourceId = elementConnection['sourceId']
                 targetId = elementConnection['targetId']
                 sourceEndpoint = elementConnection['sourceEndpoint']
@@ -93,11 +72,11 @@ class IndexView(TemplateView):
 
                 targetObject = Element.objects.get(
                     display_id=targetId,
-                    service=serviceObject)
+                    service=service)
 
                 sourceObject = Element.objects.get(
                     display_id=sourceId,
-                    service=serviceObject)
+                    service=service)
 
                 connectionObject = ElementConnection(
                     target=targetObject,
@@ -108,46 +87,11 @@ class IndexView(TemplateView):
                 )
                 connectionObject.save()
 
-            return HttpResponse(serviceObject.pk)
-
-        else:
-            return HttpResponse()
-
-
-class DeleteView(DeleteView):
-    model = Service
-
-    success_url = reverse_lazy("dashboard.index")
-
-
-class StartView(TemplateView):
-    pass
-
-
-class ListView(TemplateView):
-    pass
-
-
-class DetailView(IndexView):
-    def get(self, request, *args, **kwargs):
-        try:
-            serviceObject = Service.objects.get(id=kwargs['pk'])
-            if serviceObject.user != self.request.user:
-                return HttpResponseForbidden(
-                    "You don't have permission to open the service.")
-        except:
-            raise Http404
-        else:
-            return IndexView.get(self, request, *args, **kwargs)
-
-    @csrf_exempt
-    def post(self, request, *args, **kwargs):
-        if not self.request.user.is_authenticated():
-            return redirect(auth.views.login)
+            return JsonResponse({'serviceName': service.name})
 
-        if self.request.POST.get('event') == "loadService":
-            serviceObject = Service.objects.get(id=kwargs['pk'])
-            elementList = Element.objects.filter(service=serviceObject)
+        elif self.request.POST.get('event') == "loadService":
+            service = Service.objects.get(id=kwargs['pk'])
+            elementList = Element.objects.filter(service=service)
             elementConnectionList = ElementConnection.objects.filter(
                 Q(target__in=elementList) | Q(source__in=elementList))
 
@@ -160,20 +104,39 @@ class DetailView(IndexView):
                     'displayId': item.display_id,
                     'posX': item.pos_x,
                     'posY': item.pos_y,
-                    'anchors': item.anchors
-                })
+                    'anchors': item.anchors})
 
             for item in elementConnectionList:
                 elementConnections.append({
                     'targetEndpoint': item.target_endpoint,
                     'sourceEndpoint': item.source_endpoint,
-                    'parameters': item.parameters
-                })
+                    'parameters': item.parameters})
 
-            return HttpResponse(json.dumps(
-                {"elements": elements,
-                 "elementConnections": elementConnections,
-                 "serviceName": serviceObject.name}))
+            return JsonResponse(
+                {'elements': elements,
+                 'elementConnections': elementConnections,
+                 'serviceName': service.name})
 
         else:
-            return IndexView.post(self, request, *args, **kwargs)
+            raise PermissionDenied
+
+
+class DeleteView(LoginRequiredMixin, DeleteView):
+    model = Service
+    success_url = reverse_lazy("dashboard.index")
+
+
+class CreateView(LoginRequiredMixin, CreateView):
+    pass
+
+
+class StartView(LoginRequiredMixin, TemplateView):
+    pass
+
+
+class StopView(LoginRequiredMixin, TemplateView):
+    pass
+
+
+class ListView(LoginRequiredMixin, TemplateView):
+    pass
--
libgit2 0.26.0