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: + + 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 0000000..a571c6e Binary files /dev/null and b/Website/wwwroot/images/chevron.png differ 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 0000000..0e05401 Binary files /dev/null and b/Website/wwwroot/images/logo_email.png differ 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.