From ddf6c5b8cec1cd21257d91938db883b3620e4bbc Mon Sep 17 00:00:00 2001 From: Robert Marshall Date: Sat, 27 Apr 2019 21:24:52 +0100 Subject: [PATCH] Re-implement existing website template (from PHP). Add all assets. --- Website/Views/Home/Index.cshtml | 4 +- Website/Views/Shared/_Layout.cshtml | 150 ++++-- Website/wwwroot/css/animations.less | 31 ++ Website/wwwroot/css/blog.less | 24 + Website/wwwroot/css/colours.less | 23 + Website/wwwroot/css/contextMenu.less | 35 ++ Website/wwwroot/css/gallery.less | 198 ++++++++ Website/wwwroot/css/material_colours.less | 272 +++++++++++ Website/wwwroot/css/site.less | 49 -- Website/wwwroot/css/style-med.less | 50 ++ Website/wwwroot/css/style-small.less | 47 ++ Website/wwwroot/css/style.less | 426 ++++++++++++++++++ Website/wwwroot/css/temperature.less | 42 ++ Website/wwwroot/images/add.svg | 12 + Website/wwwroot/images/add_album.svg | 11 + Website/wwwroot/images/blog.svg | 9 + Website/wwwroot/images/chevron.png | Bin 0 -> 49154 bytes Website/wwwroot/images/delete.svg | 12 + Website/wwwroot/images/desktop.svg | 11 + Website/wwwroot/images/done.svg | 9 + Website/wwwroot/images/edit.svg | 12 + Website/wwwroot/images/error.svg | 10 + Website/wwwroot/images/gallery.svg | 11 + Website/wwwroot/images/home.svg | 10 + Website/wwwroot/images/library.svg | 13 + Website/wwwroot/images/logo_email.png | Bin 0 -> 4770 bytes Website/wwwroot/images/member.svg | 10 + Website/wwwroot/images/menu.svg | 10 + Website/wwwroot/images/next.svg | 8 + Website/wwwroot/images/photo.svg | 13 + Website/wwwroot/images/power.svg | 10 + Website/wwwroot/images/previous.svg | 8 + Website/wwwroot/images/projects.svg | 19 + Website/wwwroot/images/register.svg | 13 + Website/wwwroot/images/save.svg | 15 + Website/wwwroot/images/send.svg | 8 + Website/wwwroot/images/spinner.svg | 1 + Website/wwwroot/images/status.svg | 16 + Website/wwwroot/images/temperature.svg | 10 + Website/wwwroot/images/upload.svg | 9 + Website/wwwroot/images/weight.svg | 11 + Website/wwwroot/js/controllers/gallery.js | 12 + .../wwwroot/js/controllers/galleryManage.js | 77 ++++ .../wwwroot/js/controllers/galleryUpload.js | 101 +++++ Website/wwwroot/js/controllers/main.js | 107 +++++ Website/wwwroot/js/controllers/status.js | 65 +++ Website/wwwroot/js/controllers/temperature.js | 56 +++ Website/wwwroot/js/controllers/weight.js | 48 ++ Website/wwwroot/js/directives/contextMenu.js | 58 +++ Website/wwwroot/js/directives/dragDrop.js | 38 ++ .../wwwroot/js/directives/equalHeightWidth.js | 14 + Website/wwwroot/js/directives/googleChart.js | 122 +++++ Website/wwwroot/js/directives/scopeInit.js | 11 + .../js/directives/templates/contextMenu.html | 6 + .../js/directives/templates/googleChart.html | 1 + Website/wwwroot/js/javascript.js | 145 ++++++ Website/wwwroot/js/services/statusService.js | 60 +++ .../wwwroot/js/services/temperatureService.js | 13 + Website/wwwroot/js/services/weightService.js | 31 ++ Website/wwwroot/js/site.js | 4 - 60 files changed, 2514 insertions(+), 87 deletions(-) create mode 100644 Website/wwwroot/css/animations.less create mode 100644 Website/wwwroot/css/blog.less create mode 100644 Website/wwwroot/css/colours.less create mode 100644 Website/wwwroot/css/contextMenu.less create mode 100644 Website/wwwroot/css/gallery.less create mode 100644 Website/wwwroot/css/material_colours.less delete mode 100644 Website/wwwroot/css/site.less create mode 100644 Website/wwwroot/css/style-med.less create mode 100644 Website/wwwroot/css/style-small.less create mode 100644 Website/wwwroot/css/style.less create mode 100644 Website/wwwroot/css/temperature.less create mode 100644 Website/wwwroot/images/add.svg create mode 100644 Website/wwwroot/images/add_album.svg create mode 100644 Website/wwwroot/images/blog.svg create mode 100644 Website/wwwroot/images/chevron.png create mode 100644 Website/wwwroot/images/delete.svg create mode 100644 Website/wwwroot/images/desktop.svg create mode 100644 Website/wwwroot/images/done.svg create mode 100644 Website/wwwroot/images/edit.svg create mode 100644 Website/wwwroot/images/error.svg create mode 100644 Website/wwwroot/images/gallery.svg create mode 100644 Website/wwwroot/images/home.svg create mode 100644 Website/wwwroot/images/library.svg create mode 100644 Website/wwwroot/images/logo_email.png create mode 100644 Website/wwwroot/images/member.svg create mode 100644 Website/wwwroot/images/menu.svg create mode 100644 Website/wwwroot/images/next.svg create mode 100644 Website/wwwroot/images/photo.svg create mode 100644 Website/wwwroot/images/power.svg create mode 100644 Website/wwwroot/images/previous.svg create mode 100644 Website/wwwroot/images/projects.svg create mode 100644 Website/wwwroot/images/register.svg create mode 100644 Website/wwwroot/images/save.svg create mode 100644 Website/wwwroot/images/send.svg create mode 100644 Website/wwwroot/images/spinner.svg create mode 100644 Website/wwwroot/images/status.svg create mode 100644 Website/wwwroot/images/temperature.svg create mode 100644 Website/wwwroot/images/upload.svg create mode 100644 Website/wwwroot/images/weight.svg create mode 100644 Website/wwwroot/js/controllers/gallery.js create mode 100644 Website/wwwroot/js/controllers/galleryManage.js create mode 100644 Website/wwwroot/js/controllers/galleryUpload.js create mode 100644 Website/wwwroot/js/controllers/main.js create mode 100644 Website/wwwroot/js/controllers/status.js create mode 100644 Website/wwwroot/js/controllers/temperature.js create mode 100644 Website/wwwroot/js/controllers/weight.js create mode 100644 Website/wwwroot/js/directives/contextMenu.js create mode 100644 Website/wwwroot/js/directives/dragDrop.js create mode 100644 Website/wwwroot/js/directives/equalHeightWidth.js create mode 100644 Website/wwwroot/js/directives/googleChart.js create mode 100644 Website/wwwroot/js/directives/scopeInit.js create mode 100644 Website/wwwroot/js/directives/templates/contextMenu.html create mode 100644 Website/wwwroot/js/directives/templates/googleChart.html create mode 100644 Website/wwwroot/js/javascript.js create mode 100644 Website/wwwroot/js/services/statusService.js create mode 100644 Website/wwwroot/js/services/temperatureService.js create mode 100644 Website/wwwroot/js/services/weightService.js delete mode 100644 Website/wwwroot/js/site.js diff --git a/Website/Views/Home/Index.cshtml b/Website/Views/Home/Index.cshtml index 975fe7d..89aef55 100644 --- a/Website/Views/Home/Index.cshtml +++ b/Website/Views/Home/Index.cshtml @@ -1,6 +1,6 @@ @{ - ViewData["Title"] = "Home Page"; + ViewData["Title"] = "Home"; } -

Hi, I'm Rob. I'm a software developer. I primarily work with C# in my professional life, but my home projects are often PHP and Python based.

+

Hi, I'm Rob. I'm a senior software engineer at Code Computerlove.

If you wish to get in contact, then get in touch via my LinkedIn profile

