robeaux 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +14 -0
  3. data/{README.md → README.markdown} +22 -35
  4. data/css/normalize.css +425 -0
  5. data/css/style.css +469 -0
  6. data/css/themes/artoo.css +3 -0
  7. data/css/themes/cylon.css +3 -0
  8. data/css/themes/gobot.css +3 -0
  9. data/index.html +29 -36
  10. data/js/app.js +1 -0
  11. data/js/controllers/device_commands_ctrl.js +81 -0
  12. data/js/controllers/device_events_ctrl.js +31 -0
  13. data/js/controllers/index_ctrl.js +9 -0
  14. data/js/controllers/nav_ctrl.js +5 -0
  15. data/js/controllers/robot_commands_ctrl.js +34 -0
  16. data/js/controllers/robot_ctrl.js +25 -0
  17. data/js/controllers/themes_ctrl.js +36 -0
  18. data/js/router.js +22 -0
  19. data/js/services/themes.js +86 -0
  20. data/js/vendor/angular-route.min.js +14 -0
  21. data/js/vendor/angular.min.js +210 -0
  22. data/package.json +6 -9
  23. data/partials/device.html +67 -0
  24. data/partials/index.html +11 -0
  25. data/partials/robot.html +74 -0
  26. data/partials/themes.html +31 -21
  27. data/robeaux.gemspec +1 -1
  28. metadata +25 -22
  29. data/fonts/FontAwesome.otf +0 -0
  30. data/fonts/fontawesome-webfont.eot +0 -0
  31. data/fonts/fontawesome-webfont.svg +0 -414
  32. data/fonts/fontawesome-webfont.ttf +0 -0
  33. data/fonts/fontawesome-webfont.woff +0 -0
  34. data/javascripts/app.js +0 -219
  35. data/javascripts/vendor/angular-route.min.js +0 -14
  36. data/javascripts/vendor/angular.min.js +0 -202
  37. data/javascripts/vendor/bootstrap.min.js +0 -6
  38. data/javascripts/vendor/jquery.min.js +0 -4
  39. data/partials/robot-detail.html +0 -141
  40. data/partials/robot-index.html +0 -27
  41. data/stylesheets/bootstrap.css +0 -5780
  42. data/stylesheets/font-awesome.css +0 -1338
  43. data/stylesheets/style.css +0 -177
  44. data/stylesheets/themes/dark.css +0 -106
  45. data/stylesheets/themes/default.css +0 -73
  46. data/stylesheets/themes/flat.css +0 -164
@@ -0,0 +1,3 @@
1
+ body {
2
+ background: #027CAF;
3
+ }
@@ -0,0 +1,3 @@
1
+ body {
2
+ background: #620020;
3
+ }
@@ -0,0 +1,3 @@
1
+ body {
2
+ background: #FF7049;
3
+ }
data/index.html CHANGED
@@ -2,50 +2,43 @@
2
2
  <html lang="en" ng-app="robeaux">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
+
5
6
  <title>Robeaux</title>
6
- <link rel="stylesheet" href="/stylesheets/bootstrap.css">
7
- <link rel="stylesheet" href="/stylesheets/font-awesome.css">
8
- <link rel="stylesheet" href="/stylesheets/style.css">
7
+
8
+ <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto+Slab:700|Inconsolata:400,700|Roboto:400,700">
9
+ <link rel="stylesheet" href="/css/normalize.css">
10
+ <link rel="stylesheet" href="/css/style.css">
9
11
 
10
12
  <span class="ng-hide" ng-controller="ThemesCtrl">
11
- <link ng-if="!themes.current().custom" rel="stylesheet" ng-href="{{themes.current().url}}">
12
- <style ng-if="themes.current().custom" ng-bind-template="{{themes.current().css}}"></style>
13
+ <link ng-if="themes.active.url" rel="stylesheet" ng-href="{{themes.active.url}}">
14
+ <style ng-if="themes.active.css" ng-bind-template="{{themes.active.css}}"></style>
13
15
  </span>
14
16
  </head>
17
+
15
18
  <body>
