robeaux 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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