\ No newline at end of file diff --git a/Website/Views/Shared/_Layout.cshtml b/Website/Views/Shared/_Layout.cshtml index 5dacc27..577fb9f 100644 --- a/Website/Views/Shared/_Layout.cshtml +++ b/Website/Views/Shared/_Layout.cshtml @@ -1,37 +1,123 @@ - - - - - - @ViewData["Title"] - Website +@{ + var angularSource = "ajax.googleapis.com/ajax/libs/angularjs"; + var angularVersion = "1.5.0"; - - - - - - - - -
- - -
-
- @RenderBody() -
+ var jsFiles = new[]{ + "javascript.js", + "controllers/main.js", + "directives/equalHeightWidth.js", + "directives/scopeInit.js", + }; +} - + + + + + + @ViewData["Title"] | Robware - + + + + + + + + + + + + + + + + + + @foreach (var file in jsFiles){ + + } + + + + +
+
+ @{ // using code blocks to remove HTML space but to keep source tidy + }

@{ // Shitty, I know, but eh... + }@ViewData["Title"]@{ + }

+
+
+ The following errors were encountered: +
    +
  • {{error}}
  • +
+ Please rectify them and try again. +
+
+
+ @RenderBody() +
+ +
+
@RenderSection("ButtonsLeft", required: false)
+
@RenderSection("ButtonsRight", required: false)
+
+
+
+ +
- @RenderSection("Scripts", required: false) - + @RenderSection("Scripts", required: false) + diff --git a/Website/wwwroot/css/animations.less b/Website/wwwroot/css/animations.less new file mode 100644 index 0000000..e4a382a --- /dev/null +++ b/Website/wwwroot/css/animations.less @@ -0,0 +1,31 @@ +.ng-leave { + opacity:1; + + &.ng-leave-active { + opacity:0; + } +} + +.ng-enter{ + opacity:0; + + &.ng-enter-active { + opacity:1; + } +} + +.ng-hide-add{ + opacity:1; + + &.ng-hide-add-active{ + opacity:0; + } +} + +.ng-hide-remove{ + opacity:0; + + &.ng-hide-remove-active{ + opacity:1; + } +} \ No newline at end of file diff --git a/Website/wwwroot/css/blog.less b/Website/wwwroot/css/blog.less new file mode 100644 index 0000000..b4a6676 --- /dev/null +++ b/Website/wwwroot/css/blog.less @@ -0,0 +1,24 @@ +.ng-leave { + opacity: 1; +} +.ng-leave.ng-leave-active { + opacity: 0; +} +.ng-enter { + opacity: 0; +} +.ng-enter.ng-enter-active { + opacity: 1; +} +.ng-hide-add { + opacity: 1; +} +.ng-hide-add.ng-hide-add-active { + opacity: 0; +} +.ng-hide-remove { + opacity: 0; +} +.ng-hide-remove.ng-hide-remove-active { + opacity: 1; +} diff --git a/Website/wwwroot/css/colours.less b/Website/wwwroot/css/colours.less new file mode 100644 index 0000000..34de056 --- /dev/null +++ b/Website/wwwroot/css/colours.less @@ -0,0 +1,23 @@ +@import "material_colours.less"; + +@background:@Grey-50; + +@primary-100:@Red-100; +@primary-200:@Red-200; +@primary-300:@Red-300; +@primary-400:@Red-400; +@primary-500:@Red-500; +@primary-600:@Red-600; +@primary-700:@Red-700; +@primary-800:@Red-800; +@primary-900:@Red-900; + +@accent-100:@Teal-100; +@accent-200:@Teal-200; +@accent-400:@Teal-400; +@accent-700:@Teal-700; + +@primary:@primary-600; +@accent:@accent-400; +@control:@Grey-200; +@control2:@Grey-300; \ No newline at end of file diff --git a/Website/wwwroot/css/contextMenu.less b/Website/wwwroot/css/contextMenu.less new file mode 100644 index 0000000..627ee37 --- /dev/null +++ b/Website/wwwroot/css/contextMenu.less @@ -0,0 +1,35 @@ +@import "colours.less"; + +.contextMenu{ + position: absolute; + float:left; + background:@background; + border:1px solid @Grey-500; + border-radius:3px; + padding: 3px 0; + box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.5); + -webkit-user-select: none; /* Chrome all / Safari all */ + -moz-user-select: none; /* Firefox all */ + -ms-user-select: none; /* IE 10+ */ + user-select: none; + cursor:default; + z-index:10000; + + .menuItem{ + padding: 0 5px; + position:relative; + white-space: nowrap; + + &:hover{ + background: @accent-200; + } + + &:not(:last-child){ + border-bottom: 1px solid @Grey-200; + } + + .arrow{ + text-align: right; + } + } +} \ No newline at end of file diff --git a/Website/wwwroot/css/gallery.less b/Website/wwwroot/css/gallery.less new file mode 100644 index 0000000..78e71eb --- /dev/null +++ b/Website/wwwroot/css/gallery.less @@ -0,0 +1,198 @@ +@import "colours.less"; + +#tabs{ + background:@primary; + margin:-20px; + margin-bottom:10px; + position:fixed; + width:100%; + box-shadow: 0px 0px 5px 0px rgba(50, 50, 50, 0.8); + overflow-x:auto; + white-space:nowrap; + + a{ + display:inline-block; + color:@primary-100; + padding:15px 30px; + text-decoration:none; + text-transform: uppercase; + + &.active{ + color:#fff; + border-bottom:3px solid #fff; + } + } +} + +.album{ + width:33.33%; + /*float:left;*/ + display:inline-block; + vertical-align:top; + + div{ + margin:5px; + padding:5px; + background:@control2; + } + + span{ + display:block; + text-align:center; + color:@Grey-800; + overflow:hidden; + } +} + +.imageSelector{ + .image{ + width:25%; + display:inline-block; + vertical-align:top; + text-align:center; + + &.selected { + >div{ + background:@primary-300; + + &:hover{ + background:@primary-400; + } + } + } + + input[type=checkbox]{ + display:none; + } + + >div{ + margin:5px; + padding:5px; + background:@control2; + + &:hover{ + background:@Grey-400; + } + } + } +} + +#album-viewer{ + height:100%; + display:flex; + flex-direction: column; + + #album-description{ + flex: 0 0 auto; + + .expander{ + padding:5px; + background:@control2; + + &:after{ + float:right; + content: "+"; + } + + &.expanded:after{ + content: '-'; + } + } + + .text{ + background:@control; + padding:5px; + + &.ng-hide{ + //opacity: 0; + line-height: 0; + } + } + } + + #album-thumbnails{ + flex: 0 0 auto; + overflow-x:auto; + overflow-y:hidden; + white-space:nowrap; + text-align:center; + + img{ + width:100px; + border:3px solid transparent; + + &.selected{ + border:3px solid @primary-300; + } + } + } + + #album-images{ + flex: 1 1 auto; + overflow:auto; + position:relative; + + .image{ + position:absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow:hidden; + text-align:center; + + img{ + max-height: 100%; + + } + } + } +} + +#galleryUpload{ + + .upload{ + font-size:2em; + padding:5px; + } + + .dragDrop{ + min-height:200px; + border-color: @background; + background-position: center; + background-size: 100px; + margin-bottom:5px; + + &.dragOver{ + border: 3px dashed @Grey-700; + background-image: url("/images/upload.svg"); + background-repeat: no-repeat; + } + } + + .newImage{ + display:flex; + margin-bottom:5px; + + .information{ + flex:1; + } + + .preview{ + flex:1; + position: relative; + + .imageContainer{ + position:absolute; + top:0; + bottom:0; + right:0; + text-align:right; + + img{ + height:100%; + } + } + } + } +} \ No newline at end of file diff --git a/Website/wwwroot/css/material_colours.less b/Website/wwwroot/css/material_colours.less new file mode 100644 index 0000000..712b81d --- /dev/null +++ b/Website/wwwroot/css/material_colours.less @@ -0,0 +1,272 @@ +@Red-50:#ffebee; +@Red-100:#ffcdd2; +@Red-200:#ef9a9a; +@Red-300:#e57373; +@Red-400:#ef5350; +@Red-500:#f44336; +@Red-600:#e53935; +@Red-700:#d32f2f; +@Red-800:#c62828; +@Red-900:#b71c1c; +@Red-A100:#ff8a80; +@Red-A200:#ff5252; +@Red-A400:#ff1744; +@Red-A700:#d50000; + +@Pink-50:#fce4ec; +@Pink-100:#f8bbd0; +@Pink-200:#f48fb1; +@Pink-300:#f06292; +@Pink-400:#ec407a; +@Pink-500:#e91e63; +@Pink-600:#d81b60; +@Pink-700:#c2185b; +@Pink-800:#ad1457; +@Pink-900:#880e4f; +@Pink-A100:#ff80ab; +@Pink-A200:#ff4081; +@Pink-A400:#f50057; +@Pink-A700:#c51162; + +@Purple-50:#f3e5f5; +@Purple-100:#e1bee7; +@Purple-200:#ce93d8; +@Purple-300:#ba68c8; +@Purple-400:#ab47bc; +@Purple-500:#9c27b0; +@Purple-600:#8e24aa; +@Purple-700:#7b1fa2; +@Purple-800:#6a1b9a; +@Purple-900:#4a148c; +@Purple-A100:#ea80fc; +@Purple-A200:#e040fb; +@Purple-A400:#d500f9; +@Purple-A700:#aa00ff; + +@DeepPurple-50:#ede7f6; +@DeepPurple-50100:#d1c4e9; +@DeepPurple-50200:#b39ddb; +@DeepPurple-50300:#9575cd; +@DeepPurple-50400:#7e57c2; +@DeepPurple-50500:#673ab7; +@DeepPurple-50600:#5e35b1; +@DeepPurple-50700:#512da8; +@DeepPurple-50800:#4527a0; +@DeepPurple-50900:#311b92; +@DeepPurple-50A100:#b388ff; +@DeepPurple-50A200:#7c4dff; +@DeepPurple-50A400:#651fff; +@DeepPurple-50A700:#6200ea; + +@Indigo-50:#e8eaf6; +@Indigo-100:#c5cae9; +@Indigo-200:#9fa8da; +@Indigo-300:#7986cb; +@Indigo-400:#5c6bc0; +@Indigo-500:#3f51b5; +@Indigo-600:#3949ab; +@Indigo-700:#303f9f; +@Indigo-800:#283593; +@Indigo-900:#1a237e; +@Indigo-A100:#8c9eff; +@Indigo-A200:#536dfe; +@Indigo-A400:#3d5afe; +@Indigo-A700:#304ffe; + +@Blue-50:#e3f2fd; +@Blue-100:#bbdefb; +@Blue-200:#90caf9; +@Blue-300:#64b5f6; +@Blue-400:#42a5f5; +@Blue-500:#2196f3; +@Blue-600:#1e88e5; +@Blue-700:#1976d2; +@Blue-800:#1565c0; +@Blue-900:#0d47a1; +@Blue-A100:#82b1ff; +@Blue-A200:#448aff; +@Blue-A400:#2979ff; +@Blue-A700:#2962ff; + +@LightBlue-50:#e1f5fe; +@LightBlue-100:#b3e5fc; +@LightBlue-200:#81d4fa; +@LightBlue-300:#4fc3f7; +@LightBlue-400:#29b6f6; +@LightBlue-500:#03a9f4; +@LightBlue-600:#039be5; +@LightBlue-700:#0288d1; +@LightBlue-800:#0277bd; +@LightBlue-900:#01579b; +@LightBlue-A100:#80d8ff; +@LightBlue-A200:#40c4ff; +@LightBlue-A400:#00b0ff; +@LightBlue-A700:#0091ea; + +@Cyan-50:#e0f7fa; +@Cyan-100:#b2ebf2; +@Cyan-200:#80deea; +@Cyan-300:#4dd0e1; +@Cyan-400:#26c6da; +@Cyan-500:#00bcd4; +@Cyan-600:#00acc1; +@Cyan-700:#0097a7; +@Cyan-800:#00838f; +@Cyan-900:#006064; +@Cyan-A100:#84ffff; +@Cyan-A200:#18ffff; +@Cyan-A400:#00e5ff; +@Cyan-A700:#00b8d4; + +@Teal-50:#e0f2f1; +@Teal-100:#b2dfdb; +@Teal-200:#80cbc4; +@Teal-300:#4db6ac; +@Teal-400:#26a69a; +@Teal-500:#009688; +@Teal-600:#00897b; +@Teal-700:#00796b; +@Teal-800:#00695c; +@Teal-900:#004d40; +@Teal-A100:#a7ffeb; +@Teal-A200:#64ffda; +@Teal-A400:#1de9b6; +@Teal-A700:#00bfa5; + +@Green-50:#e8f5e9; +@Green-100:#c8e6c9; +@Green-200:#a5d6a7; +@Green-300:#81c784; +@Green-400:#66bb6a; +@Green-500:#4caf50; +@Green-600:#43a047; +@Green-700:#388e3c; +@Green-800:#2e7d32; +@Green-900:#1b5e20; +@Green-A100:#b9f6ca; +@Green-A200:#69f0ae; +@Green-A400:#00e676; +@Green-A700:#00c853; + +@LightGreen-50:#f1f8e9; +@LightGreen-100:#dcedc8; +@LightGreen-200:#c5e1a5; +@LightGreen-300:#aed581; +@LightGreen-400:#9ccc65; +@LightGreen-500:#8bc34a; +@LightGreen-600:#7cb342; +@LightGreen-700:#689f38; +@LightGreen-800:#558b2f; +@LightGreen-900:#33691e; +@LightGreen-A100:#ccff90; +@LightGreen-A200:#b2ff59; +@LightGreen-A400:#76ff03; +@LightGreen-A700:#64dd17; + +@Lime-50:#f9fbe7; +@Lime-100:#f0f4c3; +@Lime-200:#e6ee9c; +@Lime-300:#dce775; +@Lime-400:#d4e157; +@Lime-500:#cddc39; +@Lime-600:#c0ca33; +@Lime-700:#afb42b; +@Lime-800:#9e9d24; +@Lime-900:#827717; +@Lime-A100:#f4ff81; +@Lime-A200:#eeff41; +@Lime-A400:#c6ff00; +@Lime-A700:#aeea00; + +@Yellow-50:#fffde7; +@Yellow-100:#fff9c4; +@Yellow-200:#fff59d; +@Yellow-300:#fff176; +@Yellow-400:#ffee58; +@Yellow-500:#ffeb3b; +@Yellow-600:#fdd835; +@Yellow-700:#fbc02d; +@Yellow-800:#f9a825; +@Yellow-900:#f57f17; +@Yellow-A100:#ffff8d; +@Yellow-A200:#ffff00; +@Yellow-A400:#ffea00; +@Yellow-A700:#ffd600; + +@Amber-50:#fff8e1; +@Amber-100:#ffecb3; +@Amber-200:#ffe082; +@Amber-300:#ffd54f; +@Amber-400:#ffca28; +@Amber-500:#ffc107; +@Amber-600:#ffb300; +@Amber-700:#ffa000; +@Amber-800:#ff8f00; +@Amber-900:#ff6f00; +@Amber-A100:#ffe57f; +@Amber-A200:#ffd740; +@Amber-A400:#ffc400; +@Amber-A700:#ffab00; + +@Orange-50:#fff3e0; +@Orange-100:#ffe0b2; +@Orange-200:#ffcc80; +@Orange-300:#ffb74d; +@Orange-400:#ffa726; +@Orange-500:#ff9800; +@Orange-600:#fb8c00; +@Orange-700:#f57c00; +@Orange-800:#ef6c00; +@Orange-900:#e65100; +@Orange-A100:#ffd180; +@Orange-A200:#ffab40; +@Orange-A400:#ff9100; +@Orange-A700:#ff6d00; + +@DeepOrange-50:#fbe9e7; +@DeepOrange-100:#ffccbc; +@DeepOrange-200:#ffab91; +@DeepOrange-300:#ff8a65; +@DeepOrange-400:#ff7043; +@DeepOrange-500:#ff5722; +@DeepOrange-600:#f4511e; +@DeepOrange-700:#e64a19; +@DeepOrange-800:#d84315; +@DeepOrange-900:#bf360c; +@DeepOrange-A100:#ff9e80; +@DeepOrange-A200:#ff6e40; +@DeepOrange-A400:#ff3d00; +@DeepOrange-A700:#dd2c00; + +@Brown-50:#efebe9; +@Brown-100:#d7ccc8; +@Brown-200:#bcaaa4; +@Brown-300:#a1887f; +@Brown-400:#8d6e63; +@Brown-500:#795548; +@Brown-600:#6d4c41; +@Brown-700:#5d4037; +@Brown-800:#4e342e; +@Brown-900:#3e2723; + +@Grey-50:#fafafa; +@Grey-100:#f5f5f5; +@Grey-200:#eeeeee; +@Grey-300:#e0e0e0; +@Grey-400:#bdbdbd; +@Grey-500:#9e9e9e; +@Grey-600:#757575; +@Grey-700:#616161; +@Grey-800:#424242; +@Grey-900:#212121; + +@BlueGrey-50:#eceff1; +@BlueGrey-100:#cfd8dc; +@BlueGrey-200:#b0bec5; +@BlueGrey-300:#90a4ae; +@BlueGrey-400:#78909c; +@BlueGrey-500:#607d8b; +@BlueGrey-600:#546e7a; +@BlueGrey-700:#455a64; +@BlueGrey-800:#37474f; +@BlueGrey-900:#263238; \ No newline at end of file diff --git a/Website/wwwroot/css/site.less b/Website/wwwroot/css/site.less deleted file mode 100644 index 5f9679d..0000000 --- a/Website/wwwroot/css/site.less +++ /dev/null @@ -1,49 +0,0 @@ -html { - font-family: sans-serif; -} - -header { - display: flex; - align-items: flex-end; - justify-content: space-between; - flex-wrap: wrap; - background: red; - padding: 10px; - - #logo { - display: flex; - align-items: center; - font-size: 24px; - height: 50px; - color: inherit; - text-decoration: inherit; - - img { - height: 100%; - margin-right: 10px; - } - } - - nav { - margin-top: 10px; - margin-left: auto; - - ul { - list-style: none; - padding: 0; - margin: 0; - - li { - display: inline-block; - margin-left: 10px; - } - } - } -} - -main { - background: green; - margin: auto; - max-width: 800px; - padding: 20px; -} \ No newline at end of file diff --git a/Website/wwwroot/css/style-med.less b/Website/wwwroot/css/style-med.less new file mode 100644 index 0000000..d5e3131 --- /dev/null +++ b/Website/wwwroot/css/style-med.less @@ -0,0 +1,50 @@ +@import "colours.less"; + +body{ + display:initial; +} + +#menu{ + left:0; + position:fixed; + height:100%; + box-shadow:-10px 0px 5px 10px @Grey-900; + transition:all ease-in-out 0.4s; + + &.ng-hide{ + left:-280px; + } +} + +#main{ + .header{ + #menu-button{ + cursor: pointer; + display: inline-block; + max-height: 100%; + } + } +} + +#buttons-left, #buttons-right{ + button{ + width:60px; + height:60px; + } +} + +.backdrop{ + display: initial; +} + +.row{ + + >.col { + width:100%; + float:left; + } + + &.col-lg-1>.col, &.col-lg-2>.col, &.col-lg-3>.col, &.col-lg-4>.col{ + width: 100%; + } +} \ No newline at end of file diff --git a/Website/wwwroot/css/style-small.less b/Website/wwwroot/css/style-small.less new file mode 100644 index 0000000..40afee0 --- /dev/null +++ b/Website/wwwroot/css/style-small.less @@ -0,0 +1,47 @@ +dl{ + margin-top: 12px; + margin-bottom: 12px; +} + +#main{ + .header { + span:not(:last-child){ + display:none; + } + + >*{ + padding-left: 15px !important; + } + } +} + +#nav-container{ + top:48px; +} + +.header{ + height: 48px; + + > *{ + line-height: 48px; + font-size:18px; + } + span:not(:first-child):before{ + content:none; + } +} + +.row{ + >.col { + width:100%; + float:left; + } + + &.col-md-1>.col, &.col-md-2>.col, &.col-md-3>.col, &.col-md-4>.col{ + width: 100%; + } +} + +table{ + width:100%; +} \ No newline at end of file diff --git a/Website/wwwroot/css/style.less b/Website/wwwroot/css/style.less new file mode 100644 index 0000000..f9f8329 --- /dev/null +++ b/Website/wwwroot/css/style.less @@ -0,0 +1,426 @@ +@import "colours.less"; +@import "animations.less"; +@import "contextMenu.less"; + +@import url(//fonts.googleapis.com/css?family=Roboto); + +* { + box-sizing: border-box; + outline: none; + + transition: all 0.2s ease; +} + +html{ + width:100%; + height:100%; +} + +html,body,h1,h2, button { + padding:0; + margin:0; +} + +html, button, input, textarea{ + font-family: 'Roboto', sans-serif; +} + +body{ + width:100%; + height:100%; + background:@background; + display:flex; +} + +a { + color:@accent; +} + +nav { + img{ + vertical-align: middle; + margin-right: 5px; + } + + dt, dd{ + &:hover, &.active{ + background: @control; + cursor: pointer; + cursor: hand; + } + } + + dt { + padding: 10px 10px; + } + + dd { + margin: 0; + padding: 10px 10px 10px 40px; + } + + a{ + color: black; + text-decoration: none; + } +} + +h2{ + margin-bottom:10px; + a{ + color:inherit; + } +} + +h3{ + margin-bottom: 10px; + + &:first-child{ + margin-top: 0px; + } +} + +hr{ + border: 1px solid @control; +} + +table{ + border-collapse: collapse; + + input[type=text], textarea{ + width: 100%; + } +} + +th{ + text-align: left; +} + +td, th{ + padding:0 10px 5px 0; + vertical-align: top; +} + +button{ + border:none; + background:@control; +} + +form{ + button, input[type=submit]{ + border:none; + background:@control; + + &:hover{ + background: @control2; + } + } +} + +button:hover{ + cursor: pointer; + cursor: hand; +} + +img{ + max-width: 100%; +} + +p{ + &:first-child{ + margin-top: 0; + } + + &:last-child{ + margin-bottom:0; + } +} + +#menu{ + background:@background; + width:280px; + flex: 0 0 auto; + display:flex; + flex-direction: column; + box-shadow: -2px 0px 0px 0px @control inset; + z-index: 400; + overflow-x: hidden; + + #nav-container{ + overflow: auto; + width:100%; + flex:1 1 auto; + + dl { + margin: 5px 0 0 0; + } + } + + .header{ + z-index: 401; + flex: 0 0 auto; + overflow: auto; + } +} + +#main{ + flex: 1 1 auto; + display:flex; + flex-direction: column; + overflow:hidden; + height:100%; + position:relative; + + .header{ + flex: 0 0 auto; + z-index:1; + + #menu-button{ + display:none; + } + + >*{ + padding-left: 20px !important; + } + } + + #content{ + flex: 1 1 auto; + overflow:auto; + z-index:0; + height:100%; + display:flex; + flex-direction: column; + + #body{ + padding:20px; + //height:100%; + flex: 1; + } + } + +} + +#buttons-left,#buttons-right{ + position:absolute; + padding: 10px; + bottom: 0px; + + button{ + margin:10px; + border-radius: 50%; + display: inline-block; + text-align: center; + box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.25); + vertical-align: middle; + border:none; + background:@accent; + width:40px; + height:40px; + color: white; + font-size: 24px; + line-height: 24px; + } +} + +#buttons-left{ + left: 0px; +} + +#buttons-right{ + right: 0px; +} + +#cookiePopup{ + position:absolute; + background:black; + top:50%; + left:50%; + padding:20px; + border-radius: 30px; + width:230px; + height:230px; + margin-top:-115px; + margin-left:-115px; + color:white; + + > div{ + margin-top:40px; + text-align: center; + } +} + +#spinner{ + position:absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + align-items: center; + text-align: center; + display: flex; + z-index: 1000; + transition: all ease-in-out 0.5s; + + &.ng-hide{ + opacity: 0; + } + + >img{ + margin: auto; + } +} + +.scrollLock{ + overflow:hidden; +} + +.header { + height:64px; + background:@primary; + color:white; + white-space:nowrap; + padding:0 !important; + overflow: hidden; + box-shadow: 0 -9px 5px 10px rgba(50, 50, 50, 0.8); + + >*{ + line-height:64px; + font-size:24px; + font-weight:400; + padding-left: 10px; + white-space: nowrap; + display: inline; + vertical-align: middle; + } + + + >h2>span{ + display: inline-block; + } + + >img{ + height:50%; + } + + span{ + display: inline-block; + + &:not(:first-child):before{ + margin:2px 10px 0 10px; + width:6px; + float:left; + height:62px; + background: url("/images/chevron.png") no-repeat scroll right center / contain rgba(0, 0, 0, 0); + content:""; + } + } +} + +.circle{ + border-radius: 50%; + display: inline-block; + text-align: center; + vertical-align: middle; + overflow: hidden; +} + +.go, .expand{ + display:none; + float:right; +} + +.backdrop{ + background: rgba(0,0,0,0.5); + width: 100%; + height:100%; + position:absolute; + top:0; + left:0; + z-index:100; + display:none; + transition:all ease-in-out 0.5s; + + &.ng-hide{ + opacity: 0; + } +} + +.errors{ + background: url(/images/error.svg) no-repeat scroll 10px 10px @Grey-800; + padding:10px; + padding-left:40px; + color:#fff; + + ul{ + margin: 0; + list-style: square; + } +} + +.row{ + width:100%; + clear:both; + + >.col { + width:100%; + float:left; + } + + &.col-lg-1, &.col-md-1, &.col-sm-1{ + >.col{ + width:100%; + } + } + + &.col-lg-2, &.col-md-2, &.col-sm-2{ + >.col{ + width:50%; + } + } + + &.col-lg-3, &.col-md-3, &.col-sm-3{ + >.col{ + width:33.3333%; + } + } + + &.col-lg-4, &.col-md-4, &.col-sm-4{ + >.col{ + width:25%; + } + } +} + +table.alternatingRows{ + tr{ + &:nth-child(1n){ + background:@control; + } + + &:nth-child(2n){ + background:@control2; + } + } +} + +td, th{ + padding:3px; + + &.number{ + text-align: right; + } +} + +div.percentageBar{ + width:100%; + height:20px; + background:@LightGreen-400; + overflow:hidden; + + >div{ + background:@Red-400; + height:100%; + transition: all 500ms ease-in-out; + } +} \ No newline at end of file diff --git a/Website/wwwroot/css/temperature.less b/Website/wwwroot/css/temperature.less new file mode 100644 index 0000000..92e4f25 --- /dev/null +++ b/Website/wwwroot/css/temperature.less @@ -0,0 +1,42 @@ +@import "colours.less"; + +@gaugeHeight:200px; +@gaugeWidth:200px; + +.currentTemp{ + background:@Red-100; + height:@gaugeHeight; + width:@gaugeWidth; + position:relative; + //border:1px solid @Grey-900; + display:inline-block; + + .background{ + position: absolute; + bottom: 0; + left:0; + right:0; + top: 20%; + transition: all 500ms ease-in-out; + } + + .reading{ + position: relative; + height:@gaugeHeight; + width:@gaugeWidth; + vertical-align:middle; + display:table-cell; + text-align:center; + font-size: 250%; + } + + .shadow{ + box-shadow: inset 0px 0px 5px 1px rgba(0,0,0,0.75); + border:2px solid @Grey-900; + position: absolute; + bottom: 0; + left:0; + right:0; + top: 0; + } +} \ No newline at end of file diff --git a/Website/wwwroot/images/add.svg b/Website/wwwroot/images/add.svg new file mode 100644 index 0000000..c6a72d7 --- /dev/null +++ b/Website/wwwroot/images/add.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/add_album.svg b/Website/wwwroot/images/add_album.svg new file mode 100644 index 0000000..82ac4db --- /dev/null +++ b/Website/wwwroot/images/add_album.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/Website/wwwroot/images/blog.svg b/Website/wwwroot/images/blog.svg new file mode 100644 index 0000000..162db3e --- /dev/null +++ b/Website/wwwroot/images/blog.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/Website/wwwroot/images/chevron.png b/Website/wwwroot/images/chevron.png new file mode 100644 index 0000000000000000000000000000000000000000..a571c6e43183a071776219e65d0ae3eccd43dca1 GIT binary patch literal 49154 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
    m001AfE1?d8102B4J2DG5`vO(go=PWdIZmr3=yp z$^a-BN*AOHlmSriAErzG>vRtq3z*{}048(5Gcy*J3=gv>SQ6Ee9mT*CK=yBp>?>N8 zXL&v1!B({uc@7*BtNyPMv$=26<>bBB>5WdW?p7H7MQ3tt?VjFK+Bs)Fg{y2aQllHx z{$u{o`5kh4@9I?DSHlNe19nI8WWocbJyl4;lrl$URpCa7^z4rAbG$e&W}vR?racp} e8cu3MaEp~C1G^FqzlmO$lVZQyiI8t~B;q&a@8e7W literal 0 HcmV?d00001 diff --git a/Website/wwwroot/images/delete.svg b/Website/wwwroot/images/delete.svg new file mode 100644 index 0000000..e1b851b --- /dev/null +++ b/Website/wwwroot/images/delete.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/Website/wwwroot/images/desktop.svg b/Website/wwwroot/images/desktop.svg new file mode 100644 index 0000000..e6068a1 --- /dev/null +++ b/Website/wwwroot/images/desktop.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/done.svg b/Website/wwwroot/images/done.svg new file mode 100644 index 0000000..6d0b534 --- /dev/null +++ b/Website/wwwroot/images/done.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/Website/wwwroot/images/edit.svg b/Website/wwwroot/images/edit.svg new file mode 100644 index 0000000..8c9d966 --- /dev/null +++ b/Website/wwwroot/images/edit.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/Website/wwwroot/images/error.svg b/Website/wwwroot/images/error.svg new file mode 100644 index 0000000..773c155 --- /dev/null +++ b/Website/wwwroot/images/error.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/gallery.svg b/Website/wwwroot/images/gallery.svg new file mode 100644 index 0000000..0d2a14b --- /dev/null +++ b/Website/wwwroot/images/gallery.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/Website/wwwroot/images/home.svg b/Website/wwwroot/images/home.svg new file mode 100644 index 0000000..bbe8127 --- /dev/null +++ b/Website/wwwroot/images/home.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/library.svg b/Website/wwwroot/images/library.svg new file mode 100644 index 0000000..b1d4b81 --- /dev/null +++ b/Website/wwwroot/images/library.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/Website/wwwroot/images/logo_email.png b/Website/wwwroot/images/logo_email.png new file mode 100644 index 0000000000000000000000000000000000000000..0e0540178004d47e1e0544ca0f7202a83d5c9eb8 GIT binary patch literal 4770 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 literal 0 HcmV?d00001 diff --git a/Website/wwwroot/images/member.svg b/Website/wwwroot/images/member.svg new file mode 100644 index 0000000..f4e6b78 --- /dev/null +++ b/Website/wwwroot/images/member.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/menu.svg b/Website/wwwroot/images/menu.svg new file mode 100644 index 0000000..3456db0 --- /dev/null +++ b/Website/wwwroot/images/menu.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/next.svg b/Website/wwwroot/images/next.svg new file mode 100644 index 0000000..6467ee4 --- /dev/null +++ b/Website/wwwroot/images/next.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/Website/wwwroot/images/photo.svg b/Website/wwwroot/images/photo.svg new file mode 100644 index 0000000..667b4b7 --- /dev/null +++ b/Website/wwwroot/images/photo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/Website/wwwroot/images/power.svg b/Website/wwwroot/images/power.svg new file mode 100644 index 0000000..f6b8cdf --- /dev/null +++ b/Website/wwwroot/images/power.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/Website/wwwroot/images/previous.svg b/Website/wwwroot/images/previous.svg new file mode 100644 index 0000000..dc1ea85 --- /dev/null +++ b/Website/wwwroot/images/previous.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/Website/wwwroot/images/projects.svg b/Website/wwwroot/images/projects.svg new file mode 100644 index 0000000..ef1dbd7 --- /dev/null +++ b/Website/wwwroot/images/projects.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/Website/wwwroot/images/register.svg b/Website/wwwroot/images/register.svg new file mode 100644 index 0000000..296969f --- /dev/null +++ b/Website/wwwroot/images/register.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/Website/wwwroot/images/save.svg b/Website/wwwroot/images/save.svg new file mode 100644 index 0000000..e08fc49 --- /dev/null +++ b/Website/wwwroot/images/save.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/Website/wwwroot/images/send.svg b/Website/wwwroot/images/send.svg new file mode 100644 index 0000000..018c599 --- /dev/null +++ b/Website/wwwroot/images/send.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/Website/wwwroot/images/spinner.svg b/Website/wwwroot/images/spinner.svg new file mode 100644 index 0000000..a04d5c4 --- /dev/null +++ b/Website/wwwroot/images/spinner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Website/wwwroot/images/status.svg b/Website/wwwroot/images/status.svg new file mode 100644 index 0000000..849bbef --- /dev/null +++ b/Website/wwwroot/images/status.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/Website/wwwroot/images/temperature.svg b/Website/wwwroot/images/temperature.svg new file mode 100644 index 0000000..bbe8127 --- /dev/null +++ b/Website/wwwroot/images/temperature.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/Website/wwwroot/images/upload.svg b/Website/wwwroot/images/upload.svg new file mode 100644 index 0000000..2ea3267 --- /dev/null +++ b/Website/wwwroot/images/upload.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/Website/wwwroot/images/weight.svg b/Website/wwwroot/images/weight.svg new file mode 100644 index 0000000..2837ea3 --- /dev/null +++ b/Website/wwwroot/images/weight.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/Website/wwwroot/js/controllers/gallery.js b/Website/wwwroot/js/controllers/gallery.js new file mode 100644 index 0000000..145802a --- /dev/null +++ b/Website/wwwroot/js/controllers/gallery.js @@ -0,0 +1,12 @@ +angular.module("robware").controller('gallery', ["$scope", function($scope) { + $scope.images=[]; + $scope.imageToShow=0; + + $scope.showNextImage=function(){ + $scope.imageToShow=Math.min($scope.imageToShow+1, $scope.images.length-1); + }; + + $scope.showPreviousImage=function(){ + $scope.imageToShow=Math.max($scope.imageToShow-1, 0); + }; +}]); \ No newline at end of file diff --git a/Website/wwwroot/js/controllers/galleryManage.js b/Website/wwwroot/js/controllers/galleryManage.js new file mode 100644 index 0000000..7d41312 --- /dev/null +++ b/Website/wwwroot/js/controllers/galleryManage.js @@ -0,0 +1,77 @@ +angular.module("robware").controller('galleryManage', ["$scope", "$http", function($scope, $http) { + $scope.albums=[]; + var albumActions={}; + + var moveIndex="Move selected to album"; + $scope.contextMenuActions={ + "Delete selected":deleteSelected + }; + + function getSelectedImageIds(){ + var images=[]; + angular.forEach($scope.selectedAlbum.Images, function(image){ + if (image.selected) + images.push(image.ImageId); + }); + return images; + } + + function updateAlbums(data){ + var selectedId=$scope.selectedAlbum.AlbumId; + $scope.albums=data; + angular.forEach($scope.albums, function(album){ + if (album.AlbumId===selectedId) + $scope.selectedAlbum=album; + }); + } + + function deleteSelected(){ + var images=getSelectedImageIds(); + if (images.length===0 || !confirm("Are you sure you want to delete?")) + return; + + var data={ + selectedImages:images + }; + + $http.post("/gallery/deleteimages",data).then(function(response){ + updateAlbums(response.data); + }); + }; + + function moveImages(newAlbum){ + var images=getSelectedImageIds(); + if (images.length===0) + return; + + var data={ + selectedImages:images, + targetAlbumId:newAlbum.AlbumId + }; + $http.post("/gallery/move",data).then(function(response){ + updateAlbums(response.data); + }); + } + + $scope.$watch("albums", function(){ + if ($scope.selectedAlbum===undefined) + $scope.selectedAlbum=$scope.albums[0]; + + albumActions={}; + angular.forEach($scope.albums, function(album){ + albumActions[album.AlbumTitle]=function(){ + moveImages(album); + }; + }); + $scope.contextMenuActions[moveIndex]=albumActions; + }); + + $scope.selectImage=function(image){ + image.selected=!image.selected; + }; + + $scope.editAlbum=function(album){ + Navigate("/gallery/editablum/"+album.AlbumId); + }; +}]); + diff --git a/Website/wwwroot/js/controllers/galleryUpload.js b/Website/wwwroot/js/controllers/galleryUpload.js new file mode 100644 index 0000000..1157286 --- /dev/null +++ b/Website/wwwroot/js/controllers/galleryUpload.js @@ -0,0 +1,101 @@ +angular.module("robware").controller('galleryUpload', ["$scope", "$http", function($scope, $http) { + $scope.images = []; + $scope.contextMenuActions = { + Remove: remove, + Clear: clear + }; + + function remove(containerScope) { + if (!containerScope.image) + return; + + var index = $scope.images.indexOf(containerScope.image); + if (index !== -1) + $scope.images.splice(index, 1); + } + + function clear() { + if (confirm("Are you sure you want to clear the uploads?")) + $scope.images = []; + } + + function removeImages(toRemove) { + // I need this roundabout way to work with Angular's view engine + var imageReferences = []; + angular.forEach(toRemove, function(index) { + imageReferences.push($scope.images[index]); + }); + angular.forEach(imageReferences, function(ref) { + $scope.images.splice($scope.images.indexOf(ref), 1); + }); + } + + $scope.getTotalImageSize = function() { + var totalSize = 0; + angular.forEach($scope.images, function(image) { + totalSize += image.file.size; + }); + return totalSize; + } + + $scope.safeUploadSize = function() { + return $scope.maxUploadSize * 0.95; + } + + $scope.fileDrop = function(files) { + angular.forEach(files, function(file) { + var imageObject = { + file: file, + title: file.name.replace(/\.[^/.]+$/, ""), + description: "", + preview: "" + }; + + var reader = new FileReader(); + reader.onload = function(e) { + imageObject.preview = e.target.result; + $scope.$apply(); + }; + reader.readAsDataURL(file); + + $scope.images.push(imageObject); + }); + }; + + $scope.upload = function() { + var data = new FormData(); + var errors = []; + angular.forEach($scope.images, function(image) { + if (!image.title) + errors.push("The image " + image.file.name + " doesn't have a title"); + data.append("files[]", image.file); + data.append("title[]", image.title); + data.append("description[]", image.description); + }); + + var totalSize = 0; + data.getAll("files[]").forEach(function(entry){ + totalSize += entry.size; + }); + if (totalSize>$scope.safeUploadSize()) + errors.push("Max upload size exceeded"); + + if (errors.length>0){ + $scope.showErrors(errors); + return; + } + + $http({ + method: 'POST', + url: '/gallery/uploadimages', + data: data, + showSpinner: true, + headers: {'Content-Type': undefined}, + transformRequest: angular.identity + }).then(function(response) { + $scope.showErrors(response.data.errors); + removeImages(response.data.uploaded); + }); + }; +}]); + diff --git a/Website/wwwroot/js/controllers/main.js b/Website/wwwroot/js/controllers/main.js new file mode 100644 index 0000000..46a9563 --- /dev/null +++ b/Website/wwwroot/js/controllers/main.js @@ -0,0 +1,107 @@ +/* global app, angular */ + +angular.module("robware", ['ngAnimate'], ["$httpProvider", function($httpProvider) { + /* + * Stolen from: http://stackoverflow.com/questions/19254029/angularjs-http-post-does-not-send-data + * Reason: send payload as form data instead of JSON, bind to controller action parameters + */ + // Use x-www-form-urlencoded Content-Type + $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; + /** + * The workhorse; converts an object to x-www-form-urlencoded serialization. + * @param {Object} obj + * @return {String} + */ + var param = function(obj) { + var query = '', name, value, fullSubName, subName, subValue, innerObj, i; + for (name in obj) { + value = obj[name]; + if (value instanceof Array) { + for (i = 0; i < value.length; ++i) { + subValue = value[i]; + fullSubName = name + '[' + i + ']'; + innerObj = {}; + innerObj[fullSubName] = subValue; + query += param(innerObj) + '&'; + } + } + else if (value instanceof Object) { + for (subName in value) { + subValue = value[subName]; + fullSubName = name + '[' + subName + ']'; + innerObj = {}; + innerObj[fullSubName] = subValue; + query += param(innerObj) + '&'; + } + } + else if (value !== undefined && value !== null) + query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&'; + } + + return query.length ? query.substr(0, query.length - 1) : query; + }; + // Override $http service's default transformRequest + $httpProvider.defaults.transformRequest = [ + function(data) { + return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data; + } + ]; + + $httpProvider.interceptors.push([ + '$rootScope', + '$q', + function($rootScope, $q) { + return { + request: function(config) { + if (config.showSpinner) + $rootScope.showSpinner(); + return config; + }, + requestError: function(rejection) { + $rootScope.hideSpinner(); + return $q.reject(rejection); + }, + response: function(response) { + $rootScope.hideSpinner(); + return response; + }, + responseError: function(rejection) { + $rootScope.hideSpinner(); + return $q.reject(rejection); + } + }; + } + ]); +}]); + +angular.module("robware").controller("main", ['$scope', '$rootScope', function ($scope, $rootScope) { + console.log(123); + var largeWindowBoundary = 1023; + + $scope.menuVisible = false;//window.innerWidth > largeWindowBoundary; + + $scope.shouldShowMenu = function() { + return $scope.menuVisible || window.innerWidth > largeWindowBoundary; + }; + + $scope.spinnerVisible = false; + + $rootScope.showSpinner = function() { + $scope.spinnerVisible = true; + }; + + $rootScope.hideSpinner = function() { + $scope.spinnerVisible = false; + }; + + $(window).on("resize.doResize", function() { + $scope.$apply(function() { + //$scope.menuVisible = false; + }); + }); + + $scope.errors=[]; + $scope.showErrors=function(errors){ + $scope.errors=errors; + } + }]); \ No newline at end of file diff --git a/Website/wwwroot/js/controllers/status.js b/Website/wwwroot/js/controllers/status.js new file mode 100644 index 0000000..1aa0b59 --- /dev/null +++ b/Website/wwwroot/js/controllers/status.js @@ -0,0 +1,65 @@ +angular.module("robware").controller('status', ["$scope", "statusService", "$interval", function($scope, statusService, $interval) { + $scope.chartData=[]; + var chartDataId=0; + + function reloadUptime() { + statusService.getUptime().then(function(data) { + $scope.uptime = data; + $scope.haveUptime = true; + }); + } + $scope.haveUptime = false; + $scope.uptime = {}; + reloadUptime(); + $interval(reloadUptime, 60000); + + function reloadSystemInfo() { + statusService.getSystemInfo().then(function(data) { + if ($scope.chartData.length>=50) + $scope.chartData.shift(); + $scope.chartData=$scope.chartData.concat([{id: chartDataId, CPU: data.load[0]*100, Mem: data.memUsage, Temp: data.cpuTemp}]); + chartDataId++; + $scope.load = data.load; + $scope.cpuTemp = data.cpuTemp; + $scope.memory = { + free: data.memFree, + used: data.memUsage, + total: data.memTotal + }; + $scope.haveSystemInfo = true; + }); + } + $scope.haveSystemInfo = false; + $scope.load = {}; + $scope.loadHistory = []; + $scope.cpuTemp = 0; + $scope.systemInfoRefreshInterval = 5; + var systemRefresh = $interval(reloadSystemInfo, $scope.systemInfoRefreshInterval * 1000); + reloadSystemInfo(); + $scope.changeSystemInfoRefreshInterval = function(amount) { + $scope.systemInfoRefreshInterval += amount; + if ($scope.systemInfoRefreshInterval < 1) + return; + $interval.cancel(systemRefresh); + systemRefresh = $interval(reloadSystemInfo, $scope.systemInfoRefreshInterval * 1000); + }; + + function reloadProcesses() { + statusService.getProcesses().then(function(data) { + $scope.processes = data; + $scope.haveProcesses = true; + }); + } + $scope.haveProcesses = false; + $scope.processses = {}; + $scope.processRefreshInterval = 10; + var processRefesh = $interval(reloadProcesses, $scope.processRefreshInterval * 1000); + reloadProcesses(); + $scope.changeProcesRefreshInterval = function(amount) { + $scope.processRefreshInterval += amount; + if ($scope.processRefreshInterval < 1) + return; + $interval.cancel(processRefesh); + processRefesh = $interval(reloadProcesses, $scope.processRefreshInterval * 1000); + }; +}]); \ No newline at end of file diff --git a/Website/wwwroot/js/controllers/temperature.js b/Website/wwwroot/js/controllers/temperature.js new file mode 100644 index 0000000..3810c09 --- /dev/null +++ b/Website/wwwroot/js/controllers/temperature.js @@ -0,0 +1,56 @@ +angular.module("robware").controller('temperature', ["$scope", "temperatureService", "$interval", function($scope, temperatureService, $interval) { + $scope.readings = []; + $scope.temperatureData = {}; + + $scope.$watch("readings", function() { + for (var i = 0; i < $scope.readings.length; i++) { + $scope.readings[i].Timestamp = new Date($scope.readings[i].Timestamp * 1000); + } + }); + + function getData() { + temperatureService.getReadings().then(function(data) { + $scope.readings = data; + }); + } + $interval(getData, 60000); + + function updateCurrentTemperature() { + temperatureService.getCurrentTemperatureData().then(function(data) { + $scope.temperatureData = data; + }); + } + $interval(updateCurrentTemperature, 5000); + + var graphAdjustment = 10; + + $scope.getGraphPercentage = function() { + var graphTarget = ($scope.temperatureData.target * 1.2) - graphAdjustment; + var graphTemperature = $scope.temperatureData.temperature - graphAdjustment; + return Math.min((graphTemperature / graphTarget) * 100, 100); + }; + + function getPercentage() { + return (($scope.temperatureData.target - graphAdjustment) / (($scope.temperatureData.temperature * 1.5) - graphAdjustment)) * 100; + } + + $scope.getRed = function() { + var percentage = getPercentage(); + return 255 * (1 - (Math.min(50, percentage) / 50)); + }; + + $scope.getBlue = function() { + var percentage = Math.max(getPercentage() - 50, 0); + return 255 * (Math.min(50, percentage) / 50); + }; + + $scope.getGreen = function() { + var percentage = getPercentage(); + percentage = Math.min(percentage, 75); + percentage = Math.max(percentage, 25); + percentage = Math.abs(50 - percentage); + percentage /= 25; + return 255 * (1 - Math.max(percentage, 0)); + }; +}]); + diff --git a/Website/wwwroot/js/controllers/weight.js b/Website/wwwroot/js/controllers/weight.js new file mode 100644 index 0000000..f958932 --- /dev/null +++ b/Website/wwwroot/js/controllers/weight.js @@ -0,0 +1,48 @@ +angular.module("robware").controller("weight", ["$scope", "weightService", function($scope, weightService) { + $scope.submitting = false; + $scope.weight = null; + $scope.fat = null; + + $scope.readings = []; + $scope.headings = {weight: "Weight (KG)", fat: "Fat %", bmi: "BMI"}; + + function refreshReadings() { + weightService.getReadings().then(function(data) { + $scope.readings = data; + }); + } + + $scope.$watch("readings", function() { + for (var i = 0; i < $scope.readings.length; i++) { + $scope.readings[i].date = new Date($scope.readings[i].date); + } + }); + + $scope.addReading = function() { + if (!$scope.weight || !$scope.fat) + return; + + $scope.submitting = true; + weightService.addWeight($scope.weight, $scope.fat).then(function(data) { + refreshReadings(); + $scope.submitting = false; + $scope.weight = null; + $scope.fat = null; + }); + }; + + $scope.deleteReading = function(id) { + weightService.deleteWeight(id).then(function() { + refreshReadings(); + }); + }; + + Object.defineProperty($scope, "tenLatestReadings", { + get: function() { + var temp = $scope.readings.slice(-10); + temp.reverse(); + return temp; + } + }); + }]); + diff --git a/Website/wwwroot/js/directives/contextMenu.js b/Website/wwwroot/js/directives/contextMenu.js new file mode 100644 index 0000000..7a17695 --- /dev/null +++ b/Website/wwwroot/js/directives/contextMenu.js @@ -0,0 +1,58 @@ +angular.module("robware").directive('contextMenu', function() { + return { + restrict: 'E', + templateUrl: '/scripts/directives/templates/contextMenu.html', + scope: { + actions: '=', + isChild: '=?' + }, + link: function(scope, element) { + scope.isFunction = angular.isFunction; + scope.show=false; + scope.selectedElement=null; + scope.subMenuShowKey=""; + + scope.changeSubMenuShowKey=function(name){ + scope.subMenuShowKey=name; + }; + + function show(x, y, target){ + scope.changeSubMenuShowKey(""); + scope.menuX=x+"px"; + scope.menuY=y+"px"; + scope.selectedElement=target; + scope.show=true; + }; + + function hide(){ + scope.show=false; + } + + scope.performAction=function(func){ + if (!angular.isFunction(func)) + return; + func($(scope.selectedElement).scope()); + hide(); + }; + + $(document).on("click", function(e){ + hide(); + scope.$apply(); + }); + + var parent=element.parent()[0]; + + if (scope.isChild){ + show(parent.clientWidth,parent.clientTop); + }else + $(parent).css("position","relative"); + element.parent().on("contextmenu", function(e){ + var offset=$(element).offset(); + show(e.pageX-offset.left, e.pageY-offset.top, e.target); + scope.$apply(); + e.preventDefault(); + }); + } + }; +}); + diff --git a/Website/wwwroot/js/directives/dragDrop.js b/Website/wwwroot/js/directives/dragDrop.js new file mode 100644 index 0000000..2e32e61 --- /dev/null +++ b/Website/wwwroot/js/directives/dragDrop.js @@ -0,0 +1,38 @@ +angular.module("robware").directive('dragDrop', function() { + return { + restrict: 'A', + scope: { + dragDrop:'=' + }, + link: function(scope, element) { + function handleEvent(e){ + e.preventDefault(); + e.stopPropagation(); + } + + element.on('dragover', function(e) { + handleEvent(e); + }); + element.on('dragenter', function(e) { + handleEvent(e); + $(element).addClass("dragOver"); + }); + element.on('dragleave', function(e) { + handleEvent(e); + $(element).removeClass("dragOver"); + }); + element.on('drop', function(e){ + handleEvent(e); + $(element).removeClass("dragOver"); + + if (e.originalEvent.dataTransfer){ + if (e.originalEvent.dataTransfer.files.length > 0) { + scope.dragDrop(e.originalEvent.dataTransfer.files); + scope.$apply(); + } + } + }); + } + }; +}); + diff --git a/Website/wwwroot/js/directives/equalHeightWidth.js b/Website/wwwroot/js/directives/equalHeightWidth.js new file mode 100644 index 0000000..f654619 --- /dev/null +++ b/Website/wwwroot/js/directives/equalHeightWidth.js @@ -0,0 +1,14 @@ +angular.module("robware").directive("equalWidth", function() { + return { + restrict: 'A', + link: function(scope, element) { + scope.getHeight = function() { + return $(element).height(); + }; + scope.$watch(scope.getHeight, function(height) { + $(element).width(height); + }); + } + } +}); + diff --git a/Website/wwwroot/js/directives/googleChart.js b/Website/wwwroot/js/directives/googleChart.js new file mode 100644 index 0000000..47e9b95 --- /dev/null +++ b/Website/wwwroot/js/directives/googleChart.js @@ -0,0 +1,122 @@ +/* global google, angular */ + +angular.module("robware").directive('googleChart', function() { + return { + restrict: 'E', + templateUrl: '/scripts/directives/templates/googleChart.html', + scope: { + data: '=', + headings: '=?', + ignore: '=?', + height: '=' + }, + link: function(scope, element, attributes) { + var chartLoaded = false; + + if (!scope.headings) + scope.headings = []; + + if (!scope.ignore) + scope.ignore = []; + + var chartData, chart; + var chartOptions = { + title: attributes.title, + height: scope.height || 400, + curveType: attributes.curveType, + legend: { + position: attributes.legendPosition || 'right' + }, + vAxis: { + title: attributes.vAxisTitle + }, + backgroundColor: attributes.background || 'transparent' + }; + + function formatToolTip(input, args) { + return input.replace(/{(.+?)(:.*?)?}/g, function(match, index, format) { + if (format) + format = format.slice(1); + if (args[index]===undefined) + return match; + if (isNumber(args[index])) + return args[index].toString(format, true); + return args[index].toString(format); + }); + } + + function shouldIgnore(value){ + return scope.ignore.indexOf(value)>-1; + } + + function setupChart() { + if (!chartLoaded) + return; + + var firstRow = scope.data[0]; + var fields = {}; + for (var propertyName in firstRow) { + if (shouldIgnore(propertyName)) + continue; + + var datum = firstRow[propertyName]; + var type = typeof (datum); + if (type === "object") + type = datum.constructor.name; + fields[propertyName] = { + title: scope.headings[propertyName] || propertyName, + type: type.toLowerCase() + }; + } + + chartData = new google.visualization.DataTable(); + angular.forEach(fields, function(value) { + chartData.addColumn(value.type, value.title); + if (attributes['toolTip' + value.title.upperCaseFirst()]) + chartData.addColumn({type: 'string', role: 'tooltip'}); + }); + + angular.forEach(scope.data, function(value) { + var row = []; + angular.forEach(value, function(value2, index) { + if (shouldIgnore(index)) + return; + + row.push(value2); + var attrIndex = 'toolTip' + index.upperCaseFirst(); + if (attributes[attrIndex]) + row.push(formatToolTip(attributes[attrIndex], value)); + }); + chartData.addRow(row); + }); + + chart = new google.visualization.LineChart(element[0]); + drawChart(); + } + + function drawChart() { + chart.draw(chartData, chartOptions); + } + + $.getScript("https://www.google.com/jsapi", function() { + //google.load('visualization', '1.1', {packages: ['line'], callback:function(){ + google.load('visualization', '1', { + packages: ['corechart'], + callback: function() { + chartLoaded = true; + setupChart(); + } + }); + }); + + scope.$watch("data", function(v) { + setupChart(); + }); + + angular.element(window).bind('resize', function() { + drawChart(); + }); + } + }; +}); + diff --git a/Website/wwwroot/js/directives/scopeInit.js b/Website/wwwroot/js/directives/scopeInit.js new file mode 100644 index 0000000..cdf83d2 --- /dev/null +++ b/Website/wwwroot/js/directives/scopeInit.js @@ -0,0 +1,11 @@ +angular.module("robware").directive("scopeInit", function(){ + return { + restrict : 'E', + link:function(scope, element, attributes){ + var content=element[0].innerHTML.trim(); + scope[attributes.value]=JSON.parse(content); + element.remove(); + } + } +}); + diff --git a/Website/wwwroot/js/directives/templates/contextMenu.html b/Website/wwwroot/js/directives/templates/contextMenu.html new file mode 100644 index 0000000..f7cb523 --- /dev/null +++ b/Website/wwwroot/js/directives/templates/contextMenu.html @@ -0,0 +1,6 @@ +
    + +
    \ No newline at end of file diff --git a/Website/wwwroot/js/directives/templates/googleChart.html b/Website/wwwroot/js/directives/templates/googleChart.html new file mode 100644 index 0000000..3521b88 --- /dev/null +++ b/Website/wwwroot/js/directives/templates/googleChart.html @@ -0,0 +1 @@ +
    Loading chart...
    \ No newline at end of file diff --git a/Website/wwwroot/js/javascript.js b/Website/wwwroot/js/javascript.js new file mode 100644 index 0000000..2ce34f5 --- /dev/null +++ b/Website/wwwroot/js/javascript.js @@ -0,0 +1,145 @@ +function Navigate(url) { + window.location = url; +} + +function CreateCookie(name, value, days) { + var expires; + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } + else { + expires = ""; + } + document.cookie = name + "=" + value + expires + "; path=/"; +} + +function round(value, decimals) { + return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); +} + +function isNumber(value){ + return isFinite(String(value)); +} + +String.prototype.upperCaseFirst = function() { + var lower = this.toLowerCase(); + return lower.charAt(0).toUpperCase() + lower.slice(1); +}; + +function twoDigitZeroPad(input){ + input=input.toString(); + return [!input[1] && '0' || "", input].join(''); +} + +Date.prototype.oldToString = Date.prototype.toString; +Date.prototype.toString = function(format) { + if (!format) + return this.oldToString(); + var me=this; + return format.replace(/./g, function(match) { + switch(match){ + case 'd': return twoDigitZeroPad(me.getDate()); + case 'm': return twoDigitZeroPad(me.getMonth()+1); + case 'y': return me.getFullYear(); + case "H": return twoDigitZeroPad(me.getHours()); + case "i": return twoDigitZeroPad(me.getMinutes()); + case "s": return twoDigitZeroPad(me.getSeconds()); + default: return match; + } + }); +}; + +Number.prototype.oldToString=Number.prototype.toString; +Number.prototype.toString=function(input, shouldRound){ + if (!shouldRound) + return this.oldToString(input); + + return round(this, input); +}; + +$(function() { + var panels = $("nav > dl > .sub-pages"); + var goIndicators = $("nav > dl > dt .go"); + var expandIndicators = $("nav > dl > dt .expand"); + panels.hide(); + expandIndicators.show(); + + $("nav > dl > dt").click(function(e) { + var next = $(this).next(); + if (!next.hasClass("sub-pages")) { + Navigate($(this).children("a").attr("href")); + return; + } + panels.slideUp(); + goIndicators.hide(); + expandIndicators.show(); + if (next.is(":visible")) { + return; + } + next.slideDown(); + $(this).children(".go").show(); + $(this).children(".expand").hide(); + }); + + $("nav dd").click(function(e) { + Navigate($(this).children("a").attr("href")); + }); + + $("nav a").click(function(e) { + $(this).parent().click(); + return false; + }); + + $("#body").css("padding-bottom", window.innerHeight - $("#buttons-right").offset().top); + + $("form[ajaxForm]").submit(function(e) { + e.preventDefault(); + form = $(this); + $.ajax({ + url: form.attr("action"), + method: form.attr("method"), + data: form.serialize(), + success: function(data) { + var successFunction = form.attr("onsuccess"); + if (successFunction !== undefined && window[successFunction] !== undefined) + window[successFunction](data); + }, + error: function() { + var errorFunction = form.attr("onerror"); + if (errorFunction !== undefined && window[errorFunction] !== undefined) + window[errorFunction](); + }, + }); + var postFunction = form.attr("onpost"); + if (postFunction !== undefined && window[postFunction] !== undefined) + window[postFunction](); + }); + + $("td").each(function() { + var elem = $(this); + if (/^[+-]?\d+(\.\d+)?$/.test(elem.text())) { + elem.addClass("number"); + } + }); +}); + +$(document).delegate('.allowTabInput', 'keydown', function(e) { + var keyCode = e.keyCode || e.which; + console.log(e); + if (keyCode == 9) { + e.preventDefault(); + var start = $(this).get(0).selectionStart; + var end = $(this).get(0).selectionEnd; + + // set textarea value to: text before caret + tab + text after caret + $(this).val($(this).val().substring(0, start) + + "\t" + + $(this).val().substring(end)); + + // put caret at right position again + $(this).get(0).selectionStart = + $(this).get(0).selectionEnd = start + 1; + } +}); \ No newline at end of file diff --git a/Website/wwwroot/js/services/statusService.js b/Website/wwwroot/js/services/statusService.js new file mode 100644 index 0000000..d5bcc32 --- /dev/null +++ b/Website/wwwroot/js/services/statusService.js @@ -0,0 +1,60 @@ +angular.module("robware").service('statusService', ["$http", function($http) { + function parseProcessData(data) { + var headers = data.shift(); + var cpuIndex = -1; + var ignoreColNames = ["user", "pid", "vsz", "rss", "tty", "stat"]; + var ignoreCols = []; + + var processHeaders = [] + for (var col in headers) { + if (headers[col].toLowerCase() === "%cpu") + cpuIndex = col; + + if (ignoreColNames.indexOf(headers[col].toLowerCase()) > -1) + ignoreCols.push(col); + else + processHeaders.push(headers[col]); + } + + data.sort(function (a, b) { + cpuA = parseFloat(a[cpuIndex]); + cpuB = parseFloat(b[cpuIndex]); + if (cpuA > cpuB) + return -1; + if (cpuA < cpuB) + return 1 + return 0; + }); + + var processData = []; + for (var row in data) { + var obj = {}; + for (var col in data[row]) { + if (ignoreCols.indexOf(col) === -1) + obj[headers[col]] = data[row][col]; + } + processData.push(obj); + } + + return [processHeaders, processData]; + } + + this.getProcesses = function() { + return $http.get('/status/getprocesses').then(function(response) { + parsedData = parseProcessData(response.data); + return {headers: parsedData[0], data: parsedData[1]}; + }); + }; + + this.getSystemInfo=function(){ + return $http.get('/status/getsysteminfo').then(function(response){ + return response.data; + }); + }; + + this.getUptime=function(){ + return $http.get('/status/getuptime').then(function(response){ + return response.data; + }) + }; +}]); \ No newline at end of file diff --git a/Website/wwwroot/js/services/temperatureService.js b/Website/wwwroot/js/services/temperatureService.js new file mode 100644 index 0000000..5f5d2a0 --- /dev/null +++ b/Website/wwwroot/js/services/temperatureService.js @@ -0,0 +1,13 @@ +angular.module("robware").service('temperatureService', ["$http", function($http) { + this.getReadings = function() { + return $http.get('/temperature/getreadings', {dontShowSpinner:true}).then(function(response) { + return response.data; + }); + }; + + this.getCurrentTemperatureData = function() { + return $http.get("/temperature/GetTemperatureData", {dontShowSpinner:true}).then(function(response) { + return response.data; + }); + } +}]); \ No newline at end of file diff --git a/Website/wwwroot/js/services/weightService.js b/Website/wwwroot/js/services/weightService.js new file mode 100644 index 0000000..27d810b --- /dev/null +++ b/Website/wwwroot/js/services/weightService.js @@ -0,0 +1,31 @@ +angular.module("robware").service('weightService', ["$http", function($http) { + this.addWeight=function(weight, fat){ + return $http({ + method:'post', + url:'/weight/add', + data:{weight:weight, fat:fat}, + showSpinner:true + }).then(function(response){ + return response.data; + }); + }; + this.deleteWeight=function(id){ + return $http({ + method:'post', + url:'/weight/delete', + data:{id:id}, + showSpinner:true + }).then(function(response){ + return response.data; + }); + }; + this.getReadings=function(){ + return $http({ + method:'post', + url:'/weight/getreadings', + showSpinner:true + }).then(function(response){ + return response.data; + }); + }; +}]); \ No newline at end of file diff --git a/Website/wwwroot/js/site.js b/Website/wwwroot/js/site.js deleted file mode 100644 index b2f58e1..0000000 --- a/Website/wwwroot/js/site.js +++ /dev/null @@ -1,4 +0,0 @@ -// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification -// for details on configuring this project to bundle and minify static web assets. - -// Write your JavaScript code.