16
- <nav class="navbar navbar-default" role="navigation">
17
- <div class="theme-selector" ng-controller="ThemesCtrl">
18
- <select class="nav navbar-nav" ng-selected="themes.setActiveTheme()" ng-model="themes.activeTheme" ng-options="name for name in themes.list()">
19
- </select>
20
- </div>
21
-
22
- <div class="container">
23
- <div class="navbar-header">
24
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
25
- <span class="sr-only">Toggle navigation</span>
26
- <span class="icon-bar"></span>
27
- <span class="icon-bar"></span>
28
- <span class="icon-bar"></span>
29
- </button>
30
- <a class="navbar-brand" href="#">Robeaux</a>
31
- </div>
32
-
33
- <div class="collapse navbar-collapse">
34
- <ul class="nav navbar-nav" ng-controller="NavigationCtrl">
35
- <li ng-class="{active: active('robots')}"><a href="#/robots">Robots</a></li>
36
- <li ng-class="{active: active('themes')}"><a href="#/themes">Themes</a></li>
37
- </ul>
38
- </div>
39
-
40
- </div>
19
+ <nav ng-controller="NavCtrl">
20
+ <span class="logo">Robeaux</span>
21
+
22
+ <span class="links">
23
+ <a href="#/robots" ng-class="{active: active('robots')}">robots</a>
24
+ <a href="#/themes" ng-class="{active: active('themes')}">themes</a>
25
+ </span>
41
26
  </nav>
42
27
 
43
- <div ng-view></div>
28
+ <div ng-view class="container"></div>
29
+
30
+ <script src="/js/vendor/angular.min.js"></script>
31
+ <script src="/js/vendor/angular-route.min.js"></script>
44
32
 
45
- <script src="/javascripts/vendor/jquery.min.js"></script>
46
- <script src="/javascripts/vendor/angular.min.js"></script>
47
- <script src="/javascripts/vendor/angular-route.min.js"></script>
48
- <script src="/javascripts/vendor/bootstrap.min.js"></script>
49
- <script src="/javascripts/app.js"></script>
33
+ <script src="/js/app.js"></script>
34
+ <script src="/js/router.js"></script>
35
+ <script src="/js/services/themes.js"></script>
36
+ <script src="/js/controllers/nav_ctrl.js"></script>
37
+ <script src="/js/controllers/index_ctrl.js"></script>
38
+ <script src="/js/controllers/themes_ctrl.js"></script>
39
+ <script src="/js/controllers/robot_ctrl.js"></script>
40
+ <script src="/js/controllers/robot_commands_ctrl.js"></script>
41
+ <script src="/js/controllers/device_commands_ctrl.js"></script>
42
+ <script src="/js/controllers/device_events_ctrl.js"></script>
50
43
  </body>
51
44
  </html>
data/js/app.js ADDED
@@ -0,0 +1 @@
1
+ var robeaux = angular.module('robeaux', ['ngRoute']);
@@ -0,0 +1,81 @@
1
+ var DeviceCommandsCtrl = function DeviceCommandsCtrl($scope, $http) {
2
+ $scope.command = "";
3
+ $scope.types = [ 'string', 'boolean', 'number' ];
4
+
5
+ $scope.isDisabled = function() {
6
+ return ($scope.command === "")
7
+ };
8
+
9
+ $scope.addParam = function(last) {
10
+ if (!last) { return; }
11
+ $scope.device.params.push({ name: '', value: '', type: 'string' });
12
+ };
13
+
14
+ $scope.removeParam = function(index) {
15
+ if ($scope.device.params.length === 1) { return; }
16
+ $scope.device.params.splice(index, 1);
17
+ };
18
+
19
+ $scope.submit = function() {
20
+ var robot = $scope.robot.name,
21
+ device = $scope.device.name,
22
+ command = $scope.command,
23
+ params = parseParams($scope.device.params);
24
+
25
+ var url ='/robots/' + robot + "/devices/" + device + "/commands/" + command;
26
+
27
+ $http.post(url, params).success(function(data) {
28
+ if (data.result) {
29
+ if ($scope.device.results.length > 4) { $scope.device.results.pop(); }
30
+ $scope.device.results.unshift(data);
31
+ }
32
+ });
33
+ };
34
+ }
35
+
36
+ // Parses command params, coercing to types where necessary.
37
+ //
38
+ // Returns an array.
39
+ var parseParams = function(formParams) {
40
+ if (paramsAreEmpty(formParams)) { return null; }
41
+
42
+ var params = {};
43
+
44
+ for (var i = 0; i < formParams.length; i++) {
45
+ var param = formParams[i];
46
+
47
+ // skip if param name or value is empty
48
+ if (param.name === '' || param.value === '') { continue; }
49
+
50
+ params[param.name] = param.value;
51
+
52
+ switch(param.type) {
53
+ case 'boolean':
54
+ string = String(param.value).toLowerCase();
55
+ params[param.name] = (string === 'true' || string === 't');
56
+ break;
57
+
58
+ case 'number':
59
+ params[param.name] = Number(param.value)
60
+ break;
61
+
62
+ default:
63
+ // assume a string, nothing changes
64
+ break;
65
+ }
66
+ };
67
+
68
+ return params;
69
+ };
70
+
71
+ var paramsAreEmpty = function(params) {
72
+ for (var i = 0; i < params.length; i++) {
73
+ var param = params[i]
74
+
75
+ if (param.name !== '' || param.value !== '') {
76
+ return false;
77
+ };
78
+ }
79
+
80
+ return true;
81
+ };
@@ -0,0 +1,31 @@
1
+ var DeviceEventsCtrl = function DeviceEventsCtrl($scope, $filter) {
2
+ $scope.listen = function() {
3
+ if ($scope.eventName === "") { return; }
4
+
5
+ if ($scope.device.events == null) { $scope.device.events = []; }
6
+ if ($scope.device.listeners == null) { $scope.device.listeners = {}; }
7
+
8
+ var robot = $scope.robot.name,
9
+ device = $scope.device.name,
10
+ event = $scope.eventName;
11
+
12
+ var uri = "/robots/" + robot + "/devices/" + device + "/events/" + event;
13
+ var $device = $scope.device;
14
+ var source = new EventSource(uri);
15
+
16
+ source.addEventListener('message', function(message) {
17
+ $scope.$apply(function() {
18
+ if ($device.events.length > 4) { $device.events.pop(); }
19
+ $device.events.unshift({ name: event, data: JSON.parse(message.data) });
20
+ });
21
+ }, false);
22
+
23
+ $scope.device.listeners[event] = source;
24
+ $scope.eventName = "";
25
+ };
26
+
27
+ $scope.remove = function(name) {
28
+ $scope.device.listeners[name].close();
29
+ delete $scope.device.listeners[name];
30
+ };
31
+ }
@@ -0,0 +1,9 @@
1
+ var IndexCtrl = function IndexCtrl($scope, $http, $location) {
2
+ $http.get("/robots").success(function(data) {
3
+ $scope.robots = data;
4
+ });
5
+
6
+ $scope.details = function (robot) {
7
+ $location.path("robots/" + robot);
8
+ }
9
+ };
@@ -0,0 +1,5 @@
1
+ var NavCtrl = function NavCtrl($scope, $location) {
2
+ $scope.active = function(path) {
3
+ return ($location.path().substring(1).split("/")[0] || "robots") === path;
4
+ }
5
+ }
@@ -0,0 +1,34 @@
1
+ var RobotCommandsCtrl = function RobotCommandsCtrl($scope, $http) {
2
+ $scope.command = "";
3
+ $scope.types = [ 'string', 'boolean', 'number' ];
4
+
5
+ $scope.isDisabled = function() {
6
+ return ($scope.command === "")
7
+ };
8
+
9
+ $scope.addParam = function(last) {
10
+ console.log("HEY");
11
+ if (!last) { return; }
12
+ $scope.robot.params.push({ name: '', value: '', type: 'string' });
13
+ };
14
+
15
+ $scope.removeParam = function(index) {
16
+ if ($scope.robot.params.length === 1) { return; }
17
+ $scope.robot.params.splice(index, 1);
18
+ };
19
+
20
+ $scope.submit = function() {
21
+ var robot = $scope.robot.name,
22
+ command = $scope.command,
23
+ params = parseParams($scope.robot.params);
24
+
25
+ var url ='/robots/' + robot + "/commands/" + command;
26
+
27
+ $http.post(url, params).success(function(data) {
28
+ if (data.result) {
29
+ if ($scope.robot.results.length > 4) { $scope.robot.results.pop(); }
30
+ $scope.robot.results.unshift(data);
31
+ }
32
+ });
33
+ };
34
+ }
@@ -0,0 +1,25 @@
1
+ var RobotCtrl = function RobotCtrl($scope, $http, $routeParams) {
2
+ $http.get("/robots/" + $routeParams.robot).success(function(data) {
3
+ $scope.robot = data;
4
+
5
+ $scope.robot.params = [ { name: '', value: '', type: 'string' } ];
6
+ $scope.robot.results = [];
7
+ });
8
+
9
+ $scope.select = function(device) {
10
+ $scope.device = ($scope.device === device) ? null : device;
11
+
12
+ // setting up params we'll need in other controllers
13
+ if ($scope.device.params == null) {
14
+ $scope.device.params = [ { name: '', value: '', type: 'string' } ];
15
+ }
16
+
17
+ if ($scope.device.events == null) { $scope.device.events = []; }
18
+ if ($scope.device.results == null) { $scope.device.results = []; }
19
+ if ($scope.device.listeners == null) { $scope.device.listeners = {}; }
20
+ }
21
+
22
+ $scope.selected = function(device) {
23
+ return ($scope.device === device);
24
+ }
25
+ }
@@ -0,0 +1,36 @@
1
+ var ThemesCtrl = function ThemesCtrl($scope, Themes) {
2
+ $scope.themes = Themes;
3
+
4
+ // save when a theme is selected
5
+ $scope.$watch('themes.active', function() {
6
+ // watch for corner case where 'themes.active' update was causing themes to
7
+ // be saved as undefined
8
+ if (Themes.list) { Themes.save(); }
9
+ });
10
+
11
+ // selecting a theme for editing
12
+ $scope.edit = function(theme) {
13
+ if ($scope.editing === theme || !theme.custom) {
14
+ return $scope.editing = null;
15
+ }
16
+
17
+ $scope.editing = theme;
18
+ };
19
+
20
+ // add a new theme
21
+ $scope.add = function(name) {
22
+ if (!name || name === '') { return false; }
23
+
24
+ var result = Themes.add(name);
25
+ if (result) {
26
+ $scope.edit(result);
27
+ $scope.name = '';
28
+ }
29
+ };
30
+
31
+ // remove an existing theme
32
+ $scope.remove = function(name) {
33
+ if ($scope.editing === Themes.find(name)) { $scope.editing = null; }
34
+ Themes.remove(name);
35
+ };
36
+ };
data/js/router.js ADDED
@@ -0,0 +1,22 @@
1
+ robeaux.config(function($routeProvider) {
2
+ $routeProvider
3
+
4
+ .when('/robots', {
5
+ controller: 'IndexCtrl',
6
+ templateUrl: '/partials/index.html',
7
+ })
8
+
9
+ .when('/robots/:robot', {
10
+ controller: 'RobotCtrl',
11
+ templateUrl: '/partials/robot.html',
12
+ })
13
+
14
+ .when('/themes', {
15
+ controller: 'ThemesCtrl',
16
+ templateUrl: '/partials/themes.html',
17
+ })
18
+
19
+ .otherwise({
20
+ redirectTo: '/robots'
21
+ });
22
+ });
@@ -0,0 +1,86 @@
1
+ robeaux.factory('Themes', function() {
2
+ var service = {};
3
+
4
+ // default themes
5
+ var defaults = [
6
+ { name: 'default', custom: false, css: '' },
7
+ { name: 'artoo', custom: false, url: '/css/themes/artoo.css'},
8
+ { name: 'cylon', custom: false, url: '/css/themes/cylon.css'},
9
+ { name: 'gobot', custom: false, url: '/css/themes/gobot.css'},
10
+ { name: 'custom', custom: true, css: ''}
11
+ ];
12
+
13
+ service.list = [];
14
+ service.active = {};
15
+
16
+ // loads themes from localstorage, falling back to the above default themes
17
+ service.load = function() {
18
+ if (localStorage['themes']) {
19
+ this.list = angular.fromJson(localStorage['themes']);
20
+ } else {
21
+ this.list = defaults;
22
+ }
23
+
24
+ if (localStorage['active'] && this.find(localStorage['active'])) {
25
+ this.active = this.find(localStorage['active']);
26
+ } else {
27
+ this.active = this.find('default');
28
+ }
29
+ };
30
+
31
+ // saves themes to localstorage
32
+ service.save = function() {
33
+ localStorage.setItem('themes', angular.toJson(this.list));
34
+ localStorage.setItem('active', this.active.name);
35
+ };
36
+
37
+ // find a specific theme by name
38
+ service.find = function(name) {
39
+ for (var i = 0; i < this.list.length; i++) {
40
+ if (this.list[i].name === name) { return this.list[i]; }
41
+ }
42
+ return false;
43
+ };
44
+
45
+ // add a new custom theme
46
+ service.add = function(name) {
47
+ if (this.find(name)) { return false; }
48
+
49
+ var theme = {
50
+ name: name,
51
+ custom: true,
52
+ css: "/* write some css for the " + name + " theme here */"
53
+ };
54
+
55
+ this.list.push(theme);
56
+
57
+ this.save();
58
+
59
+ return this.find(name);
60
+ };
61
+
62
+ service.remove = function(name) {
63
+ if (this.active.name === name) {
64
+ this.active = this.find('default');
65
+ };
66
+
67
+ for (var i = 0; i < this.list.length; i++) {
68
+ if (this.list[i].name === name) {
69
+ this.list.splice(i, 1);
70
+ }
71
+ }
72
+
73
+ this.save();
74
+ };
75
+
76
+ // reset localStorage and reload everything from defaults.
77
+ service.reset = function() {
78
+ localStorage.removeItem('themes');
79
+ localStorage.removeItem('active');
80
+ this.load();
81
+ }
82
+
83
+ service.load();
84
+
85
+ return service;
86
+ });
@@ -0,0 +1,14 @@
1
+ /*
2
+ AngularJS v1.2.16
3
+ (c) 2010-2014 Google, Inc. http://angularjs.org
4
+ License: MIT
5
+ */
6
+ (function(n,e,A){'use strict';function x(s,g,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,w){function y(){p&&(p.remove(),p=null);h&&(h.$destroy(),h=null);l&&(k.leave(l,function(){p=null}),p=l,l=null)}function v(){var b=s.current&&s.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),d=s.current;l=w(b,function(d){k.enter(d,null,l||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});h=d.scope=b;h.$emit("$viewContentLoaded");h.$eval(u)}else y()}
7
+ var h,l,p,t=b.autoscroll,u=b.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var w=e(c.contents());b.controller&&(f.$scope=a,f=g(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,c){return e.extend(new (e.extend(function(){},
8
+ {prototype:a})),c)}function g(a,e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},k=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;k.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&g(a,c));if(a){var b=
9
+ "/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},g(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,g,n,v,h){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart",
10
+ d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(t(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?g.get(d):g.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=h.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=
11
+ b,c=n.get(b,{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function p(){var a,b;e.forEach(k,function(f,k){var q;if(q=!b){var g=c.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var h=1,p=g.length;h<p;++h){var n=q[h-1],r="string"==typeof g[h]?decodeURIComponent(g[h]):
12
+ g[h];n&&r&&(l[n.name]=r)}q=l}else q=null;else q=null;q=a=q}q&&(b=s(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&s(k[null],{params:{},pathParams:{}})}function t(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var u=!1,r={routes:k,reload:function(){u=!0;a.$evalAsync(l)}};a.$on("$locationChangeSuccess",l);return r}]});n.provider("$routeParams",
13
+ function(){this.$get=function(){return{}}});n.directive("ngView",x);n.directive("ngView",z);x.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
14
+ //# sourceMappingURL=angular-route.min.js.map