diff --git a/.angular-cli.json b/.angular-cli.json
new file mode 100644
index 0000000000000000000000000000000000000000..ce654106465498153093c13f88c34696eaa0252b
--- /dev/null
+++ b/.angular-cli.json
@@ -0,0 +1,68 @@
+{
+    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+    "project": {
+        "name": "cli-stable"
+    },
+    "apps": [
+        {
+            "root": "src",
+            "outDir": "dist",
+            "assets": [
+                "assets",
+                "favicon.ico",
+                ".htaccess"
+            ],
+            "index": "index.html",
+            "main": "main.ts",
+            "polyfills": "polyfills.ts",
+            "test": "test.ts",
+            "tsconfig": "tsconfig.app.json",
+            "testTsconfig": "tsconfig.spec.json",
+            "prefix": "app",
+            "styles": [
+                "../node_modules/font-awesome/css/font-awesome.css",
+                "styles/app.scss",
+                "styles/console.css",
+                "../node_modules/tippy.js/dist/tippy.css",
+                "../node_modules/codemirror/lib/codemirror.css",
+                "../node_modules/codemirror/addon/hint/show-hint.css",
+                "../node_modules/codemirror/addon/dialog/dialog.css",
+                "../node_modules/codemirror/addon/search/matchesonscrollbar.css"
+            ],
+            "scripts": [
+                "../node_modules/jquery/dist/jquery.min.js",
+                "../node_modules/jq-console/lib/jqconsole.js"
+            ],
+            "environmentSource": "environments/environment.ts",
+            "environments": {
+                "dev": "environments/environment.ts",
+                "prod": "environments/environment.prod.ts"
+            }
+        }
+    ],
+    "e2e": {
+        "protractor": {
+            "config": "./protractor.conf.js"
+        }
+    },
+    "lint": [
+        {
+            "project": "src/tsconfig.app.json"
+        },
+        {
+            "project": "src/tsconfig.spec.json"
+        },
+        {
+            "project": "e2e/tsconfig.e2e.json"
+        }
+    ],
+    "test": {
+        "karma": {
+            "config": "./karma.conf.js"
+        }
+    },
+    "defaults": {
+        "styleExt": "scss",
+        "component": {}
+    }
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000000000000000000000000000000000..9b7352176acfa09b8f83d19dea54e34a8089cd5f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# Editor configuration, see http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..537939b80542401d5f9011cd662d0fbe7e7092d1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,42 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+# /dist
+/tmp
+/out-tsc
+
+# dependencies
+/node_modules
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+testem.log
+/typings
+
+# e2e
+/e2e/*.js
+/e2e/*.map
+
+# System Files
+.DS_Store
+Thumbs.db
diff --git a/README.md b/README.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..73593638e277094c600c4a55edad6a3ff3a8482e 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,42 @@
+Luego de ejecutar npm install, copiar los archivos ubicados en la carpeta "custom codemirror" en "node_modules/codemirror/addon/search"
+
+
+Detalles del template utilizado:
+
+# SB Admin rewritten in Angular4 and Bootstrap 4
+
+Simple Dashboard Admin App built using Angular 4 and Bootstrap 4
+
+This project is a port of the famous Free Admin Bootstrap Theme [SB Admin v4.0](http://startbootstrap.com/template-overviews/sb-admin-2/) to Angular4 Theme.
+
+Powered by [StartAngular](http://startangular.com/) & [StrapUI](http://strapui.com/)
+
+## [Demo](http://rawgit.com/start-angular/SB-Admin-BS4-Angular-4/master/dist/)
+
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0.
+
+### Development server
+
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+### Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
+
+### Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
+
+### Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+### Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+Before running the tests make sure you are serving the app via `ng serve`.
+
+### Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/custom codemirror/jump-to-line.js b/custom codemirror/jump-to-line.js
new file mode 100644
index 0000000000000000000000000000000000000000..696bbd40a1d6c9c8fa9f5235ed14f27ed590182e
--- /dev/null
+++ b/custom codemirror/jump-to-line.js	
@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Defines jumpToLine command. Uses dialog.js if present.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../dialog/dialog"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../dialog/dialog"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function dialog(cm, text, shortText, deflt, f) {
+    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
+    else f(prompt(shortText, deflt));
+  }
+
+  var jumpDialog =
+      'Ir a la línea: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint"></span>';
+
+  function interpretLine(cm, string) {
+    var num = Number(string)
+    if (/^[-+]/.test(string)) return cm.getCursor().line + num
+    else return num - 1
+  }
+
+  CodeMirror.commands.jumpToLine = function(cm) {
+    var cur = cm.getCursor();
+    dialog(cm, jumpDialog, "Jump to line:", (cur.line + 1) + ":" + cur.ch, function(posStr) {
+      if (!posStr) return;
+
+      var match;
+      if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) {
+        cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
+      } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) {
+        var line = Math.round(cm.lineCount() * Number(match[1]) / 100);
+        if (/^[-+]/.test(match[1])) line = cur.line + line + 1;
+        cm.setCursor(line - 1, cur.ch);
+      } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) {
+        cm.setCursor(interpretLine(cm, match[1]), cur.ch);
+      }
+    });
+  };
+
+  CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine";
+});
diff --git a/custom codemirror/search.js b/custom codemirror/search.js
new file mode 100644
index 0000000000000000000000000000000000000000..08b24d265f1d2ff73856b40ebe071bc8c4ecf035
--- /dev/null
+++ b/custom codemirror/search.js	
@@ -0,0 +1,252 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Define search commands. Depends on dialog.js or another
+// implementation of the openDialog method.
+
+// Replace works a little oddly -- it will do the replace on the next
+// Ctrl-G (or whatever is bound to findNext) press. You prevent a
+// replace by making sure the match is no longer selected when hitting
+// Ctrl-G.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function searchOverlay(query, caseInsensitive) {
+    if (typeof query == "string")
+      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
+    else if (!query.global)
+      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
+
+    return {token: function(stream) {
+      query.lastIndex = stream.pos;
+      var match = query.exec(stream.string);
+      if (match && match.index == stream.pos) {
+        stream.pos += match[0].length || 1;
+        return "searching";
+      } else if (match) {
+        stream.pos = match.index;
+      } else {
+        stream.skipToEnd();
+      }
+    }};
+  }
+
+  function SearchState() {
+    this.posFrom = this.posTo = this.lastQuery = this.query = null;
+    this.overlay = null;
+  }
+
+  function getSearchState(cm) {
+    return cm.state.search || (cm.state.search = new SearchState());
+  }
+
+  function queryCaseInsensitive(query) {
+    return typeof query == "string" && query == query.toLowerCase();
+  }
+
+  function getSearchCursor(cm, query, pos) {
+    // Heuristic: if the query string is all lowercase, do a case insensitive search.
+    return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
+  }
+
+  function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
+    cm.openDialog(text, onEnter, {
+      value: deflt,
+      selectValueOnOpen: true,
+      closeOnEnter: false,
+      onClose: function() { clearSearch(cm); },
+      onKeyDown: onKeyDown
+    });
+  }
+
+  function dialog(cm, text, shortText, deflt, f) {
+    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
+    else f(prompt(shortText, deflt));
+  }
+
+  function confirmDialog(cm, text, shortText, fs) {
+    if (cm.openConfirm) cm.openConfirm(text, fs);
+    else if (confirm(shortText)) fs[0]();
+  }
+
+  function parseString(string) {
+    return string.replace(/\\(.)/g, function(_, ch) {
+      if (ch == "n") return "\n"
+      if (ch == "r") return "\r"
+      return ch
+    })
+  }
+
+  function parseQuery(query) {
+    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
+    if (isRE) {
+      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
+      catch(e) {} // Not a regular expression after all, do a string search
+    } else {
+      query = parseString(query)
+    }
+    if (typeof query == "string" ? query == "" : query.test(""))
+      query = /x^/;
+    return query;
+  }
+
+  var queryDialog =
+    '<span class="CodeMirror-search-label">Buscar:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint"></span>';
+
+  function startSearch(cm, state, query) {
+    state.queryText = query;
+    state.query = parseQuery(query);
+    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
+    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
+    cm.addOverlay(state.overlay);
+    if (cm.showMatchesOnScrollbar) {
+      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+    }
+  }
+
+  function doSearch(cm, rev, persistent, immediate) {
+    var state = getSearchState(cm);
+    if (state.query) return findNext(cm, rev);
+    var q = cm.getSelection() || state.lastQuery;
+    if (persistent && cm.openDialog) {
+      var hiding = null
+      var searchNext = function(query, event) {
+        CodeMirror.e_stop(event);
+        if (!query) return;
+        if (query != state.queryText) {
+          startSearch(cm, state, query);
+          state.posFrom = state.posTo = cm.getCursor();
+        }
+        if (hiding) hiding.style.opacity = 1
+        findNext(cm, event.shiftKey, function(_, to) {
+          var dialog
+          if (to.line < 3 && document.querySelector &&
+              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
+              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
+            (hiding = dialog).style.opacity = .4
+        })
+      };
+      persistentDialog(cm, queryDialog, q, searchNext, function(event, query) {
+        var keyName = CodeMirror.keyName(event)
+        var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
+        if (!cmd) cmd = cm.getOption('extraKeys')[keyName]
+        if (cmd == "findNext" || cmd == "findPrev" ||
+          cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
+          CodeMirror.e_stop(event);
+          startSearch(cm, getSearchState(cm), query);
+          cm.execCommand(cmd);
+        } else if (cmd == "find" || cmd == "findPersistent") {
+          CodeMirror.e_stop(event);
+          searchNext(query, event);
+        }
+      });
+      if (immediate && q) {
+        startSearch(cm, state, q);
+        findNext(cm, rev);
+      }
+    } else {
+      dialog(cm, queryDialog, "Buscar por:", q, function(query) {
+        if (query && !state.query) cm.operation(function() {
+          startSearch(cm, state, query);
+          state.posFrom = state.posTo = cm.getCursor();
+          findNext(cm, rev);
+        });
+      });
+    }
+  }
+
+  function findNext(cm, rev, callback) {cm.operation(function() {
+    var state = getSearchState(cm);
+    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
+    if (!cursor.find(rev)) {
+      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
+      if (!cursor.find(rev)) return;
+    }
+    cm.setSelection(cursor.from(), cursor.to());
+    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
+    state.posFrom = cursor.from(); state.posTo = cursor.to();
+    if (callback) callback(cursor.from(), cursor.to())
+  });}
+
+  function clearSearch(cm) {cm.operation(function() {
+    var state = getSearchState(cm);
+    state.lastQuery = state.query;
+    if (!state.query) return;
+    state.query = state.queryText = null;
+    cm.removeOverlay(state.overlay);
+    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+  });}
+
+  var replaceQueryDialog =
+    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint"></span>';
+  var replacementQueryDialog = '<span class="CodeMirror-search-label">Con:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
+  var doReplaceConfirm = '<span class="CodeMirror-search-label">Reemplazar?</span> <button>Si</button> <button>No</button> <button>Todos</button> <button>Cancelar</button>';
+
+  function replaceAll(cm, query, text) {
+    cm.operation(function() {
+      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+        if (typeof query != "string") {
+          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+        } else cursor.replace(text);
+      }
+    });
+  }
+
+  function replace(cm, all) {
+    if (cm.getOption("readOnly")) return;
+    var query = cm.getSelection() || getSearchState(cm).lastQuery;
+    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Reemplazar todo:' : 'Reemplazar:') + '</span>';
+    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
+      if (!query) return;
+      query = parseQuery(query);
+      dialog(cm, replacementQueryDialog, "Reemplazar con:", "", function(text) {
+        text = parseString(text)
+        if (all) {
+          replaceAll(cm, query, text)
+        } else {
+          clearSearch(cm);
+          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+          var advance = function() {
+            var start = cursor.from(), match;
+            if (!(match = cursor.findNext())) {
+              cursor = getSearchCursor(cm, query);
+              if (!(match = cursor.findNext()) ||
+                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+            }
+            cm.setSelection(cursor.from(), cursor.to());
+            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+            confirmDialog(cm, doReplaceConfirm, "Reemplazar?",
+                          [function() {doReplace(match);}, advance,
+                           function() {replaceAll(cm, query, text)}]);
+          };
+          var doReplace = function(match) {
+            cursor.replace(typeof query == "string" ? text :
+                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+            advance();
+          };
+          advance();
+        }
+      });
+    });
+  }
+
+  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
+  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
+  CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
+  CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
+  CodeMirror.commands.findNext = doSearch;
+  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
+  CodeMirror.commands.clearSearch = clearSearch;
+  CodeMirror.commands.replace = replace;
+  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
+});
diff --git a/e2e/app.e2e-spec.ts b/e2e/app.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0bcefd88f860c2a231cb0841b5f1f8fc3db1630a
--- /dev/null
+++ b/e2e/app.e2e-spec.ts
@@ -0,0 +1,14 @@
+import { CliStablePage } from './app.po';
+
+describe('cli-stable App', () => {
+  let page: CliStablePage;
+
+  beforeEach(() => {
+    page = new CliStablePage();
+  });
+
+  it('should display message saying app works', () => {
+    page.navigateTo();
+    expect(page.getParagraphText()).toEqual('app works!');
+  });
+});
diff --git a/e2e/app.po.ts b/e2e/app.po.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11ce66a9f934629452998aafe0037f0a4de6ab89
--- /dev/null
+++ b/e2e/app.po.ts
@@ -0,0 +1,11 @@
+import { browser, element, by } from 'protractor';
+
+export class CliStablePage {
+  navigateTo() {
+    return browser.get('/');
+  }
+
+  getParagraphText() {
+    return element(by.css('app-root h1')).getText();
+  }
+}
diff --git a/e2e/tsconfig.e2e.json b/e2e/tsconfig.e2e.json
new file mode 100644
index 0000000000000000000000000000000000000000..ac7a37325798f692729aa27a75a1b6c86db3502b
--- /dev/null
+++ b/e2e/tsconfig.e2e.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/e2e",
+    "module": "commonjs",
+    "target": "es5",
+    "types":[
+      "jasmine",
+      "node"
+    ]
+  }
+}
diff --git a/karma.conf.js b/karma.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..84b4cd5acab29968044fc5748fa53dd97d21229d
--- /dev/null
+++ b/karma.conf.js
@@ -0,0 +1,44 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/0.13/config/configuration-file.html
+
+module.exports = function (config) {
+  config.set({
+    basePath: '',
+    frameworks: ['jasmine', '@angular/cli'],
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-chrome-launcher'),
+      require('karma-jasmine-html-reporter'),
+      require('karma-coverage-istanbul-reporter'),
+      require('@angular/cli/plugins/karma')
+    ],
+    client:{
+      clearContext: false // leave Jasmine Spec Runner output visible in browser
+    },
+    files: [
+      { pattern: './src/test.ts', watched: false }
+    ],
+    preprocessors: {
+      './src/test.ts': ['@angular/cli']
+    },
+    mime: {
+      'text/x-typescript': ['ts','tsx']
+    },
+    coverageIstanbulReporter: {
+      reports: [ 'html', 'lcovonly' ],
+      fixWebpackSourcePaths: true
+    },
+    angularCli: {
+      environment: 'dev'
+    },
+    reporters: config.angularCli && config.angularCli.codeCoverage
+              ? ['progress', 'coverage-istanbul']
+              : ['progress', 'kjhtml'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome'],
+    singleRun: false
+  });
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..37280096f01533ab49f88e2282716a7e27a9f91d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,55 @@
+{
+  "name": "sb-admin-angular4-bootstrap4",
+  "version": "1.0.0",
+  "license": "MIT",
+  "scripts": {
+    "ng": "ng",
+    "start": "ng serve",
+    "build": "ng build",
+    "test": "ng test",
+    "lint": "ng lint",
+    "e2e": "ng e2e"
+  },
+  "private": true,
+  "dependencies": {
+    "@angular/common": "^4.0.0",
+    "@angular/compiler": "^4.0.0",
+    "@angular/core": "^4.1.3",
+    "@angular/forms": "^4.0.0",
+    "@angular/http": "^4.0.0",
+    "@angular/platform-browser": "^4.0.0",
+    "@angular/platform-browser-dynamic": "^4.0.0",
+    "@angular/router": "^4.0.0",
+    "@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.22",
+    "core-js": "^2.4.1",
+    "font-awesome": "^4.7.0",
+    "ionicons": "^3.0.0",
+    "jq-console": "^2.13.2",
+    "jquery": "^3.2.1",
+    "ng2-bootstrap-modal": "^1.0.1",
+    "ng2-codemirror": "^1.1.1",
+    "rxjs": "^5.1.0",
+    "tippy.js": "^1.2.0",
+    "zone.js": "^0.8.4"
+  },
+  "devDependencies": {
+    "@angular/cli": "1.0.0",
+    "@angular/compiler-cli": "^4.0.0",
+    "@types/jasmine": "2.5.38",
+    "@types/jquery": "^2.0.45",
+    "@types/node": "~6.0.60",
+    "codelyzer": "~2.0.0",
+    "jasmine-core": "~2.5.2",
+    "jasmine-spec-reporter": "~3.2.0",
+    "karma": "~1.4.1",
+    "karma-chrome-launcher": "~2.0.0",
+    "karma-cli": "~1.0.1",
+    "karma-coverage-istanbul-reporter": "^0.2.0",
+    "karma-jasmine": "~1.1.0",
+    "karma-jasmine-html-reporter": "^0.2.2",
+    "protractor": "~5.1.0",
+    "ts-node": "~2.0.0",
+    "tslint": "~4.5.0",
+    "typescript": "~2.2.0"
+  }
+}
diff --git a/protractor.conf.js b/protractor.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..fc13da6c6586a873cead1d9c6864a6787ad81b3b
--- /dev/null
+++ b/protractor.conf.js
@@ -0,0 +1,30 @@
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter } = require('jasmine-spec-reporter');
+
+exports.config = {
+  allScriptsTimeout: 11000,
+  specs: [
+    './e2e/**/*.e2e-spec.ts'
+  ],
+  capabilities: {
+    'browserName': 'chrome'
+  },
+  directConnect: true,
+  baseUrl: 'http://0.0.0.0:4200/',
+  framework: 'jasmine',
+  jasmineNodeOpts: {
+    showColors: true,
+    defaultTimeoutInterval: 30000,
+    print: function() {}
+  },
+  beforeLaunch: function() {
+    require('ts-node').register({
+      project: 'e2e/tsconfig.e2e.json'
+    });
+  },
+  onPrepare() {
+    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+  }
+};
diff --git a/src/.htaccess b/src/.htaccess
new file mode 100644
index 0000000000000000000000000000000000000000..e906ce034d9b3a4bbd1dc4a8bdca08481f5fd927
--- /dev/null
+++ b/src/.htaccess
@@ -0,0 +1,6 @@
+RewriteEngine On
+RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
+RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
+RewriteRule ^ - [L]
+
+RewriteRule ^ /index.html [L]
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d3972a1895d806d37dbf2b6e7144d2ba5cd13092
--- /dev/null
+++ b/src/app/app-routing.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { AuthGuard } from './shared/guards/auth.guard';
+
+const routes: Routes = [
+    { path: '', loadChildren: './layout/layout.module#LayoutModule', canActivate: [AuthGuard] },
+    { path: 'login', loadChildren: './login/login.module#LoginModule' },
+    { path: 'not-found', loadChildren: './not-found/not-found.module#NotFoundModule' },
+    { path: '**', redirectTo: 'not-found' }
+];
+
+@NgModule({
+    imports: [RouterModule.forRoot(routes, { useHash: true })],
+    exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/src/app/app.component.html b/src/app/app.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0680b43f9c6ae05df91c576141f20ed411d07c7d
--- /dev/null
+++ b/src/app/app.component.html
@@ -0,0 +1 @@
+<router-outlet></router-outlet>
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fee2a8011f1505e0a6cf68f6dc12f162e128530
--- /dev/null
+++ b/src/app/app.component.spec.ts
@@ -0,0 +1,36 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        RouterTestingModule
+      ],
+      declarations: [
+        AppComponent
+      ],
+    }).compileComponents();
+  }));
+
+  it('should create the app', async(() => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.debugElement.componentInstance;
+    expect(app).toBeTruthy();
+  }));
+
+  it(`should have as title 'app works!'`, async(() => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.debugElement.componentInstance;
+    expect(app.title).toEqual('app works!');
+  }));
+
+  it('should render title in a h1 tag', async(() => {
+    const fixture = TestBed.createComponent(AppComponent);
+    fixture.detectChanges();
+    const compiled = fixture.debugElement.nativeElement;
+    expect(compiled.querySelector('h1').textContent).toContain('app works!');
+  }));
+});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa1c15aa845343c53a856188c8938fa69b42f654
--- /dev/null
+++ b/src/app/app.component.ts
@@ -0,0 +1,14 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+@Component({
+    selector: 'app-root',
+    templateUrl: './app.component.html',
+    styleUrls: ['./app.component.scss']
+})
+export class AppComponent implements OnInit {
+    constructor(public router: Router) { }
+    ngOnInit() {
+        //this.router.navigate(['/login']);
+    }
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..084f634a68ef5c67a0b66e483b4fa78d954826aa
--- /dev/null
+++ b/src/app/app.module.ts
@@ -0,0 +1,25 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+import { AuthGuard } from './shared/guards/auth.guard';
+import { SessionService } from './shared/services/session.service';
+import { NotificacionService } from './shared/services/notificacion.service';
+import {NotificacionModule} from './notificacion/notificacion.module'
+@NgModule({
+    declarations: [
+        AppComponent,
+    ],
+    imports: [
+        NotificacionModule,
+        BrowserModule,
+        FormsModule,
+        HttpModule,
+        AppRoutingModule
+    ],
+    providers: [AuthGuard, SessionService,NotificacionService],
+    bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/src/app/layout/archivos/archivos-routing.module.ts b/src/app/layout/archivos/archivos-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47cc7eab4298205c636aab78669e16deac50c9b9
--- /dev/null
+++ b/src/app/layout/archivos/archivos-routing.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { ArchivosComponent } from './archivos.component';
+
+const routes: Routes = [
+    { path: '', component: ArchivosComponent }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class ArchivosRoutingModule { }
\ No newline at end of file
diff --git a/src/app/layout/archivos/archivos.component.html b/src/app/layout/archivos/archivos.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6df5a10f81854ae0c8b10b5f89db033375702fca
--- /dev/null
+++ b/src/app/layout/archivos/archivos.component.html
@@ -0,0 +1,152 @@
+<notificacion></notificacion> 
+<div class="container-fluid">
+    <div class="row">
+        <div class="col-lg-5">
+            <label for="search">Nombre del archivo:</label>
+            <div class="input-group">
+                <input type="text" class="form-control" id="search" [(ngModel)]=filtroNombre>
+                <span class="input-group-addon fa fa-search">
+                </span>
+            </div>
+
+        </div>
+                       
+    </div>
+    <div class="row" style="margin-top: 20px">
+        <div class="col-lg-5">
+            <ngb-tabset [destroyOnHide]=false>
+                <ngb-tab title="Mis archivos">
+                    <ng-template ngbTabContent>
+
+                        <div class="card">
+                            <div>
+                            <div class="btn-group pull-right" style="cursor: pointer; margin-top: -9px;">
+                                <button  ngbPopover="Atras"  triggers="mouseenter:mouseleave" data-placement="bottom" class="btn btn-sm btn-secondary pull-right" *ngIf="directorioActual.padreId!=-1" style="cursor: pointer; margin-top: -33.5px; margin-right: 73px; height: 30px;"(click)="navBack()">
+                                    <i class="fa fa-arrow-up"></i>
+                                </button>
+                            </div>
+                                <!-- <button class="btn btn-sm btn-secondary pull-right" style="cursor: pointer; margin-top: -35px;" > 
+                                    <i class="fa fa-plus"></i> 
+                                </buton> -->
+                                <div class="btn-group pull-right" style="cursor: pointer;    margin-top: -42px; height: 30px;    width: 29px;">
+                                  <button ngbPopover="Nuevo"  triggers="mouseenter:mouseleave" data-placement="bottom" style="cursor: pointer;border-radius: 3px;" type="button" class="btn btn-sm btn-secondary" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                    <i class="fa fa-plus"></i> 
+                                  </button>
+                                  <div class="dropdown-menu">
+                                    <a class="dropdown-item"(click)="mkFile()" >Archivo</a>
+                                    <div class="dropdown-divider"></div>
+                                    <a class="dropdown-item" (click)="mkdir()">Carpeta</a>
+                                  </div>
+                                </div>
+
+                                <div class="btn-group pull-right" style="cursor: pointer;    margin-top: -42px; height: 30px;    width: 29px; margin-right: 36px;">
+                                  <button ngbPopover="Ordenar"  triggers="mouseenter:mouseleave" data-placement="bottom" style="cursor: pointer;border-radius: 3px;width: 36px;" type="button" class="btn btn-sm btn-secondary" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                    <i class="fa fa-sort"></i> 
+                                  </button>
+                                  <div class="dropdown-menu">
+                                    <a class="dropdown-item"(click)="ordenarPorTipo()" >Carpetas primero</a>
+                                    <div class="dropdown-divider"></div>
+                                    <a class="dropdown-item" (click)="ordenarPorFecha()">Fecha de creación</a>
+                                  </div>
+                                </div>
+
+
+                            </div>
+                            <div class="card-block">
+                                <div class="row listado-archivos" style="min-height: 100px; overflow-y: scroll;">
+                                    <div class="loading" *ngIf="loading">
+                                        <div class="loading-bar"></div>
+                                        <div class="loading-bar"></div>
+                                        <div class="loading-bar"></div>
+                                        <div class="loading-bar"></div>
+                                    </div>
+                                    <div *ngFor="let arch of directorioActual.archivos | filter: 'nombre': filtroNombre " (click)="seleccionarArchivo(arch)" class="col-sm-3 col-4" style="text-align: center;">
+                                        <i *ngIf="arch.directorio" class="fa fa-folder" style="font-size: 3em; cursor: pointer;color:#f95e5e" aria-hidden="true"></i>
+                                        <i *ngIf="!arch.directorio" class="fa fa-file-text" style="font-size: 3em; cursor: pointer;color:#ff8383" aria-hidden="true"></i>
+                                        <p style="cursor: pointer;">{{arch.nombre}}</p>
+                                    </div>            
+                                </div>
+                            </div>
+                        </div>
+
+                    </ng-template>
+                </ngb-tab>
+                <ngb-tab title="Compartidos">
+                    <ng-template ngbTabContent>
+
+                        <div class="card" *ngIf="esAlumno">
+                            <div class="card-block">
+                                <div class="row listado-archivos" style="min-height: 100px; overflow-y: scroll;">
+                                    <div class="loading" *ngIf="loadingCompartidos">
+                                        <div class="loading-bar"></div>
+                                        <div class="loading-bar"></div>
+                                        <div class="loading-bar"></div>
+                                        <div class="loading-bar"></div>
+                                    </div>
+                                    <div *ngFor="let arch of archivosCompartidosSinDuplicados | filter: 'nombre': filtroNombre " (click)="seleccionarArchivo(arch)" class="col-sm-3 col-4" style="text-align: center;">
+                                        <i class="fa fa-file-text" style="font-size: 3em; cursor: pointer;color:#ff8383" aria-hidden="true"></i>
+
+                                        <p style="cursor: pointer;">{{arch.nombre}}</p>
+                                    </div>            
+                                </div>
+                            </div>
+                        </div>
+                    </ng-template>
+                </ngb-tab>
+            </ngb-tabset> 
+        </div>
+        <div class="col-lg-7">
+            <div class="card">
+                <div class="card-header">
+                    <button ngbPopover="Cargar/Editar" data-placement="bottom" triggers="mouseenter:mouseleave" class="btn btn-sm btn-secondary pull-left mr-2" (click)="cargarArchivo()">
+                        <i class="fa fa-pencil"></i>
+                    </button>
+                    <button ngbPopover="Eliminar" data-placement="bottom" triggers="mouseenter:mouseleave" class="btn btn-sm btn-secondary pull-left mr-2" (click)="mostrarEliminarDialogo()">  
+                        <i class="fa fa-remove"></i> 
+                    </button>
+                    <button ngbPopover="Mover Archivo"data-placement="bottom" triggers="mouseenter:mouseleave" class="btn btn-sm btn-secondary pull-left mr-2" (click)="seleccionarDirectorioAMover()">  
+                        <i class="fa fa-cut"></i> 
+                    </button>
+
+                    <button class="btn btn-sm btn-secondary pull-left mr-2" *ngIf="esAlumno && archivoSeleccionado && hayArchivoOriginal()" (click)="seleccionarArchivoOriginal()">Ver original</button>
+
+                    <button class="btn btn-sm btn-secondary pull-left mr-2" *ngIf="esAlumno && archivoSeleccionado && hayArchivoMio()" (click)="seleccionarArchivoMio()">Ver mio</button>
+
+                    <button class="btn btn-sm btn-secondary pull-left mr-4" *ngIf="esAlumno && archivoSeleccionado && !archivoSeleccionado.directorio && archivoSeleccionado.archivoOrigenId != -1 && (archivoSeleccionado.estado == 'Edicion' || archivoSeleccionado.estado == 'Devuelto')" (click)="confirmarEntrega()">Entregar</button>
+                    
+                    <div class="pull-left mr-2" *ngIf="esAlumno && (archivoSeleccionado?.estado == 'Entregado')">{{archivoSeleccionado.estado}} - </div>
+
+                    <button *ngIf="esAlumno && archivoSeleccionado?.estado == 'Corregido'" class="btn btn-sm btn-secondary pull-left mr-2" (click)="verCalificacion()">Ver calificacion</button>
+
+                    <button *ngIf="!esAlumno" class="btn btn-sm btn-secondary pull-left mr-2" (click)="compartirArchivo()">Compartir</button>
+
+                    <div class="pull-left" *ngIf="archivoSeleccionado">
+                        Nombre: {{archivoSeleccionado?.nombre}} - Creado: {{archivoSeleccionado?.fechaCreacion | date}}
+                    </div>
+                    <div class="pull-left" *ngIf="!archivoSeleccionado">Seleccione un archivo para previsualizarlo</div>
+                    <div class="pull-right" *ngIf="archivoSeleccionado">
+                        <label class="custom-control custom-checkbox" *ngIf="!esAlumno">
+                          <input type="checkbox"  *ngIf="archivoSeleccionado.editable" (click)="setSoloLectura()" class="custom-control-input">
+                          <input type="checkbox"  *ngIf="!archivoSeleccionado.editable" checked (click)="setSoloLectura()" class="custom-control-input">
+                          <span class="custom-control-indicator"></span>
+                          <span class="custom-control-description">Solo lectura</span>
+                      </label>
+                  </div>
+              </div>
+              <div *ngIf="!archivoSeleccionado" class="card-block">
+                <div class="previewArchivoNoSeleccionado" style="width: 100%; text-align: center;">
+                    <i style="color: rgb(220,220,220); font-size: 10em; padding: 0.1em" class="fa fa-file-text"></i>    
+                </div>                    
+            </div>
+            <codemirror class="codemirrorArchivo" *ngIf="archivoSeleccionado" [(ngModel)]="preview" [config]="configCodeMirror" [ngStyle]="{'font-size': configCodeMirror.fontSize+'px'}">
+                </codemirror>
+        </div>
+
+    </div>
+
+</div>
+</div>
+
+
+
+
diff --git a/src/app/layout/archivos/archivos.component.ts b/src/app/layout/archivos/archivos.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09ceca704024f9f7ef76acbde016818648a8df64
--- /dev/null
+++ b/src/app/layout/archivos/archivos.component.ts
@@ -0,0 +1,535 @@
+import { Component,ViewChild } from '@angular/core';
+import { Archivo } from '../../shared/objects/archivo';
+import { AuthenticationService } from '../../shared/services/authentication.service';
+import { HaskellService } from '../../shared/services/haskell.service';
+import { SessionService } from '../../shared/services/session.service';
+import { NotificacionService } from '../../shared/services/notificacion.service';
+import { Router } from '@angular/router';
+import { NuevoArchivo } from './nuevoArchivo.component';
+import { VerCalificacionComponent } from './verCalificacion.component';
+import { CompartirArchivoComponent } from './compartirArchivo.component';
+import { ConfirmarEliminar } from './confirmarEliminar.component';
+import { DialogService } from "ng2-bootstrap-modal";
+import { ConfirmComponent } from '../../shared/modal/confirm.component';
+import { SeleccionarDirectorioMove } from './seleccionarDirectorio.component';
+import { CodemirrorComponent } from 'ng2-codemirror';
+import { NgbPopoverConfig, NgbPopover} from '@ng-bootstrap/ng-bootstrap';
+
+import 'codemirror/mode/haskell/haskell';
+import 'codemirror/addon/display/panel';
+import 'codemirror/addon/hint/show-hint';
+import 'codemirror/addon/hint/anyword-hint';
+import 'codemirror/mode/markdown/markdown';
+
+@Component({
+	moduleId: module.id,
+	selector: 'archivos',
+	templateUrl: './archivos.component.html'
+})
+
+export class ArchivosComponent {
+	archivos : Archivo[] = [];
+	archivosCompartidos: Archivo[] = [];
+	archivosCompartidosSinDuplicados: Archivo [] = [];
+	archivoSeleccionado: Archivo;
+	loading: boolean = false;
+	loadingCompartidos:boolean = false;
+	filtroNombre: string = '';
+	idRecorridos :any = [];
+	esAlumno :boolean;
+	tree: any;
+	preview: string = '';
+	directorioActual:any;
+	sortFunction:any;
+	configCodeMirror = JSON.parse(sessionStorage.getItem('codeMirrorConfig')) ;
+
+	constructor(
+		private router: Router,
+		private notifService: NotificacionService,
+		private authService: AuthenticationService,
+		private haskellService: HaskellService,
+		private sessionService: SessionService,
+		private dialogService:DialogService
+		){
+		this.esAlumno = JSON.parse(sessionStorage.getItem("currentUser")).tipo ==="alumno";
+		this.directorioActual = {};
+		this.directorioActual.archivos = [];
+		this.configCodeMirror.readOnly = true;
+	}
+	@ViewChild(CodemirrorComponent) codemirror: CodemirrorComponent;
+
+	ngOnInit(){
+		this.sortFunction = 'tipo'
+		let cedula = this.authService.getUser().cedula;
+		this.loading = true;
+		this.haskellService.getArchivos(cedula)
+		.subscribe(
+			archivos => {
+				this.archivos = archivos;
+				this.loading = false;
+				this.buildTreeFromList();
+
+			}, 
+			error => console.log(error) 
+			);
+
+		if(this.esAlumno){
+			this.loadingCompartidos = true;
+			this.haskellService.getArchivosCompartidosAlumno(cedula)
+			.subscribe(
+				archivos => {
+					this.archivosCompartidos = archivos;
+					this.archivosCompartidosSinDuplicados = archivos.filter(arch => arch.archivoOrigenId != -1 || !archivos.some(a => a.archivoOrigenId == arch.id));
+					this.loadingCompartidos = false;
+				}, 
+				error => console.log(error) 
+				);
+
+		}
+	}
+	ordenarMixto(){
+		//1. primero las carpetas.
+		this.archivosCompartidosSinDuplicados = this.archivosCompartidosSinDuplicados.sort(this.ordenarTipo);
+		this.directorioActual.archivos= this.directorioActual.archivos.sort(this.ordenarTipo);
+
+		var archs1 = this.directorioActual.archivos;
+		var archs2 = this.archivosCompartidosSinDuplicados;
+
+		var archs1_directorios = archs1.filter(
+			function (a) {
+				return a.directorio;
+			}
+		);
+
+		var archs1_archivos =archs1.filter(
+			function(a){
+				return !a.directorio;
+			}
+
+		);
+
+		var archs2_directorios = archs2.filter(
+			function (a) {
+				return a.directorio;
+			}
+		);
+
+		var archs2_archivos =archs2.filter(
+			function(a){
+				return !a.directorio;
+			}
+
+		);
+		//2. dentro de cada categoría ordeno alfabéticamente.
+
+		archs1_archivos = archs1_archivos.sort(this.ordenarAlph);
+		archs1_directorios = archs1_directorios.sort(this.ordenarAlph);
+		archs2_archivos = archs2_archivos.sort(this.ordenarAlph);
+		archs2_directorios = archs2_directorios.sort(this.ordenarAlph);
+
+		for(var i in archs1_archivos){
+			archs1_directorios.push(archs1_archivos[i])
+		}
+		for(var i in archs2_archivos){
+			archs2_directorios.push(archs2_archivos[i])
+		}
+
+		this.directorioActual.archivos = archs1_directorios;
+		this.archivosCompartidosSinDuplicados = archs2_directorios;
+	}
+
+	ordenarAlph(a,b){
+		if(a.nombre.toLowerCase() < b.nombre.toLowerCase()) return -1;
+		if(a.nombre.toLowerCase() > b.nombre.toLowerCase()) return 1;
+		return 0;
+	}
+	ordenarFecha(a, b){
+		if(a.fechaCreacion < b.fechaCreacion) return -1;
+		if(a.fechaCreacion > b.fechaCreacion) return 1;
+		return 0;
+	}
+	ordenarTipo(a,b){
+		if(a.directorio && !b.directorio) return -1;
+		if(!a.directorio && b.directorio) return 1;
+		return 0;	
+	}
+	ordenarPorTipo(){
+		this.sortFunction='tipo';
+		this.ordenarArchivos();
+	}
+	ordenarPorFecha(){
+		this.sortFunction='fecha';
+		this.ordenarArchivos();
+	}
+	ordenarFechaCreacion(){
+		this.archivosCompartidosSinDuplicados = this.archivosCompartidosSinDuplicados.sort(this.ordenarFecha);
+		this.directorioActual.archivos= this.directorioActual.archivos.sort(this.ordenarFecha);
+	}
+
+	ordenarArchivos(){
+		var tipo = this.sortFunction;
+		if(tipo==='tipo'){
+			this.ordenarMixto();	
+		} else if(tipo==='fecha'){
+			this.ordenarFechaCreacion();
+		}
+	}
+
+	mostrarEliminarDialogo(){
+		if(this.archivoSeleccionado){
+			//Si el archivo es del alumno lo puedo eliminar. 
+			//(No se controla por creador dado que los compartidos mantienen este atributo)
+			if(this.archivos.some(arch => arch.id == this.archivoSeleccionado.id)){
+				var that = this;
+				let disposable = this.dialogService.addDialog(ConfirmarEliminar, {
+					nombreArchivo:that.archivoSeleccionado.nombre, 
+					esDirectorio:that.archivoSeleccionado.directorio,
+					parentContext :that})
+				.subscribe((isConfirmed)=>{
+
+					if(isConfirmed) {
+
+
+						//codeMirrorRef.options.readOnly = false;
+						//componentRef.editDialogFired = true;
+					}
+				});
+			}else{
+				this.notifService.warning("Sin permisos para eliminar el archivo");
+			}
+		}else{
+			this.notifService.warning("Archivo no seleccionado");
+		}
+	}
+	seleccionarDirectorioAMover(){
+		if(this.archivoSeleccionado){
+			//Si el archivo es del alumno lo puedo eliminar. 
+			//(No se controla por creador dado que los compartidos mantienen este atributo)
+			if(this.archivos.some(arch => arch.id == this.archivoSeleccionado.id)){
+				var that = this;
+				let disposable = this.dialogService.addDialog(SeleccionarDirectorioMove, {
+					archivos:that.tree,
+					directorioActual:that.directorioActual,
+					nombre:that.archivoSeleccionado.nombre, 
+					directorio:that.archivoSeleccionado.directorio,
+					parent :that})
+				.subscribe((isConfirmed)=>{
+
+					if(isConfirmed) {
+
+
+					}
+				});
+			}else{
+				this.notifService.warning("Sin permisos para mover el archivo");
+			}
+		}else{
+			this.notifService.warning("Archivo no seleccionado");
+		}
+	}
+
+	recargarArchivos(idDirectorioActual){
+		let cedula = this.authService.getUser().cedula;
+		this.loading = true;
+		this.haskellService.getArchivos(cedula)
+		.subscribe(
+			archivos => {
+				this.archivos = archivos;
+				this.loading = false;
+				this.buildTreeFromList_setearDirectorioActual(idDirectorioActual);
+
+			}, 
+			error => console.log(error) 
+			);
+	}
+
+
+	navBack(){
+		var that =this;
+		if(this.directorioActual.padreId!==-1){
+			var padre = this.archivos.filter(function(a){return a.id===that.directorioActual.padreId})[0];
+			this.directorioActual = padre;	
+		}
+		
+	}
+	
+	setSoloLectura = function(arch: Archivo){
+
+		this.archivoSeleccionado.editable = !this.archivoSeleccionado.editable;
+		this.haskellService.editarArchivo(this.archivoSeleccionado.id,this.archivoSeleccionado)
+		.subscribe(
+			archivo => {
+				console.log("Archivo modificado");
+
+			}, 
+			error => {
+				this.notifService.error(error);
+			});
+		
+	}
+	
+	cargarArchivo(){
+		if(this.archivoSeleccionado){
+			if(this.archivoSeleccionado.directorio){
+				this.notifService.warning('No se seleccionó ningún archivo',false);
+			}else{
+				//Si el archivo es compartido con el grupo, editabe y no lo he editado, lo voy a buscar al servidor. 
+				if(this.archivosCompartidos.some(arch => arch.id == this.archivoSeleccionado.id) && this.archivoSeleccionado.editable && this.archivoSeleccionado.archivoOrigenId == -1){
+					if(this.hayArchivoMio()){
+						this.seleccionarArchivoMio();
+						this.sessionService.setArchivo(this.archivoSeleccionado);
+						this.router.navigate(['/matefun']);
+					}else{
+						let cedula = this.authService.getUser().cedula;
+						this.haskellService.getCopiaArchivoCompartidoGrupo(cedula,this.archivoSeleccionado.id).subscribe(
+							archivo => {
+								this.sessionService.setArchivo(archivo);
+								this.router.navigate(['/matefun']);
+							},
+							error =>{
+								console.log(error);
+							});
+					}
+				}else{
+					this.sessionService.setArchivo(this.archivoSeleccionado);
+					this.router.navigate(['/matefun']);
+				}
+			}
+		}else{
+			this.notifService.warning("Archivo no seleccionado");
+		}
+	}
+
+	confirmarEntrega(){
+		let disposable = this.dialogService.addDialog(ConfirmComponent, {
+			title:'Entregar archivo', 
+			message:'¿Desea entregar el archivo "'+this.archivoSeleccionado.nombre+'"?\nNo se podrá editar luego de la entrega.',
+			confirmText: 'Entregar',
+			cancelText: 'Cancelar'
+		})
+		.subscribe((isConfirmed)=>{
+			if(isConfirmed) {
+				this.entregarArchivo();
+			}
+		});
+	}
+
+	entregarArchivo(){
+		this.archivoSeleccionado.estado = 'Entregado';
+		this.haskellService.editarArchivo(this.archivoSeleccionado.id, this.archivoSeleccionado)
+		.subscribe(
+			archivo => {
+				this.archivoSeleccionado = archivo;                                
+			}, 
+			error => {
+				this.notifService.error(error);
+			});
+	}
+
+	buildTreeFromList_setearDirectorioActual(idDirectorioActual){
+		var archivos = this.archivos;
+		this.sessionService.setArchivosList(archivos);
+		var root :Archivo;
+		
+		for(var a in archivos){
+			var arch = archivos[a];
+			if(arch.padreId===-1){
+				root = arch;
+			} 
+		}
+		this.idRecorridos = [root.id];
+		var archivos2 = archivos.filter(
+			function(a){
+				return a.id!==root.id;
+			}
+			);
+		var directorioActual = this.archivos.filter(function(a){return a.id===idDirectorioActual})[0];
+		var tree = this.buildTree(archivos2,root);
+		this.tree = tree;
+		this.directorioActual = directorioActual;
+		this.ordenarArchivos();
+		this.sessionService.setArchivosTree(tree);
+	}
+
+	buildTreeFromList (){
+		
+		var archivos = this.archivos;
+		this.sessionService.setArchivosList(archivos);
+		var root :Archivo;
+		
+		for(var a in archivos){
+			var arch = archivos[a];
+			if(arch.padreId===-1){
+				root = arch;
+			} 
+		}
+		this.idRecorridos = [root.id];
+		var archivos2 = archivos.filter(
+			function(a){
+				return a.id!==root.id;
+			}
+			);
+		var tree = this.buildTree(archivos2,root);
+		this.tree = tree;
+		this.directorioActual = tree;
+		this.ordenarArchivos();
+		this.sessionService.setArchivosTree(tree);
+	}
+
+
+	buildTree(archivos, root){
+		root.archivos = this.getArchivos(root.id,archivos);
+		for(var a in root.archivos){
+			if(root.archivos[a].directorio && this.idRecorridos[root.archivos[a].id] === undefined){
+				var id = root.archivos[a].id;
+				var archivos2 = archivos.filter(function(a){return a.id!==id});
+				root.archivos[a] = this.buildTree(archivos2 ,root.archivos[a]);
+			}
+		}
+		return root;
+	}
+
+	getArchivos(id,archivos){
+		return archivos.filter(
+			function(a){
+				return a.padreId === id;
+			}
+			);
+	}
+
+	cantArchivos(idPadre,archivos){
+		return archivos.filter(function(a){a.padreId ===idPadre;}).length;
+	}
+
+	elem(id,archivos){
+		if(archivos===[]){
+			return false;
+		}else {
+			return archivos.filter(
+				function(a){
+					a.id ===id;
+				}).length>0;
+		}
+	}
+
+	mkdir(){
+		var that = this;
+		let disposable = this.dialogService.addDialog(NuevoArchivo, {
+			nombre:'', 
+			descripcion:'',
+			esDirectorio:true,
+			parentContext :that})
+		.subscribe((isConfirmed)=>{
+
+			if(isConfirmed) {
+
+
+				//codeMirrorRef.options.readOnly = false;
+				//componentRef.editDialogFired = true;
+			}
+		});
+	}
+	mkFile(){
+		var that = this;
+		let disposable = this.dialogService.addDialog(NuevoArchivo, {
+			nombre:'', 
+			descripcion:'',
+			esDirectorio:false,
+			parentContext :that})
+		.subscribe((isConfirmed)=>{
+
+			if(isConfirmed) {
+
+
+				//codeMirrorRef.options.readOnly = false;
+				//componentRef.editDialogFired = true;
+			}
+		});
+	}
+
+	seleccionarArchivo = function(arch: Archivo){
+		if(arch.directorio){
+			this.directorioActual=arch;
+		}else {
+			this.sessionService.setDirectorioActual(this.directorioActual);
+			this.sessionService.cargarDependencias(arch);
+		}
+		this.archivoSeleccionado = arch;	
+		this.preview = arch.contenido;
+		this.ordenarArchivos();
+	}
+
+	compartirArchivo(){
+		if(this.archivoSeleccionado){
+			var grupos = this.sessionService.getGrupos();
+			if(grupos == undefined){
+				this.haskellService.getGrupos(this.authService.getUser().cedula)
+				.subscribe(
+					gruposRest => {
+						this.sessionService.setGrupos(grupos);
+						this.dialogService.addDialog(CompartirArchivoComponent, {
+							grupos:gruposRest, 
+							archivo:this.archivoSeleccionado,
+							parent:this
+						})
+						.subscribe((isConfirmed)=>{
+							if(isConfirmed) {
+								this.notifService.success("confirmado?");
+							}
+						});
+
+
+					},
+					error => {
+
+					})
+			}else{
+				this.dialogService.addDialog(CompartirArchivoComponent, {
+					grupos:grupos, 
+					archivo:this.archivoSeleccionado,
+					parent:this
+				})
+				.subscribe((isConfirmed)=>{
+					if(isConfirmed) {
+						this.notifService.success("confirmado?");
+					}
+				});
+			}
+		}else{
+			this.notifService.warning("Archivo no seleccionado");
+		}
+
+	}
+
+	hayArchivoOriginal(){
+		return !this.archivoSeleccionado.directorio && this.archivosCompartidos.some(a => a.id == this.archivoSeleccionado.archivoOrigenId);
+	}
+
+	seleccionarArchivoOriginal(){
+		this.archivoSeleccionado = this.archivosCompartidos.find(arch => arch.id == this.archivoSeleccionado.archivoOrigenId);
+		this.preview = this.archivoSeleccionado.contenido;
+	}
+
+	hayArchivoMio(){
+		return !this.archivoSeleccionado.directorio && this.archivosCompartidos.some(a => a.archivoOrigenId == this.archivoSeleccionado.id);	
+	}
+
+	seleccionarArchivoMio(){
+		this.archivoSeleccionado = this.archivosCompartidos.find(a => a.archivoOrigenId == this.archivoSeleccionado.id);
+		this.preview = this.archivoSeleccionado.contenido;
+	}
+
+	verCalificacion(){
+		this.dialogService.addDialog(VerCalificacionComponent, {
+			archivo:this.archivoSeleccionado
+		}).subscribe((isConfirmed)=>{
+			if(isConfirmed) {
+				this.notifService.success("confirmado?");
+			}
+		});
+	}
+
+
+	
+
+}
diff --git a/src/app/layout/archivos/archivos.module.ts b/src/app/layout/archivos/archivos.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..757246bce89ac97b6005a9447db97e71147f85a2
--- /dev/null
+++ b/src/app/layout/archivos/archivos.module.ts
@@ -0,0 +1,33 @@
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ArchivosComponent } from './archivos.component';
+import { CommonModule } from '@angular/common';
+import { ArchivosRoutingModule } from './archivos-routing.module';
+import { FilterPipe } from '../../shared/pipes/filter.pipe';
+import { ConfirmComponent } from '../../shared/modal/confirm.component'; 
+import { NuevoArchivo } from './nuevoArchivo.component';
+import { VerCalificacionComponent } from './verCalificacion.component';
+import { CompartirArchivoComponent } from './compartirArchivo.component';
+import { SeleccionarDirectorioMove } from './seleccionarDirectorio.component';
+import { ConfirmarEliminar } from './confirmarEliminar.component';
+import { DialogService } from "ng2-bootstrap-modal";
+import { BootstrapModalModule } from 'ng2-bootstrap-modal';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { CodemirrorModule } from 'ng2-codemirror';
+import { NotificacionModule } from '../../notificacion/notificacion.module'; 
+
+@NgModule({
+    imports: [CommonModule, ArchivosRoutingModule, FormsModule,BootstrapModalModule, NgbModule, CodemirrorModule,NotificacionModule],
+    declarations: [ArchivosComponent, FilterPipe,NuevoArchivo, VerCalificacionComponent, ConfirmComponent, CompartirArchivoComponent,ConfirmarEliminar, SeleccionarDirectorioMove],
+    entryComponents: [
+        NuevoArchivo, 
+        VerCalificacionComponent,
+        ConfirmComponent,
+        CompartirArchivoComponent,
+        ConfirmarEliminar,
+        SeleccionarDirectorioMove
+    ],
+    exports: [ArchivosComponent]
+})
+
+export class ArchivosModule { }
diff --git a/src/app/layout/archivos/compartirArchivo.component.ts b/src/app/layout/archivos/compartirArchivo.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..02b5187edc781c1f383b9887da049e1cc53e6210
--- /dev/null
+++ b/src/app/layout/archivos/compartirArchivo.component.ts
@@ -0,0 +1,64 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo } from '../../shared/objects/archivo';
+import { Grupo } from '../../shared/objects/grupo';
+
+export interface ConfirmModel {
+  archivo: Archivo;
+  grupos:any;
+  parent:any;
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                     <h6 class="modal-title pull-lefth">Compartir &quot;{{archivo.nombre}}&quot; con:</h6> 
+                     <button type="button" class="close" (click)="close()" style="margin-rigth:8px;">&times;</button>
+                   </div>
+                   <div class="modal-body" style="height:350px;overflow-y: scroll;">
+                       <div>
+                         <div class="list-group">
+                            <button *ngFor="let g of grupos" type="button" (click)="seleccionarGrupo(g)" style="cursor:pointer" class="list-group-item list-group-item-action" [ngClass]="{'active':grupo!=undefined && g.grado == grupo.grado && g.grupo == grupo.grupo && g.anio == grupo.anio && g.liceoId == grupo.liceoId}">
+                                  <i class="fa fa-group" style="margin-right:10px; font-size: 3em; cursor: pointer;" aria-hidden="true" ></i>
+                                 {{g.grado+"°"+g.grupo+" - "+g.anio}}
+                            </button>
+                          </div>
+                        </div>
+                     </div>
+                   <div class="modal-footer">
+                     <button type="button" class="btn btn-primary" (click)="compartir()">Compartir</button>
+                   </div>
+                 </div>
+              </div>`
+})
+export class CompartirArchivoComponent extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  archivo: Archivo;
+  grupos:any;
+  grupo: Grupo;
+  parent: any;
+  
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+
+  seleccionarGrupo(grupo){
+    this.grupo = grupo;
+  }
+
+  compartir(){
+    if(this.grupo){
+      this.parent.haskellService.compartirArchivoGrupo(this.grupo, this.archivo.id)
+                .subscribe(
+                    success => {
+                        this.parent.notifService.success("Archivo compartido");
+                        this.close();
+                    }, 
+                    error => {
+                        this.parent.notifService.error(error);
+                    });
+    }else{
+      this.parent.notifService.error("Seleccione un grupo");
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/app/layout/archivos/confirmarEliminar.component.ts b/src/app/layout/archivos/confirmarEliminar.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1cb8755e497f4ecedc0064e2acd48ec16f5c1402
--- /dev/null
+++ b/src/app/layout/archivos/confirmarEliminar.component.ts
@@ -0,0 +1,64 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo } from '../../shared/objects/archivo';
+export interface ConfirmModel {
+  nombreArchivo:string;
+  esDirectorio:boolean;
+  parentContext: any;
+
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                      <h6 class="modal-title" *ngIf="!esDirectorio">Eliminar archivo</h6> 
+                      <h6 class="modal-title" *ngIf="esDirectorio">Eliminar carpeta</h6> 
+                      <button type="button" class="close" (click)="close()" style="margin-left:8px;">&times;</button>
+                   </div>
+                   <div class="modal-body">
+                        <p *ngIf="!esDirectorio">¿Está seguro que desea eliminar el archivo {{nombreArchivo}}?</p>
+                        <p *ngIf="esDirectorio">¿Está seguro que desea eliminar la carpeta {{nombreArchivo}}?</p>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-default" (click)="close()">Cancelar</button>
+                    <button type="button" class="btn btn-success" (click)="confirmarEliminar()">Eliminar</button>
+                  </div>
+                 </div>
+              </div>`
+})
+export class ConfirmarEliminar extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  nombreArchivo: string;
+  esDirectorio: boolean;
+  parentContext: any;
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+   confirmarEliminar(){
+      var that = this.parentContext;
+      var directorio = this.parentContext.archivoSeleccionado.directorio;
+      this.parentContext.archivoSeleccionado.eliminado = true;
+      if(directorio){
+        delete this.parentContext.archivoSeleccionado['archivos'];
+      }
+      this.parentContext.haskellService.editarArchivo(this.parentContext.archivoSeleccionado.id,this.parentContext.archivoSeleccionado)
+      .subscribe(
+        archivo => {
+          console.log("Archivo eliminado");
+          if(directorio){
+            var idDirActual = that.directorioActual.padreId;
+          }else {
+            var idDirActual = that.directorioActual.id;
+          }
+          that.recargarArchivos(idDirActual);
+          that.archivoSeleccionado = null;  
+          
+        }, 
+        error => {
+          this.parentContext.notifService.error(error);
+        });
+      this.close();
+      
+    }
+  
+}
\ No newline at end of file
diff --git a/src/app/layout/archivos/index.ts b/src/app/layout/archivos/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ca9d5018ad6489d2ad15d7e146a5b152c6138aff
--- /dev/null
+++ b/src/app/layout/archivos/index.ts
@@ -0,0 +1,4 @@
+/**
+ * This barrel file provides the export for the lazy loaded BlankpageComponent.
+ */
+export * from './archivos.component';
\ No newline at end of file
diff --git a/src/app/layout/archivos/nuevoArchivo.component.ts b/src/app/layout/archivos/nuevoArchivo.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d51363e99c4abb73e8d37bfed3804858bab34ed0
--- /dev/null
+++ b/src/app/layout/archivos/nuevoArchivo.component.ts
@@ -0,0 +1,85 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo } from '../../shared/objects/archivo';
+export interface ConfirmModel {
+  nombre:string;
+  descripcion:string;
+  parentContext: any;
+  esDirectorio:boolean;
+
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+
+                   <div class="modal-header">
+                      <h6 class="modal-title" *ngIf="esDirectorio">Nueva carpeta</h6> 
+                      <h6 class="modal-title" *ngIf="!esDirectorio">Nuevo archivo</h6> 
+                      <button type="button" class="close" (click)="close()" style="margin-left:8px;">&times;</button>
+                   </div>
+                   
+                   <div class="modal-body">
+                    <form>
+                      <div class="form-group">
+                        <label for="recipient-name" class="form-control-label">Nombre:</label>
+                        <input type="text" class="form-control" id="recipient-name" [(ngModel)]="nombre" [ngModelOptions]="{standalone: true}" >
+                      </div>
+                      <div class="form-group" *ngIf="esDirectorio">
+                        <label for="message-text" class="form-control-label">Descripción:</label>
+                        <textarea class="form-control" id="message-text" [(ngModel)]="descripcion" [ngModelOptions]="{standalone: true}" ></textarea>
+                      </div>
+                    </form>
+                  </div>
+
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-success" (click)="confirm()">Crear</button>
+                  </div>
+
+                 </div>
+              </div>`
+})
+export class NuevoArchivo extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  nombre: string;
+  esDirectorio: boolean;
+  descripcion: string;
+  parentContext: any;
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+  confirm() {
+    var nombre = this.nombre;
+    var desc = this.descripcion;
+    var archivo : Archivo;
+    archivo = new Archivo();
+    archivo.cedulaCreador = this.parentContext.directorioActual.cedulaCreador;
+    if(this.esDirectorio){
+      archivo.contenido = desc;
+    } else {
+      archivo.contenido = '';
+    }
+    archivo.directorio = this.esDirectorio;
+    archivo.editable = true;
+    archivo.fechaCreacion  = new Date();
+    archivo.nombre = nombre;
+    archivo.padreId =  this.parentContext.directorioActual.id;
+    var that = this.parentContext;
+    var regex = /^[A-Z]/
+    if(regex.test(nombre)){
+      this.parentContext.haskellService.crearArchivo(archivo).subscribe(
+                      archivo => {
+                        var id = that.directorioActual.id;
+                        that.recargarArchivos(id);        
+                      }, 
+                      error => {
+                          this.parentContext.notifService.error(error);
+                      });
+      
+
+      this.close();
+    }else{
+      alert("Nombre de archivo debe iniciar con mayusula.")
+    }
+  }
+  
+}
\ No newline at end of file
diff --git a/src/app/layout/archivos/seleccionarDirectorio.component.ts b/src/app/layout/archivos/seleccionarDirectorio.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ca43a236fd7d186c80f8a4a36451e61485ec3c6
--- /dev/null
+++ b/src/app/layout/archivos/seleccionarDirectorio.component.ts
@@ -0,0 +1,85 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo } from '../../shared/objects/archivo';
+export interface ConfirmModel {
+  directorio: boolean;
+  archivos:any;
+  directorioActual:any;
+  parent:any;
+  nombre:string;
+
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                     <h6 class="modal-title pull-lefth">¿Dónde quieres mover el archivo?</h6> 
+                     <button type="button" class="close" (click)="close()" style="margin-rigth:8px;">&times;</button>
+                   </div>
+                   <div class="modal-body" style="height:350px;overflow-y: scroll;">
+                       <div>
+                         <div class="list-group" >
+                            <button *ngFor="let arch of directorioActual.archivos" type="button" (click)="navToDir(arch)" style="cursor:pointer" class="list-group-item list-group-item-action">
+                                  <i *ngIf="arch.directorio" class="fa fa-folder" style="margin-right:10px; font-size: 3em; cursor: pointer;" aria-hidden="true" ></i>
+                                  <i *ngIf="!arch.directorio" class="fa fa-file-text" style="margin-right:10px;font-size: 3em; cursor: pointer;" aria-hidden="true"></i>
+                                 {{arch.nombre}}
+                             </button>
+                          </div>
+                        </div>
+                     </div>
+                   <div class="modal-footer">
+                     <button type="button" class="btn btn-default" (click)="navBack()">Atras</button>
+                     <button type="button" class="btn btn-primary" (click)="move()">Mover aquí</button>
+                   </div>
+                 </div>
+              </div>`
+})
+export class SeleccionarDirectorioMove extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  directorio: boolean;
+  archivos:any;
+  directorioActual:any;
+  parent:any;
+  nombre:string;
+
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+  move() {
+    // we set dialog result as true on click on confirm button, 
+    // then we can get dialog result from caller code 
+    var that = this;
+    if(this.nombre!==undefined && this.nombre!==""){
+       this.parent.archivoSeleccionado.padreId = this.directorioActual.id;
+       if(this.parent.archivoSeleccionado.directorio){
+         delete this.parent.archivoSeleccionado['archivos'];
+       }
+       this.parent.haskellService.editarArchivo(this.parent.archivoSeleccionado.id,this.parent.archivoSeleccionado)
+      .subscribe(
+        archivo => {
+          
+          that.parent.recargarArchivos(this.directorioActual.id);
+          that.parent.archivoSeleccionado = null;  
+          
+        }, 
+        error => {
+          this.parent.notifService.error(error);
+        });
+      
+    }
+    this.close();
+  }
+
+  navToDir(arch){
+    if(arch.directorio){
+      this.directorioActual = arch;  
+    }
+  }
+
+  navBack(){
+    var idPadre = this.directorioActual.padreId;
+    var archivosList = this.parent.sessionService.getArchivosList();
+    var nuevoDirectorioActual = archivosList.filter(function(a){return a.id===idPadre})[0];
+    this.directorioActual=nuevoDirectorioActual;
+  }
+}
\ No newline at end of file
diff --git a/src/app/layout/archivos/verCalificacion.component.ts b/src/app/layout/archivos/verCalificacion.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d8a438dee6482b284204af452ee57aa593478bb3
--- /dev/null
+++ b/src/app/layout/archivos/verCalificacion.component.ts
@@ -0,0 +1,35 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo, Evaluacion } from '../../shared/objects/archivo';
+
+export interface ConfirmModel {
+  archivo: Archivo;
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                     <h6 class="modal-title pull-lefth">Calificación &quot;{{archivo.nombre}}&quot;:</h6> 
+                     <button type="button" class="close" (click)="close()" style="margin-rigth:8px;">&times;</button>
+                   </div>
+                   <div class="modal-body">
+                       <div>
+                         <label><b>Fecha: </b></label>&nbsp; {{archivo.evaluacion.fecha | date}}<br>
+                         <label><b>Nota (1-100): </b></label>&nbsp; {{archivo.evaluacion.nota}}<br>
+                         <label><b>Detalle: </b></label>&nbsp; {{archivo.evaluacion.descripcion}}
+                       </div>
+                   </div>
+                   <div class="modal-footer">
+                     <button type="button" class="btn btn-primary" (click)="close()">Cerrar</button>
+                   </div>
+                 </div>
+              </div>`
+})
+export class VerCalificacionComponent extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  archivo: Archivo;
+
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+}
\ No newline at end of file
diff --git a/src/app/layout/canvas/canvas.component.html b/src/app/layout/canvas/canvas.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7495fcb82f1093ee84279f510a36f9fb776b4ec6
--- /dev/null
+++ b/src/app/layout/canvas/canvas.component.html
@@ -0,0 +1,49 @@
+<div class="card">
+<!-- 	<div class="card-header">
+		<label>Gráficas</label>
+	</div>  -->
+	<div class="card-block contenedor-canvas" >
+		<button placement="bottom" [ngbPopover]=popoverTipoZoom  triggers="mouseenter:mouseleave:click" data-placement="bottom" class="btn btn-sm btn-secondary" style="float:right; margin-right: 198px; margin-top: -55px" (click)=cambiarTipoZoom() >{{tipoZoom}}</button>
+		<ng-template #popoverTipoZoom>
+			{{tipoZoomDesc}}
+		</ng-template>
+		<button ngbPopover="Zoom +"  triggers="mouseenter:mouseleave" data-placement="bottom" class="btn btn-sm btn-secondary" style="float:right; margin-right: 165px; margin-top: -55px" (click)=zoomMas() ><i class="fa fa-plus"></i></button>
+		<button ngbPopover="Zoom -"  data-placement="bottom" triggers="mouseenter:mouseleave" class="btn btn-sm btn-secondary" style="float:right; margin-right: 132px; margin-top: -55px" (click)=zoomMenos() ><i class="fa fa-minus"></i></button>
+		<button ngbPopover="Centrar"  triggers="mouseenter:mouseleave" data-placement="bottom" class="btn btn-sm btn-secondary" style=" float:right; margin-right: 99px; margin-top: -55px" (click)="centrarCanvas()" ><i class="fa fa-arrows"></i></button>
+		<button ngbPopover="Borrar"  triggers="mouseenter:mouseleave" data-placement="bottom" class="btn btn-sm btn-secondary" style=" float:right; margin-right: 66px; margin-top: -55px" (click)="limpiarCanvas()" ><i class="fa fa-trash"></i></button>
+
+		<button ngbPopover="Descargar PNG"  triggers="mouseenter:mouseleave" data-placement="bottom" class="btn btn-sm btn-secondary" style="float:right; margin-right: 33px; margin-top: -55px" (click)=exportImg() ><i class="fa fa-download"></i></button>
+		<a id="dl" download="Canvas.png" style="display: none">Download Canvas</a>
+
+		<button class="btn btn-sm btn-secondary" style="float:right; margin-top: -55px" id="popover"  placement="bottom" [ngbPopover]=popoverCanvas popoverTitle="Configuración" #popover="ngbPopover" tiggers="click">
+			<i class="fa fa-gear"></i>
+		</button>
+		<ng-template #popoverCanvas style="width: 15em">
+			<div style="width: 8em">
+				<div class="form-group">
+					<label>
+						<input type="checkbox" style="width: 15px; display: inline-block;" name="evaluacionVertical" class="form-control form-control-sm" [checked]=evaluacionVertical (click)="mostrarEvaluacionVertical()">
+						Evaluacion
+					</label>
+					<br>
+					<label>
+						<input type="checkbox" style="width: 15px; display: inline-block;" name="mostrarGrilla" class="form-control form-control-sm" [checked]=mostrarGrilla (click)="mostrarOcultarGrilla()">
+						Grilla
+					</label>
+					<br>
+					<label>
+						<input type="checkbox" style="width: 15px; display: inline-block;" name="mostrarEjes" class="form-control form-control-sm" [checked]=mostrarEjes (click)="mostrarOcultarEjes()">
+						Ejes
+					</label>
+				</div>
+			</div>
+		</ng-template>
+		
+		<button class="btn btn-sm btn-secondary" data-placement="bottom" *ngIf="!animando && elementosAnimacion.length>0" style=" float:left; margin-top: -5px; margin-right: 5px" (click)="play()" ><i class="fa fa-play"></i></button>
+		<button class="btn btn-sm btn-secondary" data-placement="bottom" *ngIf="animando && elementosAnimacion.length>0" style=" float:left; margin-top: -5px; margin-right: 5px" (click)="pause()" ><i class="fa fa-pause"></i></button>
+		<ngb-progressbar style="float: none" *ngIf="elementosAnimacion.length>0" type="info" [value]="((frameAnimacion+1)/elementosAnimacion.length)*100"></ngb-progressbar>
+		<canvas #canvasElement width="2000" height="2000" style="max-width: 100%;" (mousemove)="moveGraph($event)" (touchmove)="moveGraph($event)" (touchstart)="moveGraph($event)" (touchend)="moveGraph($event)" (mouseleave)="leaveCanvas($event)" (mousewheel)="zoomGraph($event)" (DOMMouseScroll)="zoomGraph($event)" id="myCanvas">
+		</canvas>
+		
+	</div>
+</div>
\ No newline at end of file
diff --git a/src/app/layout/canvas/canvas.component.ts b/src/app/layout/canvas/canvas.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab6563203bded6b9192412fe7f2d23b8dda77d99
--- /dev/null
+++ b/src/app/layout/canvas/canvas.component.ts
@@ -0,0 +1,1063 @@
+import { Component, ViewChild, HostListener, ComponentRef, ElementRef, Input, Output} from '@angular/core';
+import { NgbPopoverConfig, NgbPopover} from '@ng-bootstrap/ng-bootstrap';
+import { NgbPopoverWindow } from '@ng-bootstrap/ng-bootstrap/popover/popover';
+import { GHCIService } from '../../shared/services/ghci.service'; 
+@Component({
+    moduleId: module.id,
+    selector: 'canvas-component',
+    templateUrl: './canvas.component.html',
+    host: {
+        '(window:resize)': 'onResize($event)'
+    }
+})
+
+export class CanvasComponent {
+    //addEventListener('mousemove', tellPos, false);
+    public constructor(private ghciService: GHCIService) {
+        ghciService.messages.subscribe(
+            canvas=>{
+                this.limpiarCanvas();
+                if(canvas.tipo == 'canvas'){
+                    this.objetos = JSON.parse(canvas.resultado);
+                    this.elementosAnimacion = [];
+                    this.frameAnimacion = 0;
+
+                    this.dibujarObjetos();
+                }else if (canvas.tipo == 'animacion'){
+                    this.elementosAnimacion = canvas.resultado.map(res => JSON.parse(res));
+                    this.frameAnimacion = 0;
+                    this.animando = true;
+                    this.animar();
+                }else if (canvas.tipo == 'graph'){
+                    var jsonCanvas = JSON.parse(canvas.resultado);
+                    var fun = this.generarFuncion(jsonCanvas);
+                    this.objetos = [{tipo:'grafica',ecuacion: eval(fun), color:'black',thickness: 2}]
+                    this.dibujarObjetos();
+                }
+            }
+            ,
+            error=>{
+                this.objetos = [];
+            })
+    }
+
+    objetos:any = [];
+    evaluacionVertical:boolean = true;
+    elementosAnimacion:any = [];
+    frameAnimacion:number = 0;
+    animando: boolean = true;
+    mostrarEjes: boolean = true;
+    mostrarGrilla: boolean = true;
+    //Todo, abscisa y ordenada
+    tipoZoom = "Todo";
+    tipoZoomDesc = "Zoom en ambos ejes";
+
+
+
+    @ViewChild('canvasElement') canvasRef: ElementRef;
+
+    @ViewChild('popover') popover: NgbPopover;
+
+    onResize(event){
+        if(this.canvasRef.nativeElement.offsetParent){
+            var pixelRatio = window.devicePixelRatio || 1;
+            if(pixelRatio > 2){
+                pixelRatio = 2;
+            }else if (pixelRatio < 1){
+                pixelRatio = 1;
+            }
+            
+            this.canvasRef.nativeElement.width = this.canvasRef.nativeElement.offsetParent.offsetWidth * pixelRatio * 0.94;
+            this.canvasRef.nativeElement.height = this.canvasRef.nativeElement.offsetParent.offsetHeight * pixelRatio * 0.94;
+            var relacionAspecto = this.canvasRef.nativeElement.width/this.canvasRef.nativeElement.height;
+            this.Graph(relacionAspecto);
+            this.dibujarObjetos();
+        }
+    }
+
+    exportImg(){
+        function dlCanvas() {
+          var canvas :any;
+          canvas = document.getElementById('myCanvas');
+          var dt = canvas.toDataURL('image/png');
+          /* Change MIME type to trick the browser to downlaod the file instead of displaying it */
+          dt = dt.replace(/^data:image\/[^;]*/, 'data:application/octet-stream');
+
+          /* In addition to <a>'s "download" attribute, you can define HTTP-style headers */
+          dt = dt.replace(/^data:application\/octet-stream/, 'data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=Canvas.png');
+
+          this.href = dt;
+        };
+        document.getElementById("dl").addEventListener('click', dlCanvas, false);
+        document.getElementById("dl").click();
+    }
+    ngAfterViewInit() {
+
+        // Make it visually fill the positioned parent
+        this.canvasRef.nativeElement.width = this.canvasRef.nativeElement.offsetParent.offsetWidth * 0.94;
+        this.canvasRef.nativeElement.height = this.canvasRef.nativeElement.offsetParent.offsetHeight * 0.94;
+        var relacionAspecto = this.canvasRef.nativeElement.width/this.canvasRef.nativeElement.height;
+        this.Graph(relacionAspecto);
+
+        // this.objetos.push({tipo:'rect',x:0,y:0,w:5,h:1, d:90});
+        // this.objetos.push({tipo:'circulo',x:1,y:1,r:4});
+        // this.objetos.push({tipo:'circulo',x:1,y:0,r:2});
+        // this.objetos.push({tipo:'grafica',ecuacion:(x)=>Math.exp(x), color:'red',thickness: 2});
+        // this.objetos.push({tipo:'grafica',ecuacion:(x)=>x*x*x/20, color:'green',thickness: 2});
+
+        // this.objetos.push({tipo:'grafica',ecuacion:(x)=>Math.sin(1/x), color:'blue',thickness: 2});
+        // this.objetos.push({tipo:'rect',x:8,y:0,w:5,h:1, d:45});
+        // this.drawCircle(1,1,4);
+        // this.drawCircle(1,1,2);
+        // this.drawEquation((x)=>x*x,"black",2);
+        this.dibujarObjetos();
+    }
+
+    @HostListener('document:click', ['$event'])
+    private documentClicked(event: MouseEvent): void {
+
+        // Popover is open
+        if (this.popover && this.popover.isOpen()) {
+
+            // Not clicked on self element
+            if (!(this.popover as any)._elementRef.nativeElement.contains(event.target)) {
+
+                // Hacking typescript to access private member
+                const popoverWindowRef: ComponentRef<NgbPopoverWindow> = (this.popover as any)._windowRef;
+
+                // If clicked outside popover window
+                if (!popoverWindowRef.location.nativeElement.contains(event.target)) {
+                    this.popover.close();
+                }
+            }
+        }
+    }
+
+    play(){
+        this.animando = true;
+        this.animar();
+    }
+
+    pause(){
+        this.animando = false;
+    }
+
+    private mostrarOcultarEjes = function(){
+        this.mostrarEjes = !this.mostrarEjes;
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        this.dibujarObjetos();
+    }
+
+    private mostrarOcultarGrilla = function(){
+        this.mostrarGrilla = !this.mostrarGrilla;
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        this.dibujarObjetos();
+    }
+
+    private mostrarEvaluacionVertical = function(){
+        this.evaluacionVertical = !this.evaluacionVertical;
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        this.dibujarObjetos();
+    }
+
+    public limpiarCanvas = function(){
+        this.objetos = [];
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        if(this.mostrarEjes || this.mostrarGrilla){
+            this.drawXAxis(this.context);
+            this.drawYAxis(this.context);
+        }
+    }
+
+    public centrarCanvas = function(){
+        this.maxX = 10;
+        this.maxY = 10/this.aspectRatio;
+        this.minX = -10;
+        this.minY = -10/this.aspectRatio;
+    
+        this.rangeX = this.maxX - this.minX;
+        this.rangeY = this.maxY - this.minY;
+
+        this.unitsPerTickX = 1;
+        this.unitsPerTickY = 1;
+
+        this.unitX = this.canvasRef.nativeElement.width / this.rangeX;
+        this.unitY = this.canvasRef.nativeElement.height / this.rangeY;
+        this.centerY = (-this.minY / this.rangeY) * this.canvasRef.nativeElement.height;
+        this.centerX = (-this.minX / this.rangeX) * this.canvasRef.nativeElement.width;
+        this.iteration = (this.maxX - this.minX) / 1000;
+        this.scaleX = this.canvasRef.nativeElement.width / this.rangeX;
+        this.scaleY = this.canvasRef.nativeElement.height / this.rangeY;
+
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        this.dibujarObjetos();
+    }
+
+    public cambiarTipoZoom = function(){
+        //Todo, Abscisa y Ordenada
+        if(this.tipoZoom == "Todo"){
+            this.tipoZoom = "Abscisa";
+            this.tipoZoomDesc = "Zoom en abscisa";
+        }else if (this.tipoZoom == "Abscisa"){
+            this.tipoZoom = "Ordenada";
+            this.tipoZoomDesc = "Zoom en ordenada";
+        }else{
+            this.tipoZoom = "Todo";
+            this.tipoZoomDesc = "Zoom en ambos ejes";
+        }
+    }
+    /*
+    Se genera algo de la forma:
+
+GRAPH:{ "graph":  <nombre de funcion a graficar>, "funs" = [ <la función a graficar y todas las que usa> ] }
+
+Por cada función se genera:
+
+{ "fun" = <nombre de funcion>, "args" : [<nombres de los parámetros>], "bdy" : <expresión> }
+
+
+Las expresiones pueden ser de distintos tipos, eso lo especifica el "kind":
+*/
+
+private generarFuncion = function(graph:any){
+    var funcionString="";
+    var grafica;
+    for(var fun of graph.funs){
+        funcionString = "var "+fun.fun + " = function("+fun.args.join()+"){\n return "+this.generarExpresion(fun.bdy)+"}\n"+funcionString;
+
+        if(fun.fun == graph.graph){
+            funcionString += "return "+fun.fun+"("+fun.args.join()+");\n"
+            grafica = fun;
+        }
+    }
+    funcionString = "("+grafica.args.join()+",delta,hayPunto)=>{\n"+funcionString+"}";
+    return funcionString;
+}
+
+/*
+-condicionales (ej: cnd?exp1:exp2)
+{ "kind" : "cnd", "cond" : <expresión>, "exp1" : <expresión>, "exp2" : <expresión>}
+- operadores binarios (ej: exp1 + exp2)
+{ "kind" : "bop", "op" : <operador>, "exp1" : <expresión>, "exp2" : <expresión> }
+-operadores unarios (ej: - exp)
+{ "kind" : "uop", "op" : <operador>, "exp" : <expresión> }
+-aplicación de funciones (ej: f(3,4))
+{ "kind" : "app", "fun" : <nombre función>, "args" : [ <expresiones> ] }
+-tupla (ej:(2,3,4))
+{ "kind" : "tup", "exps" : [ <expresiones> ] }
+-literales (ej: 1, "tito", etc.)
+{ "kind" : "lit", "val" : <valor> }
+-variables (ej: x)
+{ "kind" : "var", "var" : <nombre de la variable> }
+*/
+
+private generarExpresion = function(exp:any){
+    var expresion = "";
+    if(exp.kind == "cnd"){
+        expresion = " ("+this.generarExpresion(exp.cond)+"?"+this.generarExpresion(exp.exp1)+":"+this.generarExpresion(exp.exp2)+") ";
+    }else if(exp.kind == "bop"){
+        if(exp.op == "=="){
+            expresion = " Math.abs(("+this.generarExpresion(exp.exp1)+") - ("+this.generarExpresion(exp.exp2)+")) < delta && hayPunto() ";
+        }else if (exp.op == "/="){
+            expresion = " Math.abs(("+this.generarExpresion(exp.exp1)+") - ("+this.generarExpresion(exp.exp2)+")) > delta ||  Math.abs(("+this.generarExpresion(exp.exp1)+") - ("+this.generarExpresion(exp.exp2)+")) < delta && !hayPunto()  ";
+        }else{
+            expresion = " ("+this.generarExpresion(exp.exp1)+")"+exp.op+"("+this.generarExpresion(exp.exp2)+") ";
+        }
+    }else if(exp.kind == "uop"){
+        expresion = " "+exp.op+" "+this.generarExpresion(exp.exp)+" ";
+    }else if(exp.kind == "app"){
+        if(exp.fun == 'cos'){
+            exp.fun = 'Math.cos'
+        }else if(exp.fun == 'sen'){
+            exp.fun = 'Math.sin'
+        }else if(exp.fun == 'red'){
+            exp.fun = 'Math.round'
+        }
+        expresion = " "+exp.fun+"("+exp.args.map(e => this.generarExpresion(e)).join()+") ";
+    }else if(exp.kind == "tup"){
+        expresion = " ("+exp.exps.map(e => this.generarExpresion(e)).join()+") ";
+    }else if(exp.kind == "lit"){
+        expresion = " "+exp.val+" ";
+    }else if(exp.kind == "var"){
+        expresion = " "+exp.var+" ";
+    }else{
+        expresion = " undefined ";
+    }
+
+    return expresion;
+}
+
+private animar = function(){
+    if(this.mostrarEjes || this.mostrarGrilla){
+        this.drawXAxis(this.context);
+        this.drawYAxis(this.context);
+    }
+    this.limpiarCanvas();
+    this.objetos = this.elementosAnimacion[this.frameAnimacion];        
+    this.dibujarObjetos();
+    if(this.animando){
+        setTimeout(function(){
+            if(this.animando){
+                this.frameAnimacion ++;
+                if(this.frameAnimacion>=this.elementosAnimacion.length){
+                    this.frameAnimacion = 0;
+                }
+                this.animar();
+            }
+        }.bind(this),1000);
+    }
+}
+
+private dibujarObjetos = function(){
+    if(this.mostrarEjes || this.mostrarGrilla){
+        this.drawXAxis(this.context);
+        this.drawYAxis(this.context);
+    }
+    for(let obj of this.objetos){
+        if(obj.tipo == 'circulo'){
+            this.drawCircle(obj.x, obj.y, obj.r, obj.color,obj.rotacion);
+        }else if(obj.tipo == 'grafica'){
+            this.drawEquation(obj.ecuacion, obj.color,obj.thickness)
+        }else if (obj.tipo == 'rectangulo'){
+            this.drawRect(obj.x,obj.y,obj.w,obj.h,obj.color,obj.rotacion);
+        }else if (obj.tipo == 'texto'){
+            this.drawText(obj.x, obj.y, obj.text, obj.size, obj.color,obj.rotacion);
+        }else if (obj.tipo == 'poligono'){
+            this.drawPolyline(true,obj.puntos,obj.color, obj.rotacion);
+        }else if (obj.tipo == 'lineas'){
+            this.drawPolyline(false,obj.puntos,obj.color, obj.rotacion);
+        }
+    }
+}
+
+public Graph = function(relacionAspecto) {
+    this.config = {
+        canvasId: 'myCanvas',
+        minX: -10,
+        minY: -10/relacionAspecto,
+        maxX: 10,
+        maxY: 10/relacionAspecto,
+        unitsPerTickX: 1,
+        unitsPerTickY: 1
+    }
+    this.aspectRatio = relacionAspecto;
+    this.canvas = this.canvasRef;
+    // this.canvas = document.getElementById(config.canvasId);
+    this.minX = this.config.minX;
+    this.minY = this.config.minY;
+    this.maxX = this.config.maxX;
+    this.maxY = this.config.maxY;
+    this.unitsPerTickX = this.config.unitsPerTickX;
+    this.unitsPerTickY = this.config.unitsPerTickY;
+    // constants
+    this.axisColor = '#aaa';
+    this.font = '8pt Calibri';
+    this.tickSize = 10;
+
+    // relationships
+    this.context = this.canvasRef.nativeElement.getContext('2d');
+
+    this.rangeX = this.maxX - this.minX;
+    this.rangeY = this.maxY - this.minY;
+
+
+    this.unitX = this.canvasRef.nativeElement.width / this.rangeX;
+    this.unitY = this.canvasRef.nativeElement.height / this.rangeY;
+    this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvasRef.nativeElement.height);
+    this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvasRef.nativeElement.width);
+    this.iteration = (this.maxX - this.minX) / 1000;
+    this.scaleX = this.canvasRef.nativeElement.width / this.rangeX;
+    this.scaleY = this.canvasRef.nativeElement.height / this.rangeY;
+};
+
+
+
+private drawXAxis = function(context: any) {
+    context.save();
+    
+    // draw tick marks
+    let xPosIncrement = this.unitsPerTickX * this.unitX;
+    //let xPos, unit;
+    context.font = this.font;
+    context.textAlign = 'center';
+    context.textBaseline = 'top';
+
+    // draw left tick marks
+    let xPos = this.centerX - xPosIncrement;
+    let unit = -1 * this.unitsPerTickX;
+    if(this.mostrarGrilla){
+        context.beginPath();
+        context.strokeStyle = "#EEEEEE";;
+        context.lineWidth = 1;
+        context.moveTo(this.centerX, 0);
+        context.lineTo(this.centerX, this.canvasRef.nativeElement.height);
+        context.stroke();
+    }
+
+    while (xPos > 0) {
+        if(this.mostrarGrilla){
+            context.beginPath();
+            context.strokeStyle = "#EEEEEE";;
+            context.lineWidth = 1;
+            context.moveTo(xPos, 0);
+            context.lineTo(xPos, this.canvasRef.nativeElement.height);
+            context.stroke();
+
+        }
+        if(this.mostrarEjes){
+            context.beginPath();
+            context.strokeStyle = this.axisColor;
+            context.lineWidth = 2;
+            context.moveTo(xPos, this.centerY - this.tickSize / 2);
+            context.lineTo(xPos, this.centerY + this.tickSize / 2);
+            context.stroke();
+            context.fillText(unit + '', xPos, this.centerY + this.tickSize / 2 + 3);
+        }
+        unit = parseFloat((unit-this.unitsPerTickX).toFixed(2));
+        xPos = Math.round(xPos - xPosIncrement);
+    }
+
+    // draw right tick marks
+    xPos = this.centerX + xPosIncrement;
+    unit = this.unitsPerTickX;
+    while (xPos < this.canvas.nativeElement.width) {
+        if(this.mostrarGrilla){
+            context.beginPath();
+            context.strokeStyle = "#EEEEEE";;
+            context.lineWidth = 1;
+            context.moveTo(xPos, 0);
+            context.lineTo(xPos, this.canvasRef.nativeElement.height);
+            context.stroke();
+
+        }
+        if(this.mostrarEjes){
+            context.beginPath();
+            context.strokeStyle = this.axisColor;
+            context.lineWidth = 2;
+            context.moveTo(xPos, this.centerY - this.tickSize / 2);
+            context.lineTo(xPos, this.centerY + this.tickSize / 2);
+            context.stroke();
+            context.fillText(unit + '', xPos, this.centerY + this.tickSize / 2 + 3);
+        }
+        unit = parseFloat((unit+this.unitsPerTickX).toFixed(2));
+        xPos = Math.round(xPos + xPosIncrement);
+    }
+    if(this.mostrarEjes){
+        context.beginPath();
+        context.strokeStyle = this.axisColor;
+        context.lineWidth = 2;
+        context.moveTo(0, this.centerY);
+        context.lineTo(this.canvasRef.nativeElement.width, this.centerY);
+        context.stroke();
+        context.moveTo(this.canvasRef.nativeElement.width, this.centerY);
+        context.lineTo(this.canvasRef.nativeElement.width-12, this.centerY-5);
+        context.stroke();
+        context.moveTo(this.canvasRef.nativeElement.width, this.centerY);
+        context.lineTo(this.canvasRef.nativeElement.width-12, this.centerY+5);
+        context.stroke();
+    }
+    context.restore();
+};
+
+private drawYAxis = function(context: any) {
+    context.save();
+
+    // draw tick marks
+    let yPosIncrement = this.unitsPerTickY * this.unitY;
+    //var yPos, unit;
+    context.font = this.font;
+    context.textAlign = 'right';
+    context.textBaseline = 'middle';
+
+    // draw top tick marks
+    let yPos = this.centerY - yPosIncrement;
+    let unit = this.unitsPerTickY;
+    if(this.mostrarGrilla){
+        context.beginPath();
+        context.strokeStyle = "#EEEEEE";;
+        context.lineWidth = 1;
+        context.moveTo(0, this.centerY);
+        context.lineTo(this.canvasRef.nativeElement.width, this.centerY);
+        context.stroke();
+    }
+    while (yPos > 0) {
+        if(this.mostrarGrilla){
+            context.beginPath();
+            context.strokeStyle = "#EEEEEE";;
+            context.lineWidth = 1;
+            context.moveTo(0, yPos);
+            context.lineTo(this.canvasRef.nativeElement.width, yPos);
+            context.stroke();
+        }
+        if(this.mostrarEjes){
+            context.beginPath();
+            context.strokeStyle = this.axisColor;
+            context.lineWidth = 2;
+            context.moveTo(this.centerX - this.tickSize / 2, yPos);
+            context.lineTo(this.centerX + this.tickSize / 2, yPos);
+            context.stroke();
+            context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
+        }
+        unit = parseFloat((unit+this.unitsPerTickY).toFixed(2));
+        yPos = Math.round(yPos - yPosIncrement);
+    }
+
+    // draw bottom tick marks
+    yPos = this.centerY + yPosIncrement;
+    unit = -1 * this.unitsPerTickY;
+    while (yPos < this.canvasRef.nativeElement.height) {
+        if(this.mostrarGrilla){
+
+            context.beginPath();
+            context.strokeStyle = "#EEEEEE";;
+            context.lineWidth = 1;
+            context.moveTo(0, yPos);
+            context.lineTo(this.canvasRef.nativeElement.width, yPos);
+            context.stroke();
+        }
+        if(this.mostrarEjes){
+            context.beginPath();
+            context.strokeStyle = this.axisColor;
+            context.lineWidth = 2;
+            context.moveTo(this.centerX - this.tickSize / 2, yPos);
+            context.lineTo(this.centerX + this.tickSize / 2, yPos);
+            context.stroke();
+            context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
+        }
+        unit = parseFloat((unit-this.unitsPerTickY).toFixed(2));
+        yPos = Math.round(yPos + yPosIncrement);
+    }
+    if(this.mostrarEjes){
+        context.beginPath();
+        context.strokeStyle = this.axisColor;
+        context.lineWidth = 2;
+        context.moveTo(this.centerX, 0);
+        context.lineTo(this.centerX, this.canvasRef.nativeElement.height);
+        context.stroke();
+        context.moveTo(this.centerX, 0);
+        context.lineTo(this.centerX+5, 12);
+        context.stroke();
+        context.moveTo(this.centerX, 0);
+        context.lineTo(this.centerX-5, 12);
+        context.stroke();
+    }
+    context.restore();
+};
+
+private transformContext = function(context: any) {
+
+    context.translate(this.centerX, this.centerY);
+    // stretch grid to fit the canvas window, and
+    //  invert the y scale so that that increments
+    //  as you move upwards
+
+    context.scale(this.scaleX, -this.scaleY);
+}
+
+public hayGraficas = function(){
+    for(let obj of this.objetos){
+        if(obj.tipo == 'grafica'){
+            return true;
+        }
+    }
+    return false;
+}
+
+public verticalLine = function(x:number, y:number){
+    if(this.hayGraficas()){
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        this.dibujarObjetos();
+        var context = this.context;
+
+        this.context.moveTo(x, 0);
+        this.context.lineTo(x, this.canvasRef.nativeElement.height);
+
+        var minimoX = -(this.centerX / this.scaleX);
+        var minimoY = -(this.centerY / this.scaleY);
+
+        var relativeX = (x/this.canvasRef.nativeElement.width)*this.rangeX + minimoX;
+
+        for(let obj of this.objetos){
+            if(obj.tipo == 'grafica'){
+                var relativeX = Math.trunc(relativeX*100)/100;
+                var interseccion = obj.ecuacion(relativeX,this.rangeX/500, ()=>{return true;});
+                var realY = - ((interseccion+minimoY)/this.rangeY)*this.canvasRef.nativeElement.height;
+                if(obj.color){
+                    this.context.fillStyle = obj.color;
+                }
+                this.context.fillText("("+relativeX.toFixed(2)+","+interseccion.toFixed(2)+")",x+10,realY);
+                this.context.fillStyle = 'black';
+                this.context.fillRect(x-2.5,realY-2.5,5,5);
+            }
+        }
+
+        this.context.stroke();
+    }
+}
+
+public leaveCanvas = function(e:any){
+    this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+    this.dibujarObjetos();
+}
+
+public moveGraph = function(e: any) {
+    
+
+    if (e.buttons === 1 && e.type == 'mousemove') {
+        this.centerX += e.offsetX - this.lastPositionX;
+        this.centerY += e.offsetY - this.lastPositionY;
+        this.minX = -(this.centerX / this.scaleX);
+        this.maxY = (this.centerY / this.scaleY);
+        this.maxX = (this.canvasRef.nativeElement.width / this.scaleX) - (this.centerX / this.scaleX);
+        this.minY = -((this.canvasRef.nativeElement.height / this.scaleY) - (this.centerY / this.scaleY));
+        this.lastPositionX = e.offsetX;
+        this.lastPositionY = e.offsetY;
+        this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+        this.dibujarObjetos();
+    } else if(e.type == 'touchend'){
+        if(e.touches.length==1){
+            this.lastPositionX = e.touches[0].clientX;
+            this.lastPositionY = e.touches[0].clientY;
+        }
+    } else if(e.type == 'touchmove') {
+        if(e.touches.length==1){
+            this.centerX += e.touches[0].clientX - this.lastPositionX;
+            this.centerY += e.touches[0].clientY - this.lastPositionY;
+            this.minX = -(this.centerX / this.scaleX);
+            this.maxY = (this.centerY / this.scaleY);
+            this.maxX = (this.canvasRef.nativeElement.width / this.scaleX) - (this.centerX / this.scaleX);
+            this.minY = -((this.canvasRef.nativeElement.height / this.scaleY) - (this.centerY / this.scaleY));
+            this.lastPositionX = e.touches[0].clientX;
+            this.lastPositionY = e.touches[0].clientY;
+            this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+            this.dibujarObjetos();
+        } else if(this.lastZoom && e.touches.length>1){
+            this.lastPositionX = e.touches[0].clientX;
+            this.lastPositionY = e.touches[0].clientY;
+            var x = e.touches[1].clientX;
+            var y = e.touches[1].clientY;
+            var newZoom = Math.sqrt(Math.pow(this.lastPositionX - x,2)+Math.pow(this.lastPositionY - y,2));
+            if(Math.abs(newZoom - this.lastZoom)>2){
+                //15 es un factor para que el zoom en el dispositivo sea natural. 
+                this.zoom((newZoom-this.lastZoom)/15);
+            }
+            this.lastZoom = newZoom;
+        }
+    } else if(e.type == 'touchstart'){
+        this.lastPositionX = e.touches[0].clientX;
+        this.lastPositionY = e.touches[0].clientY;
+        if(e.touches.length>1){
+            var x = e.touches[1].clientX;
+            var y = e.touches[1].clientY;
+            this.lastZoom = Math.sqrt(Math.pow(this.lastPositionX - x,2)+Math.pow(this.lastPositionY - y,2));
+        }else{
+            this.lastZoom = undefined;
+        }
+    } else {
+        this.lastPositionX = e.offsetX;
+        this.lastPositionY = e.offsetY;
+    }
+
+    if(this.evaluacionVertical){
+        var rect = this.canvasRef.nativeElement.getBoundingClientRect();
+        var x,y;
+        if(e instanceof MouseEvent){
+            x = e.clientX - rect.left;
+            y = e.clientY - rect.top;
+        }else if(e instanceof TouchEvent){
+            x = e.touches[0].clientX - rect.left;
+            y = e.touches[0].clientY - rect.top;
+        }
+        this.verticalLine(x,y);        
+    }
+    
+    if(e instanceof TouchEvent){
+        e.preventDefault();
+    }
+
+}
+
+public zoomGraph = function(e: any) {
+    // cross-browser wheel delta
+    var e = window.event || e; // old IE support
+    var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
+    this.zoom(delta, e.clientX, e.clientY);
+    return false;
+}
+
+public zoomMas = function(){
+    this.zoom(1);
+}
+
+public zoomMenos = function(){
+    this.zoom(-1);
+}
+
+private zoom = function(delta:number, clientX?:number, clientY?:number){
+    var deltaX = delta*this.rangeX / 20;
+    var deltaY = delta*this.rangeY / 20;
+    
+    if ((this.tipoZoom == "Todo" && ((this.rangeX < 0.1 && deltaX > 0 || this.rangeX > 10000 && deltaX < 0)
+        || (this.rangeY < 0.1 && deltaY > 0 || this.rangeY > 10000 && deltaY < 0)))
+        || this.tipoZoom == "Abscisa" && (this.rangeX < 0.1 && deltaX > 0 || this.rangeX > 10000 && deltaX < 0)
+        || this.tipoZoom == "Ordenada" && (this.rangeY < 0.1 && deltaY > 0 || this.rangeY > 10000 && deltaY < 0)) {
+        return;
+    }
+
+    if(clientX && clientY){
+        var rect = this.canvasRef.nativeElement.getBoundingClientRect();
+        var x = clientX - rect.left;
+        var y = clientY - rect.top;
+        var minimoX = -(this.centerX / this.scaleX);
+        var minimoY = -((this.canvasRef.nativeElement.height / this.scaleY) - (this.centerY / this.scaleY));
+        var relativeX = (x/this.canvasRef.nativeElement.width)*this.rangeX + minimoX;
+        var relativeY = (1-y/this.canvasRef.nativeElement.height)*this.rangeY + minimoY;
+        var distRelX = (Math.abs(relativeX-this.minX)/Math.abs(this.maxX-this.minX));
+        var distRelY = (Math.abs(relativeY-this.minY)/Math.abs(this.maxY-this.minY));
+
+        if(this.tipoZoom == "Todo"){
+            this.maxX -= deltaX*(1-distRelX);
+            this.maxY -= deltaY*(1-distRelY);
+            this.minX += deltaX*distRelX;
+            this.minY += deltaY*distRelY;
+            //this.minY = this.maxY - (this.maxX-this.minX)/this.aspectRatio;
+        }else if(this.tipoZoom == "Abscisa"){
+            this.maxX -= deltaX*(1-distRelX);
+            this.minX += deltaX*distRelX;
+        }else{
+            this.maxY -= deltaY*(1-distRelY);
+            this.minY += deltaY*distRelY;
+        }
+    }else{
+        if(this.tipoZoom == "Todo"){
+            this.maxX -= deltaX;
+            this.maxY -= deltaY;
+            this.minX += deltaX;
+            this.minY += deltaY;
+            //this.minY = this.maxY - (this.maxX-this.minX)/this.aspectRatio;
+        }else if(this.tipoZoom == "Abscisa"){
+            this.maxX -= deltaX;
+            this.minX += deltaX;
+        }else{
+            this.maxY -= deltaY;
+            this.minY += deltaY;
+        }
+    }
+
+    this.rangeX = this.maxX - this.minX;
+    this.rangeY = this.maxY - this.minY;
+
+    //this.tickSize = (20 / this.rangeX) * 10;
+    if(this.rangeX >15){
+        this.unitsPerTickX = Math.round(this.rangeX/15);
+    }else if (this.rangeX > 4){
+        this.unitsPerTickX = 1;
+    }else if (this.rangeX > 1.5){
+        this.unitsPerTickX = Math.round((this.rangeX/15)*10)/10;
+    }else if (this.rangeX > 0.4){
+        this.unitsPerTickX = 0.1;
+    }else if (this.rangeX > 0.15){
+        this.unitsPerTickX = Math.round((this.rangeX/15)*100)/100;
+    }else{
+        this.unitsPerTickX = 0.01;
+    }
+
+    
+    if(this.rangeY >15){
+        this.unitsPerTickY = Math.round(this.rangeY/15);
+    }else if(this.rangeY > 4){
+        this.unitsPerTickY = 1;
+    }else if (this.rangeY > 1.5){
+        this.unitsPerTickY = Math.round((this.rangeY/15)*10)/10;
+    }else if (this.rangeY > 0.4){
+        this.unitsPerTickY = 0.1;
+    }else if (this.rangeY > 0.15){
+        this.unitsPerTickY = Math.round((this.rangeY/15)*100)/100;
+    }else {
+        this.unitsPerTickY = 0.01;
+    }
+
+    this.unitX = this.canvasRef.nativeElement.width / this.rangeX;
+    this.unitY = this.canvasRef.nativeElement.height / this.rangeY;
+    this.centerY = (this.maxY / this.rangeY) * this.canvasRef.nativeElement.height;
+    this.centerX = (-this.minX / this.rangeX) * this.canvasRef.nativeElement.width;
+    this.iteration = (this.maxX - this.minX) / 1000;
+    this.scaleX = this.canvasRef.nativeElement.width / this.rangeX;
+    this.scaleY = this.canvasRef.nativeElement.height / this.rangeY;
+
+    this.context.clearRect(0, 0, this.canvasRef.nativeElement.width, this.canvasRef.nativeElement.height);
+    this.dibujarObjetos();
+
+}
+
+private drawCircle: any = function(x: number, y: number, radius: number, color:string, rotation: number) {
+
+    let context = this.context;
+    context.save();
+    context.save();
+    this.transformContext(context);
+
+    context.beginPath();
+    try {
+        context.translate(0, 0);
+        var degree = rotation * Math.PI / 180;
+        var nuevoX = Math.cos(degree)*x-Math.sin(degree)*y;
+        var nuevoY = Math.sin(degree)*x+Math.cos(degree)*y;
+
+        context.rotate(-degree);
+
+        this.context.arc(nuevoX, nuevoY, radius, 0, 2 * Math.PI, false);
+        if(color){
+            context.fillStyle = color;
+            context.fill();
+        }    
+
+    } catch (e) {
+        this.limpiarCanvas();
+    }
+
+    context.restore();
+    context.lineJoin = 'round';
+    context.lineWidth = this.thickness;
+    context.strokeStyle = this.color;
+    context.stroke();
+    context.restore();
+
+}
+
+private drawText: any = function(x: number, y: number, text: string, size:number, color:string, rotation: number) {
+
+    this.context.save();
+    var minimoX = -(this.centerX / this.scaleX);
+    var minimoY = -(this.centerY / this.scaleY);
+
+    var realY = - ((y+minimoY)/this.rangeY)*this.canvasRef.nativeElement.height;
+    var realX = ((x-minimoX)/this.rangeX)*this.canvasRef.nativeElement.width;
+    if(color){
+        this.context.fillStyle = color;
+    }
+
+    this.context.translate(realX, realY);
+    var degree = rotation * Math.PI / 180;
+
+    this.context.rotate(degree);
+
+    this.context.font = (size*100)/this.rangeX+"pt Arial";
+    this.context.textBaseline="middle";
+    this.context.textAlign="center";
+    this.context.fillText(text,0,0);
+    this.context.restore();
+}
+
+private drawRect: any = function(x: number, y: number, width: number, height:number,color:string, rotation:number) {
+
+    let context = this.context;
+    context.save();
+    context.save();
+    this.transformContext(context);
+
+    context.beginPath();
+    try {
+        context.translate(0, 0);
+        var degree = rotation * Math.PI / 180;
+        var nuevoX = Math.cos(degree)*x-Math.sin(degree)*y;
+        var nuevoY = Math.sin(degree)*x+Math.cos(degree)*y;
+
+        context.rotate(-degree);
+
+        context.rect(nuevoX-width/2,nuevoY-height/2,width,height);
+        if(color){
+            context.fillStyle = color;
+            context.fill();
+        } 
+
+        context.translate(this.centerX, this.centerY);
+    } catch (e) {
+        this.limpiarCanvas();
+    }
+
+    context.restore();
+    context.lineJoin = 'round';
+    context.lineWidth = this.thickness;
+    context.strokeStyle = this.color;
+    context.stroke();
+    context.restore();
+}
+
+private drawElipse: any = function(x: number, y: number, radiusX: number, radiusY: number, rotation: number) {
+
+    let context = this.context;
+    this.color = 'green';
+    this.thickness = 3;
+
+    context.save();
+    context.save();
+    this.transformContext(context);
+
+    context.beginPath();
+    try {
+        this.context.ellipse(x, y, radiusX, radiusY, rotation * Math.PI / 180, 0, 2 * Math.PI);
+    } catch (e) {
+        this.limpiarCanvas();
+    }
+    context.restore();
+    context.lineJoin = 'round';
+    context.lineWidth = this.thickness;
+    context.strokeStyle = this.color;
+    context.stroke();
+    context.restore();
+}
+
+private drawEquation: any = function(equation: any, color: string, thickness: number) {
+    let context = this.context;
+    context.save();
+    context.save();
+    this.transformContext(context);
+
+    context.beginPath();
+    context.lineWidth = thickness;
+    try {
+        context.moveTo(this.minX, equation(this.minX));
+        var move = true;
+        var _x = undefined;
+        var _y = undefined;
+        var pendiente = undefined;
+        var delta = 0.50;
+        var nuevoY = undefined;
+        var h = 1/1000;
+        var delta = this.rangeX/1000;
+        var anchoPunto = this.rangeX/200;
+        for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
+            try{
+
+                var punto = false;
+                var hayPunto = function(){
+                    punto = true;
+                    return true;
+                }
+                // var punto = esPunto(x, this.rangeX/1000);
+                // if(punto != undefined){
+                //     this.context.fillRect(x-anchoPunto/2,punto-anchoPunto/2,anchoPunto,anchoPunto);
+                //     move = true;
+                // }else{
+                var y = equation(x,delta,hayPunto);
+                if(punto){
+                    this.context.fillRect(x-anchoPunto/2,y-anchoPunto/2,anchoPunto,anchoPunto);
+                    move = true;
+                    punto = false;
+                }else{
+                    // if(nuevoY){
+                    //     var diff = Math.abs(Math.abs(nuevoY)-Math.abs(y));
+                    //     if(diff>delta){
+                    //         move = true;
+                    //     }
+                    // }
+                    // if(_x){
+                    //     pendiente = (y -_y)/(x-_x);
+                    //     nuevoY = y + pendiente*h;
+                    // }
+
+                    //######################################################
+                    //POSIBLE CODIGO QUE MANEJA UBICACION DEL NUEVO PUNTO
+                    //SEGUN EL ANGULO DE LA PENDIENTE Y SU UBICACION.  
+                                    // var y = equation(x);
+                                    if(pendiente != undefined){
+                                        var diff = Math.abs(Math.abs(nuevoY)-Math.abs(y));
+
+                                        var pendienteMas1 = Math.tan(Math.atan(pendiente)+Math.PI/8);
+                                        var pendienteMenos1 = Math.tan(Math.atan(pendiente)-Math.PI/8);
+                                        
+                                        if(pendiente > 0 && pendienteMas1 < 0){
+                                            pendienteMas1 = 1000000;
+                                        }
+
+                                        if(pendiente < 0 && pendienteMenos1 > 0){
+                                            pendienteMenos1 = -1000000;
+                                        }
+
+                                        var max = (x - _x)*pendienteMas1 - (y-_y);
+                                        var min = (x - _x)*pendienteMenos1 -(y-_y);
+
+                                        //tiene que ser o pendienteMas1 o max negativo, por eso se multiplica.
+                                        // if(Math.sign(pendienteMas1)*Math.sign(pendiente)*max < 0 || Math.sign(pendienteMenos1)*Math.sign(pendiente)*min > 0){ 
+                                        if(max < 0 || min > 0){ 
+                                            move = true;
+                                        }
+                                    }
+                                    if(_x){
+                                        pendiente = (y -_y)/(x-_x);
+                                    }
+                    //######################################################
+
+                
+                    if(y>10e6){
+                        y = 10e6;
+                    }else if (y<-10e6){
+                        y = -10e6;
+                    }
+                    // if(Math.abs(x)<10e-5){
+                    //     y = 0;
+                    // }
+                    // if(Math.abs(x)<10e-5){
+                    //     x = 0;
+                    // }
+                    if(move){
+                        context.moveTo(x,y);
+                        move = false;
+                    }else{
+                        context.lineTo(x, y);
+                    }
+                 }
+                _x = x;
+                _y = y;
+            }catch(e){
+                move = true;
+            }
+        }
+    } catch (e) {
+        this.limpiarCanvas();
+    }
+
+    context.restore();
+    context.lineJoin = 'bevel';
+    context.lineWidth = thickness;
+    context.strokeStyle = color;
+    context.stroke();
+    context.restore();
+};
+
+private drawPolyline: any = function(polygon:boolean, puntos: any, color: string, rotation:number) {
+    let context = this.context;
+    context.save();
+    context.save();
+    this.transformContext(context);
+
+    context.beginPath();
+    try {
+        if (puntos.length > 1){
+            var inicio = puntos[0];
+            context.moveTo(inicio[0], inicio[1]);
+            for (let punto of puntos) {
+                context.lineTo(punto[0], punto[1]);
+            }
+            if(polygon){
+                context.lineTo(inicio[0], inicio[1]);
+            }
+        }
+    } catch (e) {
+        this.limpiarCanvas();
+    }
+
+    context.restore();
+    context.lineJoin = 'round';
+    context.strokeStyle = color;
+    if(color){
+            context.fillStyle = color;
+            context.fill();
+    } 
+    context.strokeStyle = 'black';
+    context.stroke();
+    context.restore();
+};
+
+}
diff --git a/src/app/layout/canvas/canvas.module.ts b/src/app/layout/canvas/canvas.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..931d67ff30e27b86d8603f88738370614f064386
--- /dev/null
+++ b/src/app/layout/canvas/canvas.module.ts
@@ -0,0 +1,14 @@
+import { NgModule} from '@angular/core';
+import { CommonModule } from '@angular/common'
+import { RouterModule } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { FormsModule } from '@angular/forms';
+import { CanvasComponent } from './canvas.component';
+
+@NgModule({
+    imports: [FormsModule, RouterModule, CommonModule, NgbModule],
+    declarations: [CanvasComponent],
+    exports: [CanvasComponent]
+})
+
+export class CanvasModule { }
diff --git a/src/app/layout/canvas/canvas.routes.ts b/src/app/layout/canvas/canvas.routes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38e1ce5815db6590d55a9fd5a0d63348079e6a65
--- /dev/null
+++ b/src/app/layout/canvas/canvas.routes.ts
@@ -0,0 +1,9 @@
+import { Route } from '@angular/router';
+import { CanvasComponent } from './index';
+
+export const CanvasRoutes: Route[] = [
+	{
+		path: 'canvas',
+		component: CanvasComponent
+	}
+];
diff --git a/src/app/layout/canvas/index.ts b/src/app/layout/canvas/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ec27135c63c8edede3c80ac96a285e1ba8692e60
--- /dev/null
+++ b/src/app/layout/canvas/index.ts
@@ -0,0 +1,6 @@
+/**
+ * This barrel file provides the export for the lazy loaded BlankpageComponent.
+ */
+export * from './canvas.component';
+
+export * from './canvas.routes';
diff --git a/src/app/layout/functions/function.services.result.ts b/src/app/layout/functions/function.services.result.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb4d8fb3715ff3b7e227c1f698007a48e4d90c13
--- /dev/null
+++ b/src/app/layout/functions/function.services.result.ts
@@ -0,0 +1,4 @@
+export class FunctionServicesResult {
+  code: number;
+  msg: string;
+}
\ No newline at end of file
diff --git a/src/app/layout/functions/function.ts b/src/app/layout/functions/function.ts
new file mode 100644
index 0000000000000000000000000000000000000000..764863fd9a8465e33813b39a09df4bb2ae64e3b8
--- /dev/null
+++ b/src/app/layout/functions/function.ts
@@ -0,0 +1,5 @@
+export class Function {
+  id: number;
+  name: string;
+  str: string;
+}
\ No newline at end of file
diff --git a/src/app/layout/functions/functions.component.html b/src/app/layout/functions/functions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..c8a5596b905e00a961645ddbb7ce4852bafd02e4
--- /dev/null
+++ b/src/app/layout/functions/functions.component.html
@@ -0,0 +1,50 @@
+
+            
+            <div class="panel panel-success">
+
+              <div class="card">
+                  <h4 class="card-header">Mis funciones</h4>  
+                  <div class="card-block">
+                    <ul class="list-group list-group-flush">
+                   
+                        <li *ngFor="let f of _funcs" class="list-group-item">
+                            <div class="card-block">
+                              <div class="checkbox">
+                                  <label for="checkbox">
+                                      {{f.str}}
+                                  </label>
+                              </div>
+                              <div class="pull-right action-buttons">
+                                <button (click)="modifyFunc(f.name)" class="btn btn-success">
+                                  <i class="fa fa-pencil" ></i>
+                                </button> 
+                                <button (click)="removeFunc(f.name)" class="btn btn-success">
+                                  <i class="fa fa-remove" ></i>
+                                </button> 
+                              </div>
+                            </div>
+                        </li>
+
+
+
+                    </ul>
+                  </div>
+              </div>
+
+
+                      
+                <div class="panel-footer">
+                    <div class="row">
+                        <div class="col-md-12">
+                        <div class="input-group input-group">
+                          <input [(ngModel)]="_newFunction.str" placeholder="nueva función" class="form-control" >
+                          <span class="input-group-btn">
+                            <button class="btn btn-primary" [disabled]="this._newFunction.str==''" type="button" (click)="handleAddFunction()">Cargar función</button>
+                          </span>
+                        </div>
+                        </div>
+                        
+                    </div>
+                     <!-- <button (click)="popToast()">pop toast</button>  -->
+                </div>
+            </div>
diff --git a/src/app/layout/functions/functions.component.ts b/src/app/layout/functions/functions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aec0c51f8c4cd4d66787c389d6512264b24b0097
--- /dev/null
+++ b/src/app/layout/functions/functions.component.ts
@@ -0,0 +1,52 @@
+import { Component, OnInit } from '@angular/core';
+import { Function } from './function';
+import { FunctionServices } from './functions.service';
+import { FunctionServicesResult } from './function.services.result';
+//import {ToasterModule, ToasterService,ToasterConfig} from 'angular2-toaster';
+
+@Component({
+  selector: 'functions',
+  templateUrl: './functions.component.html',
+  styleUrls: ['./functions.css'],
+  providers: []
+})
+export class FunctionsComponent implements OnInit {
+  title = 'Módulo de funciones';
+  _funcs: Function[];
+  _newFunction :Function = {'id':2,'name':'g','str':'g(x)=x+41'};
+  //_toasterService: ToasterService;
+  //
+  constructor(private functionServices: FunctionServices) {
+    //    this._toasterService = toasterService;
+    }
+  getFuncs(): void {
+    this.functionServices.getAll().then(result => this._funcs = result);
+  }
+  removeFunc(name:string): void{
+    this.functionServices.delete(name);
+    this.getFuncs();
+  }
+  modifyFunc(name:string): void{
+    var f = this.functionServices.get(name);
+    console.log(f);
+    this._newFunction = f;
+
+  }
+
+   ngOnInit(): void {
+    this.getFuncs();
+  }
+
+  handleAddFunction(): void{
+    if(this._newFunction.str.length>4){
+     this._newFunction.id =1; 
+    }
+    var _f:Function  = {'id':this._newFunction.id,'name':this._newFunction.str.split('(')[0], 'str':this._newFunction.str};
+    var response = this.functionServices.add(_f);
+    this._newFunction.id=0;
+    this._newFunction.name='';
+    this._newFunction.str = '';
+
+  }
+
+}
diff --git a/src/app/layout/functions/functions.css b/src/app/layout/functions/functions.css
new file mode 100644
index 0000000000000000000000000000000000000000..7f08fa988273ceed7bebc1a77209bd4b1f0642bd
--- /dev/null
+++ b/src/app/layout/functions/functions.css
@@ -0,0 +1,13 @@
+.trash { 
+	color:rgb(209, 91, 71); 
+}
+.flag { 
+	color:rgb(248, 148, 6); 
+}
+.panel-body { padding:0px; }
+.panel-footer .pagination { margin: 0; }
+.panel .glyphicon,.list-group-item .glyphicon { margin-right:5px; }
+.panel-body .radio, .checkbox { display:inline-block;margin:0px; }
+.panel-body input[type=checkbox]:checked + label { text-decoration: line-through;color: rgb(128, 144, 160); }
+.list-group-item:hover, a.list-group-item:focus {text-decoration: none;background-color: rgb(245, 245, 245);}
+.list-group { margin-bottom:0px; }
\ No newline at end of file
diff --git a/src/app/layout/functions/functions.module.ts b/src/app/layout/functions/functions.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..46bbe37e1e2fb9aefaae7edcef4394996a4d2c73
--- /dev/null
+++ b/src/app/layout/functions/functions.module.ts
@@ -0,0 +1,19 @@
+import { NgModule }      from '@angular/core';
+
+import { CommonModule } from '@angular/common';
+import { FormsModule }   from '@angular/forms';
+import { FunctionsComponent }  from './functions.component';
+//import {ToasterModule, ToasterService} from 'angular2-toaster';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule
+    //,ToasterModule
+  ],
+  declarations: [
+    FunctionsComponent
+  ],
+  exports:[FunctionsComponent]
+})
+export class FunctionsModule { }
diff --git a/src/app/layout/functions/functions.routes.ts b/src/app/layout/functions/functions.routes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ebc6616a48ee803e6d48297915e9495dec64f12
--- /dev/null
+++ b/src/app/layout/functions/functions.routes.ts
@@ -0,0 +1,10 @@
+import { Route } from '@angular/router';
+
+import { FunctionsComponent } from './index';
+
+export const FunctionsRoutes: Route[] = [
+	{
+		path: 'functions',
+		component: FunctionsComponent
+	}
+];
diff --git a/src/app/layout/functions/functions.service.ts b/src/app/layout/functions/functions.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4d5cffa7be96e1acb0272f6fa3a01189d3b60f4b
--- /dev/null
+++ b/src/app/layout/functions/functions.service.ts
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+
+import { Function } from './function';
+import { FunctionServicesResult }from './function.services.result';
+
+
+const regex = /^\w+\(((\w+(\s)*)?|((\w+(\s)*),(\s)*)*(\w+(\s)*))\)(\s)*=(\s)*$/g;
+
+@Injectable()
+export class FunctionServices {
+	getAll(): Promise<Function[]>{
+    	return Promise.resolve(this.allfunctions);		
+  	};  
+  	delete(functionName:String):void{
+  		console.log("deleting" + functionName);
+  		this.allfunctions = this.allfunctions.filter(function(f){return f.name!=functionName});
+  	};
+  	add(f: Function): FunctionServicesResult{
+  		console.log(f);
+  		if(f.id!=0){
+  			this.allfunctions.push(f);	
+  			var result : FunctionServicesResult = {'code':200,'msg':'' };
+  		} else {
+  			var result : FunctionServicesResult = {'code':500,'msg':'Función no válida' };
+  		}
+  		return result;
+
+  	};
+  	get(name:string){
+  		try{
+  			return this.allfunctions.filter(function(f){return f.name===name})[0];
+  		} catch(err){
+  			return null;
+  		}
+  	};
+    getToPlot(name:string){
+      console.log("Functions: ");
+      console.log(this.allfunctions);
+      try{
+        return this.allfunctions.filter(function(f){return f.name===name})[0].str.split("=")[1];
+      } catch(err){
+        return null;
+      }
+    };
+
+  	private allfunctions : Function[]  = [{'id':12,'name':'f', 'str':'f(x)=x+5'}];
+}
\ No newline at end of file
diff --git a/src/app/layout/functions/index.ts b/src/app/layout/functions/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..46cafb423a5f0e921ee3a6a1761f9a422d8ab924
--- /dev/null
+++ b/src/app/layout/functions/index.ts
@@ -0,0 +1,5 @@
+/**
+ * This barrel file provides the export for the lazy loaded BlankpageComponent.
+ */
+export * from './functions.component';
+export * from './functions.routes';
diff --git a/src/app/layout/grupos/calificarEntrega.component.ts b/src/app/layout/grupos/calificarEntrega.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..32bf6eade13def9868c8aefd232451d909a4df76
--- /dev/null
+++ b/src/app/layout/grupos/calificarEntrega.component.ts
@@ -0,0 +1,86 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo, Evaluacion } from '../../shared/objects/archivo';
+
+export interface ConfirmModel {
+  cedula:string;
+  archivo: Archivo;
+  parentContext: any;
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+
+                   <div class="modal-header">
+                      <h5 class="modal-title">Calificar entrega</h5> 
+                      <button type="button" class="close" (click)="close()" style="margin-left:8px;">&times;</button>
+                   </div>
+                   
+                   <div class="modal-body">
+                    <form>                      
+                      <div class="form-group">
+                        <label for="message-text" class="form-control-label">Calificacion (1-100):</label>
+                        <input type="number" class="form-control" [(ngModel)]="nota" min=1 max=100 [ngModelOptions]="{standalone: true}" >                        
+                      </div>
+                      <div class="form-group">
+                        <label for="message-text" class="form-control-label">Detalle:</label>
+                        <textarea class="form-control" id="message-text" [(ngModel)]="descripcion" [ngModelOptions]="{standalone: true}" ></textarea>
+                      </div>
+                    </form>
+                  </div>
+
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" (click)="cancel()">Cancelar</button>
+                    <button type="button" class="btn btn-success" (click)="confirm()">Calificar</button>
+                  </div>
+
+                 </div>
+              </div>`
+})
+export class CalificarEntrega extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  descripcion: string = "";
+  cedula: string;
+  archivo: Archivo;
+  nota: number = 1;
+
+  parentContext: any;
+
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+    
+  }
+
+  ngOnInit() {
+    if(this.archivo.evaluacion){
+      this.descripcion = this.archivo.evaluacion.descripcion;
+      this.nota = this.archivo.evaluacion.nota;
+    }
+  }
+
+  confirm() {
+    var evaluacion = new Evaluacion();
+    evaluacion.cedulaDocente = this.cedula;
+    evaluacion.descripcion = this.descripcion;
+    evaluacion.nota = this.nota;
+    if(this.nota>0 && this.nota<100){
+      this.parentContext.haskellService.calificarArchivo(this.archivo.id,evaluacion )
+       .subscribe(
+        evaluacion => {
+          this.parentContext.notifService.success("Archivo evaluado");
+          this.archivo.evaluacion = evaluacion;
+          this.close();
+        }, 
+        error => {
+          this.parentContext.notifService.error(error);
+        });
+    }else{
+      this.parentContext.notifService.error("Calificacion fuera de rango");
+    }
+  }
+
+  cancel(){
+    this.close();
+  }
+  
+}
\ No newline at end of file
diff --git a/src/app/layout/grupos/grupos-routing.module.ts b/src/app/layout/grupos/grupos-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b7c8a8b3cb84d218e25a2f5f8178fc8cf36e9bc
--- /dev/null
+++ b/src/app/layout/grupos/grupos-routing.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { GruposComponent } from './grupos.component';
+
+const routes: Routes = [
+    { path: '', component: GruposComponent }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class GruposRoutingModule { }
\ No newline at end of file
diff --git a/src/app/layout/grupos/grupos.component.html b/src/app/layout/grupos/grupos.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..df1f27cb7efa30b930059138b30056a442b95b64
--- /dev/null
+++ b/src/app/layout/grupos/grupos.component.html
@@ -0,0 +1,121 @@
+<notificacion></notificacion> 
+<div class="container-fluid">
+    <div class="row">
+        <div class="col-lg-5">
+            <label for="search">Nombre del archivo:</label>
+            <div class="input-group">
+            <!--[(ngModel)]=filtroNombre-->
+                <input type="text" class="form-control" id="search" >
+                <span class="input-group-addon fa fa-search">
+                </span>
+            </div>
+        </div>
+    </div>
+    <div class="row" style="margin-top: 20px">
+        <div class="col-lg-5">
+            <div class="card" *ngIf="grupoSeleccionado == undefined">
+                <div class="card-header">
+                    <div *ngIf="grupoSeleccionado==undefined">Grupos</div>
+                </div>
+                <div class="card-block" *ngIf="grupoSeleccionado == undefined">
+                    <div class="row listado-grupos" style="min-height: 100px; overflow-y: scroll;">
+                        <div class="loading" *ngIf="loading">
+                            <div class="loading-bar"></div>
+                            <div class="loading-bar"></div>
+                            <div class="loading-bar"></div>
+                            <div class="loading-bar"></div>
+                        </div>
+                        <div *ngFor="let grupo of grupos" (click)="seleccionarGrupo(grupo)" class="col-sm-3 col-4 " style="text-align: center;">
+                            <i class="fa fa-users" style="font-size: 3em; cursor: pointer;color: #f95e5e;" aria-hidden="true"></i>
+                            <p style="cursor: pointer;">{{grupo.grado + '°' + grupo.grupo+" - "+grupo.anio}}</p>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <ngb-tabset *ngIf="grupoSeleccionado" [destroyOnHide]=false>
+                <ngb-tab title="Alumnos">
+                    <ng-template ngbTabContent>
+                        <div class="card">
+                            <div>
+                                <button class="btn btn-sm btn-secondary pull-right" style="cursor: pointer; margin-top: -35px; margin-right: 105px;" (click)="desseleccionarGrupo()" ngbPopover="Atras" data-placement="bottom" triggers="mouseenter:mouseleave">
+                                    <i class="fa fa-arrow-up"></i>
+                                </button>
+                                <p class="pull-right" style="margin-top: -34px; margin-right: 5px;">{{grupoSeleccionado.grado+ '°' + grupoSeleccionado.grupo+" - "+grupoSeleccionado.anio}}</p>
+                            </div>
+                            <div class="card-block">
+                                <div class="row listado-grupos" style="min-height: 100px; overflow-y: scroll;">
+                                    <div *ngFor="let alumno of grupoSeleccionado.alumnos" (click)="seleccionarAlumno(alumno)" class="col-sm-3 " style="text-align: center;">
+                                        <i class="fa fa-user" style="font-size: 3em; cursor: pointer;color: #f95e5e;" aria-hidden="true"></i>
+                                        <p style="cursor: pointer;">{{alumno.apellido +', ' + alumno.nombre}}</p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </ng-template>
+                </ngb-tab>
+                <ngb-tab title="Archivos">
+                    <ng-template ngbTabContent>
+                        <div class="card">
+                            <div>
+                                <button class="btn btn-sm btn-secondary pull-right" style="cursor: pointer; margin-top: -35px; margin-right: 105px;" (click)="desseleccionarGrupo()"  ngbPopover="Atras" data-placement="bottom" triggers="mouseenter:mouseleave">
+                                    <i class="fa fa-arrow-up"></i>
+                                </button>
+                                <p class="pull-right" style="margin-top: -34px; margin-right: 5px;">{{grupoSeleccionado.grado+ '°' + grupoSeleccionado.grupo+" - "+grupoSeleccionado.anio}}</p>
+                            </div>
+                            <div class="card-block">
+                                <div class="row listado-grupos" style="min-height: 100px; overflow-y: scroll;">
+                                    <div *ngFor="let archivo of grupoSeleccionado.archivos " (click)="seleccionarArchivo(archivo)" class="col-sm-3 col-4" style="text-align: center;">
+                                        <i class="fa fa-file-text" style="font-size: 3em; cursor: pointer;color: #ff8383" aria-hidden="true"></i>
+                                        <p style="cursor: pointer;">{{archivo.nombre}}</p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </ng-template>
+                </ngb-tab>
+            </ngb-tabset>
+        </div>
+        <div class="col-lg-7">
+            <div class="card" *ngIf="alumnoSeleccionado">
+                <div class="card-block">
+                    <div class="row listadoEntregasAlumnoGrupos" style="min-height: 100px; overflow-y: scroll;" >
+                        <div *ngFor="let entrega of alumnoSeleccionado.archivos" (click)="seleccionarEntrega(entrega)" class="col-sm-3 col-4" style="text-align: center;">
+                            <i [ngStyle]="" class="fa fa-file-text" style="font-size: 3em; cursor: pointer;" aria-hidden="true"></i>
+                            <p style="cursor: pointer;">{{entrega.nombre}}</p>
+                        </div>
+                        <div *ngIf="alumnoSeleccionado.archivos.length == 0" style="width: 100%; text-align: center;">
+                            <i style="color: rgb(220,220,220); font-size: 10em; padding: 0.1em" class="fa fa-file-text"></i>
+                            <p>No hay entregas del alumno: {{alumnoSeleccionado.nombre +' '+alumnoSeleccionado.apellido }}</p>
+                        </div>
+                    </div>
+                   
+                </div>
+            </div>
+            <div class="card" *ngIf="alumnoSeleccionado == undefined && archivoSeleccionado == undefined">
+                <div class="card-block">
+                     <div class="row previewArchivoNoSeleccionadoGrupos" style="min-height: 100px" >
+                        <div style="width: 100%; text-align: center;">
+                            <i style="color: rgb(220,220,220); font-size: 10em; padding: 0.1em" class="fa fa-file-text"></i>
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            <div class="card" *ngIf="archivoSeleccionado" >
+                <div class="card-header">
+                    <button *ngIf="tipoArchivo == 'entrega'" class="btn btn-sm btn-secondary pull-left mr-2" (click)="calificarEntrega()">
+                        Calificar
+                    </button>
+                    <button *ngIf="esArchivoGrupo()"  ngbPopover="Cargar/Editar" data-placement="bottom" triggers="mouseenter:mouseleave" class="btn btn-sm btn-secondary pull-left mr-2" (click)="cargarArchivoCompartido()">
+                        <i class="fa fa-pencil"></i>
+                    </button>
+                    <div class="pull-left" >
+                        Nombre: {{archivoSeleccionado?.nombre}} - Creado: {{archivoSeleccionado?.fechaCreacion | date}}
+                    </div>
+                </div>
+                <codemirror class="codemirrorGrupos" [(ngModel)]="archivoSeleccionado.contenido" [config]="configCodeMirror" [ngStyle]="{'font-size': configCodeMirror.fontSize+'px'}">
+                </codemirror>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/src/app/layout/grupos/grupos.component.ts b/src/app/layout/grupos/grupos.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29c81563d73cb8267e939a82904111d691e3a44c
--- /dev/null
+++ b/src/app/layout/grupos/grupos.component.ts
@@ -0,0 +1,177 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Archivo } from '../../shared/objects/archivo';
+import { Grupo } from '../../shared/objects/grupo';
+import { Usuario } from '../../shared/objects/usuario';
+import { AuthenticationService } from '../../shared/services/authentication.service';
+import { HaskellService } from '../../shared/services/haskell.service';
+import { SessionService } from '../../shared/services/session.service';
+import { DialogService } from "ng2-bootstrap-modal";
+import { CalificarEntrega } from './calificarEntrega.component';
+import { NgbPopoverConfig, NgbPopover} from '@ng-bootstrap/ng-bootstrap';
+import { NotificacionService } from '../../shared/services/notificacion.service';
+
+
+
+@Component({
+	moduleId: module.id,
+	selector: 'grupos',
+	templateUrl: './grupos.component.html'
+})
+
+export class GruposComponent {
+	archivos : Archivo[] = [];
+	grupos : Grupo [] = [];
+	grupoSeleccionado: Grupo = undefined;
+	
+	alumnoSeleccionado: Usuario = undefined;
+
+	archivoSeleccionado: Archivo = undefined;
+
+	tipoArchivo: string = undefined;
+
+	loading: boolean = false;
+	idRecorridos :any = [];
+	tree: any;
+	directorioActual:any;
+	configCodeMirror = JSON.parse(sessionStorage.getItem('codeMirrorConfig'));
+
+	constructor(
+		private router: Router,
+		private authService: AuthenticationService,
+		private haskellService: HaskellService,
+		private notifService: NotificacionService,
+		private sessionService: SessionService,
+		private dialogService:DialogService
+		){
+		this.directorioActual = {};
+		this.directorioActual.archivos = [];
+		this.configCodeMirror.readOnly = true;
+	}
+
+	ngOnInit(){
+		let cedula = this.authService.getUser().cedula;
+		this.loading = true;
+		this.haskellService.getGrupos(cedula)
+		.subscribe(
+			grupos => {
+				this.grupos = grupos;
+				this.ordenarGrupos();
+				this.loading = false;
+			}, 
+			error => console.log(error) 
+			);
+	}
+
+	//ordenar archivos del grupo seleccionado.
+	ordenarAlph(a, b){
+   		if(a.nombre.toLowerCase() < b.nombre.toLowerCase()) return -1;
+     	if(a.nombre.toLowerCase() > b.nombre.toLowerCase()) return 1;
+     	return 0;
+	}
+
+	ordenarArchivos(){
+		this.grupoSeleccionado.archivos = this.grupoSeleccionado.archivos.sort(this.ordenarAlph);
+	}
+
+	//ordeno los archivos del alumno (los archivos entregados.)
+	ordenarArchivosAlumno(){
+		if(this.archivoSeleccionado.archivos){
+			this.archivoSeleccionado.archivos = this.archivoSeleccionado.archivos.sort(this.ordenarAlph);
+		}
+	}
+
+	//ordenar grupos
+	ordenarGrupoF(a,b){
+		if(a.grado>b.grado) return 1;
+		if(a.grado<b.grado) return -1;
+		if(a.grupo.toLowerCase()>b.grupo.toLowerCase()) return 1;
+		if(a.grupo.toLowerCase()<b.grupo.toLowerCase()) return -1;
+		return 0;
+	}
+
+	ordenarGrupos(){
+		this.grupos = this.grupos.sort(this.ordenarGrupoF);
+	}
+
+	//ordenar alumnos
+	ordenarAlumnosF(a,b){
+		if(a.apellido.toLowerCase()>b.apellido.toLowerCase()) return  1;
+		if(a.apellido.toLowerCase()<b.apellido.toLowerCase()) return -1;
+		return 0;
+	}
+
+	ordenarAlumnos(){
+		this.grupoSeleccionado.alumnos = this.grupoSeleccionado.alumnos.sort(this.ordenarAlumnosF);
+	}
+
+	seleccionarGrupo(grupo){
+		this.grupoSeleccionado = grupo;
+		this.ordenarAlumnos();
+		this.ordenarArchivos();
+		this.archivoSeleccionado = undefined;
+		this.alumnoSeleccionado = undefined;
+	}
+
+	desseleccionarGrupo(){
+		this.grupoSeleccionado = undefined;
+		this.archivoSeleccionado = undefined;
+		this.alumnoSeleccionado = undefined;
+	}
+
+	seleccionarAlumno(alumno){
+		this.alumnoSeleccionado = alumno;
+		this.ordenarArchivosAlumno();
+		this.archivoSeleccionado = undefined;
+	}
+
+	seleccionarArchivo(archivo){
+		this.archivoSeleccionado = archivo;
+		this.alumnoSeleccionado = undefined;
+		this.tipoArchivo = "compartido";
+	}
+
+	seleccionarEntrega(entrega){
+		this.archivoSeleccionado = entrega;
+		this.alumnoSeleccionado = undefined;
+		this.tipoArchivo = "entrega";
+	}
+
+	calificarEntrega(){
+		let disposable = this.dialogService.addDialog(CalificarEntrega,
+			{
+				cedula: JSON.parse(sessionStorage.currentUser).cedula+'',  
+				archivo: this.archivoSeleccionado,
+  				parentContext: this
+  			})
+		.subscribe((isConfirmed)=>{
+			if(isConfirmed) { 
+				//codeMirrorRef.options.readOnly = false;
+				//componentRef.editDialogFired = true;
+			}
+		});
+	}
+
+	esArchivoGrupo(){
+		if(this.archivoSeleccionado && this.grupoSeleccionado && this.grupoSeleccionado.archivos.some(arch => arch.id == this.archivoSeleccionado.id)){
+			return true;
+		}else{
+			return false;
+		}
+	}
+
+	cargarArchivoCompartido(){
+		if(this.archivoSeleccionado){
+			if(this.archivoSeleccionado.directorio){
+				this.notifService.warning('No se seleccionó ningún archivo',false);
+			}else{
+				this.sessionService.setArchivo(this.archivoSeleccionado);
+				this.router.navigate(['/matefun']);
+			}
+		}else{
+			this.notifService.warning("Archivo no seleccionado");
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/src/app/layout/grupos/grupos.module.ts b/src/app/layout/grupos/grupos.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..184ff83d5e7a6daabd1d418d1b9ab67fd4e44e71
--- /dev/null
+++ b/src/app/layout/grupos/grupos.module.ts
@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { GruposComponent } from './grupos.component';
+import { CommonModule } from '@angular/common';
+import { GruposRoutingModule } from './grupos-routing.module';
+import { DialogService } from "ng2-bootstrap-modal";
+import { NotificacionService } from '../../shared/services/notificacion.service';
+import { BootstrapModalModule } from 'ng2-bootstrap-modal';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { CodemirrorModule } from 'ng2-codemirror';
+import { CalificarEntrega } from './calificarEntrega.component';
+import { NotificacionModule } from '../../notificacion/notificacion.module'; 
+
+@NgModule({
+    imports: [CommonModule, GruposRoutingModule, FormsModule,BootstrapModalModule, NgbModule, CodemirrorModule,NotificacionModule],
+    declarations: [GruposComponent, CalificarEntrega],
+    exports: [GruposComponent],
+    entryComponents: [CalificarEntrega]
+})
+
+export class GruposModule { }
diff --git a/src/app/layout/layout-routing.module.ts b/src/app/layout/layout-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0a40b0676a0578af59cb53142b10dc9120881710
--- /dev/null
+++ b/src/app/layout/layout-routing.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+import { LayoutComponent } from './layout.component';
+
+const routes: Routes = [
+    {
+        path: '', component: LayoutComponent,
+        children: [
+            { path: 'matefun', loadChildren: './matefun/matefun.module#MateFunModule' },
+            { path: 'archivos', loadChildren: './archivos/archivos.module#ArchivosModule' },
+            { path: 'grupos', loadChildren: './grupos/grupos.module#GruposModule' }            
+        ]
+    }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class LayoutRoutingModule { }
diff --git a/src/app/layout/layout.component.html b/src/app/layout/layout.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..33251e486fc88b7ae2527cda5d878c8b44c331be
--- /dev/null
+++ b/src/app/layout/layout.component.html
@@ -0,0 +1,5 @@
+<app-header></app-header>
+<app-sidebar></app-sidebar>
+<section class="main-container">
+    <router-outlet></router-outlet>
+</section>
diff --git a/src/app/layout/layout.component.scss b/src/app/layout/layout.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b82aa23034df301fe032e08c27af23c69bc472bf
--- /dev/null
+++ b/src/app/layout/layout.component.scss
@@ -0,0 +1,17 @@
+.main-container{
+    margin-top: 60px;
+    margin-left: 235px;
+    padding: 15px;
+    -ms-overflow-x: hidden;
+    overflow-x: hidden;
+    overflow-y: scroll;
+    position: relative;
+    overflow: hidden;
+
+}
+// @media screen and (max-width: 982px) {
+    .main-container {
+        margin-left: 0px !important;
+    }
+// }
+
diff --git a/src/app/layout/layout.component.spec.ts b/src/app/layout/layout.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4d6365d70259780c4656f6382416ca8418ff674
--- /dev/null
+++ b/src/app/layout/layout.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LayoutComponent } from './layout.component';
+
+describe('LayoutComponent', () => {
+  let component: LayoutComponent;
+  let fixture: ComponentFixture<LayoutComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LayoutComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LayoutComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/layout/layout.component.ts b/src/app/layout/layout.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b81e4b8d12417f6fd627094fc7105f75cf784224
--- /dev/null
+++ b/src/app/layout/layout.component.ts
@@ -0,0 +1,18 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { GHCIService } from '../shared/services/ghci.service';
+import { NotificacionService } from '../shared/services/notificacion.service';
+@Component({
+    selector: 'app-layout',
+    templateUrl: './layout.component.html',
+    styleUrls: ['./layout.component.scss'],
+    providers: [GHCIService]
+})
+export class LayoutComponent implements OnInit {
+    constructor(public router: Router) { }
+    ngOnInit() {
+        if (this.router.url === '/') {
+            this.router.navigate(['/login']);
+        }
+    }
+}
diff --git a/src/app/layout/layout.module.ts b/src/app/layout/layout.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9777481aaa12a918ec469c807b12d4c1338c7cc5
--- /dev/null
+++ b/src/app/layout/layout.module.ts
@@ -0,0 +1,30 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { FormsModule } from '@angular/forms';
+
+import { LayoutRoutingModule } from './layout-routing.module';
+import { LayoutComponent } from './layout.component';
+import { HeaderComponent, SidebarComponent } from '../shared';
+import { AuthenticationService } from '../shared/services/authentication.service';
+import { HaskellService } from '../shared/services/haskell.service';
+import { CodemirrorModule } from 'ng2-codemirror';
+import { NotificacionModule } from '../notificacion/notificacion.module'; 
+
+@NgModule({
+    imports: [
+        CommonModule,
+        FormsModule,
+        NgbModule.forRoot(),
+        LayoutRoutingModule,
+        CodemirrorModule,
+        NotificacionModule
+    ],
+    declarations: [
+        LayoutComponent,
+        HeaderComponent,
+        SidebarComponent
+    ],
+    providers: [AuthenticationService, HaskellService]
+})
+export class LayoutModule { }
diff --git a/src/app/layout/matefun/confirm.component.ts b/src/app/layout/matefun/confirm.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2c1c727ed9168f37172b43190eca8766b7f072d
--- /dev/null
+++ b/src/app/layout/matefun/confirm.component.ts
@@ -0,0 +1,37 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+export interface ConfirmModel {
+  title:string;
+  message:string;
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                     <button type="button" class="close" (click)="close()" >&times;</button>
+                     <!-- <h4 class="modal-title">{{title || 'Confirm'}}</h4> -->
+                   </div>
+                   <div class="modal-body">
+                     <p>{{message || ''}}</p>
+                   </div>
+                   <div class="modal-footer">
+                     <button type="button" class="btn btn-primary" (click)="confirm()">Editar</button>
+                     <button type="button" class="btn btn-default" (click)="close()" >Cancelar</button>
+                   </div>
+                 </div>
+              </div>`
+})
+export class ConfirmComponent extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  title: string;
+  message: string;
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+  confirm() {
+    // we set dialog result as true on click on confirm button, 
+    // then we can get dialog result from caller code 
+    this.result = true;
+    this.close();
+  }
+}
\ No newline at end of file
diff --git a/src/app/layout/matefun/index.ts b/src/app/layout/matefun/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4676de0be9833a3554428a3549bd8dac2b3d2da8
--- /dev/null
+++ b/src/app/layout/matefun/index.ts
@@ -0,0 +1,5 @@
+/**
+ * This barrel file provides the export for the lazy loaded BlankpageComponent.
+ */
+export * from './matefun.component';
+export * from './matefun.routes';
diff --git a/src/app/layout/matefun/matefun-routing.module.ts b/src/app/layout/matefun/matefun-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ff3fac0188bdba52797adf1e33277716babfafb
--- /dev/null
+++ b/src/app/layout/matefun/matefun-routing.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { MateFunComponent } from './matefun.component';
+
+const routes: Routes = [
+    { path: '', component: MateFunComponent }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class MateFunRoutingModule { }
\ No newline at end of file
diff --git a/src/app/layout/matefun/matefun.component.html b/src/app/layout/matefun/matefun.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0e2031ad57a826aa1ffd9c923bae9fc0a9e87ef9
--- /dev/null
+++ b/src/app/layout/matefun/matefun.component.html
@@ -0,0 +1,122 @@
+<notificacion></notificacion> 
+<div class="container-fluid" style=" padding-left: 0px; padding-right: 0px; margin-top: -13px; margin-left: -6px; margin-right: -6px;">
+    <div class="row">
+        <div class="col-md-6">
+            <ngb-tabset [destroyOnHide]=false>
+                <ngb-tab id="ProgramBtn"title="Programa">
+                    <ng-template ngbTabContent>
+
+            <div class="card">
+                <div class="card-header">
+                    <form>
+                        <input type="text" name="archivo" class="nomArchivoInp form-control form-control-sm" 
+                        [disabled]="!archivo.editable || archivo.estado=='Corregido' || archivo.estado == 'Entregado'"
+                        *ngIf="archivo" [(ngModel)]="archivo.nombre" (keyup)="archivoModificado()" placeholder="nombre del archivo" />
+
+                        <button id="downloadFileButton" (click)="downloadFile()" style="margin-left: 10px; float: right;"  class="btn btn-sm btn-secondary" placement="bottom" ngbPopover="Exportar (Ctrl+E)" triggers="mouseenter:mouseleave" tiggers="click">
+                            <i class="fa fa-download "></i>
+                        </button>
+
+                        <button style="margin-left: 10px; float: right;" id="popover" class="btn btn-sm btn-secondary" placement="bottom" [ngbPopover]=popoverContent #popover="ngbPopover" popoverTitle="Configuración" tiggers="click">
+                            <i class="fa fa-gear"></i>
+                        </button>
+                        <div style="margin-left: 10px; float: right;" ngbPopover="Guardar archivo (Ctrl+G)" triggers="mouseenter:mouseleave" placement="bottom" >
+                            <button [disabled]="!modificado" (click)="guardarArchivo()" class="btn btn-sm btn-secondary" >
+                                <i class="fa fa-save"></i>
+                            </button>
+                        </div>
+                        <button style="margin-left: 10px; float: right;" (click)="reiniciarInterprete()" class="btn btn-sm btn-secondary" ngbPopover="Reiniciar intérprete (Ctrl+R)" triggers="mouseenter:mouseleave" placement="bottom">
+                            <i class="fa fa-refresh"></i>
+                        </button>
+                        <button style="margin-left: 10px; float: right;" (click)="runCode()" class="btn btn-sm btn-secondary" ngbPopover="Cargar programa (Ctrl+P)" triggers="mouseenter:mouseleave" placement="bottom">
+                            <i class="fa fa-play"></i>
+                        </button>
+                        <button style="float: right;" (click)="seleccionarDirectorio()" class="btn btn-sm btn-secondary" ngbPopover="Nuevo archivo (Ctrl+A)" triggers="mouseenter:mouseleave" placement="bottom">
+                            <i class="fa fa-plus"></i>
+                        </button>
+                        <ng-template #popoverContent style="width: 15em">
+                            <div style="width: 12em">
+                                <div class="form-group">
+                                    <label>Tema:</label>
+                                    <select name="theme" class="form-control form-control-sm" #selectTheme (change)=updateConfig(selectTheme.value)>
+                                        <option *ngFor="let theme of themes" [selected]="theme==configCodeMirror.theme" value='{{theme}}'>{{theme}}</option>
+                                    </select>
+                                </div>
+                                <div class="form-group">
+                                    <label>Tamaño de fuente:</label>
+                                    <div>
+                                        <button class="btn btn-sm btn-secondary" (click)="aumentarFuente()">A⁺</button>
+                                        <button class="btn btn-sm btn-secondary" (click)="disminuirFuente()">A⁻</button>
+                                        {{configCodeMirror.fontSize}}px                                    
+                                    </div>
+                                </div>
+                                <div class="form-group">
+                                    <label>
+                                        <input type="checkbox" style="width: 15px; display: inline-block;" name="argumentoF" class="form-control form-control-sm" [(ngModel)]=argumentoF>
+                                        Mostrar advertencias de uso de funciones
+                                    </label>
+                                    <br>
+                                    <label>
+                                        <input type="checkbox" style="width: 15px; display: inline-block;" name="argumentoI" class="form-control form-control-sm" [(ngModel)]=argumentoI>
+                                        Mostrar advertencias de uso de operadores infijos
+                                    </label>
+                                </div>
+                                <div class="form-group">
+                                    <button class="btn btn-secondary" (click)="saveConfig()">Guardar</button>
+                                </div>
+                            </div>
+                        </ng-template>
+                    </form>
+                </div>
+                <codemirror class="codemirrorPrograma" [(ngModel)]="archivo.contenido" (keyup)="archivoModificado()" [config]="configCodeMirror" [ngStyle]="{'font-size': configCodeMirror.fontSize+'px'}">
+                </codemirror>
+            </div>
+
+            </ng-template>
+                </ngb-tab>
+                <ngb-tab id="FigurasBtn" title="Figuras">
+                    <ng-template ngbTabContent>
+                        <canvas-component (canvasComp)=canvasC></canvas-component>
+                    </ng-template>
+                </ngb-tab>
+            </ngb-tabset> 
+
+        </div>
+
+        <div class="col-md-6">
+            <!--  
+            <ngb-tabset [destroyOnHide]=false>
+                <ngb-tab title="Programa">
+                    <ng-template ngbTabContent>
+
+                        <div class="card">
+                            <div id="console" > </div>
+                        </div>
+
+                    </ng-template>
+                </ngb-tab>
+                <ngb-tab title="Figuras">
+                    <ng-template ngbTabContent>
+                        <canvas-component (canvasComp)=canvasC></canvas-component>
+                    </ng-template>
+                </ngb-tab>
+            </ngb-tabset> 
+            -->
+            
+
+
+            <div class="card">
+                <div id="console"> </div>
+            </div>
+           <!--
+            <canvas-component (canvasComp)=canvasC></canvas-component>
+            
+             <div class="card">
+                <div id="svgHaskell">
+                </div>
+            </div> -->
+
+        </div>
+    </div>
+</div>
+
diff --git a/src/app/layout/matefun/matefun.component.scss b/src/app/layout/matefun/matefun.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..53f3612a50baea080c6c5c35f7a34f68c66212d0
--- /dev/null
+++ b/src/app/layout/matefun/matefun.component.scss
@@ -0,0 +1,5 @@
+#svgHaskell svg{
+	width: 100% !important;
+	height: 100% !important;
+}
+
diff --git a/src/app/layout/matefun/matefun.component.ts b/src/app/layout/matefun/matefun.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..837dc7c54c6c800ef20500e38391447a8f81588c
--- /dev/null
+++ b/src/app/layout/matefun/matefun.component.ts
@@ -0,0 +1,621 @@
+import { Component, NgModule, ViewChild, HostListener, ElementRef, ComponentRef, TemplateRef } from '@angular/core';
+import { CanvasModule} from '../canvas/canvas.module';
+import { CanvasComponent } from '../canvas/canvas.component';
+import { FunctionsModule } from '../functions/functions.module';
+import { FunctionsComponent } from '../functions/functions.component';
+import { Http, JsonpModule } from '@angular/http';
+import { Headers, RequestOptions } from '@angular/http';
+import { HaskellService } from '../../shared/services/haskell.service';
+import { FunctionServices } from '../functions/functions.service';
+import { WebsocketService } from '../../shared/services/websocket.service';
+import { UsuarioService } from '../../shared/services/usuario.service';
+import { SessionService } from '../../shared/services/session.service';
+import { GHCIService } from '../../shared/services/ghci.service';
+import { AuthenticationService } from '../../shared/services/authentication.service';
+import { GHCI_URL } from '../../shared/config';
+import { Archivo } from '../../shared/objects/archivo';
+import { Configuracion } from '../../shared/objects/usuario';
+import { ConfirmComponent } from './confirm.component';
+import { SeleccionarDirectorioComp } from './seleccionarDirectorio.component';
+import { DialogService } from "ng2-bootstrap-modal";
+import { CodemirrorComponent } from 'ng2-codemirror';
+import { NgbPopoverConfig, NgbPopover} from '@ng-bootstrap/ng-bootstrap';
+import { NgbPopoverWindow } from '@ng-bootstrap/ng-bootstrap/popover/popover';
+import { NotificacionService } from '../../shared/services/notificacion.service';
+
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+import 'codemirror/mode/haskell/haskell';
+import 'codemirror/addon/display/panel';
+import 'codemirror/addon/hint/show-hint';
+import 'codemirror/addon/hint/anyword-hint';
+import 'codemirror/mode/markdown/markdown';
+import 'codemirror/lib/codemirror';
+import 'codemirror/addon/search/search';
+
+
+import 'codemirror/addon/dialog/dialog';
+import 'codemirror/addon/search/search';
+import 'codemirror/addon/search/matchesonscrollbar';
+import 'codemirror/addon/search/jump-to-line';
+
+var codeMirrorRef:any;
+var componentRef : any;
+var focus: any;
+@Component({
+    moduleId: module.id,
+    selector: 'matefun',     
+    templateUrl: './matefun.component.html',   
+    styleUrls: ['./matefun.component.scss'],  
+    providers: [ WebsocketService, FunctionServices, NgbPopoverConfig, UsuarioService ]
+})
+
+
+export class MateFunComponent {
+
+    consoleDisable: boolean = false;
+    consolaVisible: boolean = true;
+    cursorPanel: any;
+    cursorPanelLabel: any;
+    cursorLabelInit : boolean = false;
+    entrada : string = '';
+    archivo : Archivo;
+    copiaNombreArchivo:string;
+    copiaContenidoArchivo:string;
+    modificado = false;
+    argumentoI = false;
+    argumentoF = false;
+    editableLoaded = false;
+    editDialogFired = false;
+    archivosTree :any;
+    idRecorridos: any;
+    code: string ='';
+    configCodeMirror = {
+        readOnly: false,
+        lineNumbers: true,
+        lineWrapping : true,
+        extraKeys: {"Ctrl-Space": "autocomplete"},
+        mode: {
+            name: "haskell", 
+            globalVars: true
+        },
+        gutters: ["CodeMirror-linenumbers", "breakpoints"],
+        theme: 'dracula',
+        fontSize: 12
+    };
+    themes = ['3024-day', '3024-night', 'abcdef', 'ambiance-mobile', 'ambiance', 'base16-dark', 'base16-light', 'bespin', 'blackboard', 'cobalt', 'colorforth', 'dracula', 'duotone-dark', 'duotone-light', 'eclipse', 'elegant', 'erlang-dark', 'hopscotch', 'icecoder', 'isotope', 'lesser-dark', 'liquibyte', 'material', 'mbo', 'mdn-like', 'midnight', 'monokai', 'neat', 'neo', 'night', 'panda-syntax', 'paraiso-dark', 'paraiso-light', 'pastel-on-dark', 'railscasts', 'rubyblue', 'seti', 'solarized', 'the-matrix', 'tomorrow-night-bright', 'tomorrow-night-eighties', 'ttcn', 'twilight', 'vibrant-ink', 'xq-dark', 'xq-light', 'yeti', 'zenburn']
+
+
+    constructor(
+        private haskellService: HaskellService,
+        private authService: AuthenticationService, 
+        private ghciService: GHCIService, 
+        private elRef: ElementRef, 
+        private notifService: NotificacionService,
+        private functionServices: FunctionServices,
+        private sessionService: SessionService,
+        private dialogService:DialogService,
+        private usuarioService: UsuarioService) {
+        //si el archivo fue seteado en la session. 
+        this.archivo = sessionService.getArchivo();
+        if(!this.archivo || !this.archivo.id){
+            this.newFile();
+        }
+        this.copiaContenidoArchivo = this.archivo.contenido;
+        this.copiaNombreArchivo = this.archivo.nombre;
+        if(authService.getUser().configuracion){
+            var config: Configuracion = authService.getUser().configuracion;
+            if(config.fontSizeEditor<=30 && config.fontSizeEditor>=8){
+                this.configCodeMirror.fontSize = config.fontSizeEditor;
+            }
+            if(this.themes.some(theme => theme==config.themeEditor)){
+                this.configCodeMirror.theme = config.themeEditor;                
+            }
+            sessionStorage.setItem('codeMirrorConfig',JSON.stringify(this.configCodeMirror));
+            this.argumentoI = config.argumentoI;
+            this.argumentoF = config.argumentoF;
+
+        }
+        this.code = "my code";
+        let svg : string = '';
+
+    }
+
+
+    @ViewChild(CodemirrorComponent) codemirror: CodemirrorComponent;
+    // @ViewChild(NgbPopover) popover: NgbPopover;
+    @ViewChild('popover') popover: NgbPopover;
+
+    updateConfig(theme){
+        this.configCodeMirror.theme = theme;
+        this.codemirror.instance.setOption('theme', theme); 
+        sessionStorage.setItem('codeMirrorConfig',JSON.stringify(this.configCodeMirror));
+    }
+
+    lockSaveButton (){
+        this.copiaNombreArchivo = this.archivo.nombre;
+        this.copiaContenidoArchivo = this.archivo.contenido;
+        this.modificado = false;
+    }
+    
+
+    showConfirm() {
+        let disposable = this.dialogService.addDialog(ConfirmComponent, {
+            title:'Está intentando editar un archivo de solo lectura', 
+            message:'Está editando un archivo de solo lectura, desea continuar?'})
+        .subscribe((isConfirmed)=>{
+
+            if(isConfirmed) {
+                codeMirrorRef.options.readOnly = false;
+                componentRef.editDialogFired = true;
+            }
+        });
+        //We can close dialog calling disposable.unsubscribe();
+        //If dialog was not closed manually close it by timeout
+           /* setTimeout(()=>{
+                disposable.unsubscribe();
+            },10000);*/
+        }
+
+        /* Panel para la posición del cursor */
+        makePanel() {
+
+            var node = document.createElement("div");
+            node.id = "cursorpos-panel";
+            node.className = "panel bottom";
+            this.cursorPanelLabel = node.appendChild(document.createElement("span"));
+            var cm = this.codemirror.instance;
+            var x = cm.getCursor().line;
+            var y = cm.getCursor().ch; 
+            x = (Number(x) + 1).toString();
+            y = (Number(y) + 1).toString();
+            this.cursorPanelLabel.textContent = "Posición del cursor: (" + x + "," + y + ")";    
+
+
+            this.cursorPanel = this.codemirror.instance.addPanel(node, {position: "bottom", stable: true});
+            var that = this;
+            //agregamos el evento que setea la posición
+            this.codemirror.instance.on("cursorActivity",function(cm){
+                var x = cm.getCursor().line;
+                var y = cm.getCursor().ch; 
+                x = (Number(x) + 1).toString();
+                y = (Number(y) + 1).toString();
+                that.cursorPanel.node.innerText = "Posición del cursor: (" + x + "," + y + ")";    
+            });
+
+            this.codemirror.instance.on("keyHandled",function(cm,name,evt){
+                if(name.code==="Digit1" && name.ctrlKey && name.shiftKey){
+                    that.seleccionarDirectorio();
+                } else if(name.code==="Digit2" && name.ctrlKey && name.shiftKey){
+                    that.saveConfig();
+                }
+            });
+            /*
+            this.codemirror.instance.on("gutterClick", function(cm, n) {
+                      var info = cm.lineInfo(n);
+                      var makeMarker = function() {
+                          var marker = document.createElement("div");
+                          marker.style.width = "15px";
+                          marker.style.height = "15px";
+                          marker.style.marginLeft = "-5px";
+                          marker.style.cursor = "pointer";
+                          marker.style["background-image"] = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=')";
+                          marker.innerHTML = "<a href='@' title='cuidado , advertencia matefun'></a>";
+                          return marker;
+                        }
+                        cm.setGutterMarker(n, "breakpoints", info.gutterMarkers ? null : makeMarker());
+            });*/
+
+            this.codemirror.instance.on("keypress",function(cm,name,evt){
+
+                if(!that.editDialogFired && JSON.parse(sessionStorage.currentUser).tipo === "docente" && cm.options.readOnly){
+                    codeMirrorRef = that.codemirror.instance;
+                    componentRef = that;
+                    that.showConfirm(); 
+                }
+
+            });
+        } 
+
+        saveConfig(){
+            var config = new Configuracion();
+            config.themeEditor = this.configCodeMirror.theme;
+            config.fontSizeEditor = this.configCodeMirror.fontSize;
+            var confUser = this.authService.getUserConfig();
+            var reiniciar = confUser.argumentoF != this.argumentoF || confUser.argumentoI != this.argumentoI;
+            config.argumentoF = this.argumentoF;
+            config.argumentoI = this.argumentoI;
+            this.usuarioService.actualizarConfiguracion(this.authService.getUser().cedula,config)
+            .subscribe(
+                success=> {
+                    //this.ghciService.consoleRef.Write("Configuración guardada"  + "\n");
+                    this.popover.close();
+                    this.authService.setUserConfig(success);
+                    if(reiniciar){
+                        this.reiniciarInterprete();
+                    }
+
+                },
+                error=> {
+                    this.notifService.error(error);
+                    this.popover.close();
+                }
+                );
+        }
+
+        aumentarFuente(){
+            if(this.configCodeMirror.fontSize<30){
+                this.configCodeMirror.fontSize++;
+            }
+        }
+
+        disminuirFuente(){
+            if(this.configCodeMirror.fontSize>8){
+                this.configCodeMirror.fontSize--;
+            }
+        }
+
+        @HostListener('document:click', ['$event'])
+        private documentClicked(event: MouseEvent): void {
+
+            // Popover is open
+            if (this.popover && this.popover.isOpen()) {
+
+                // Not clicked on self element
+                if (!(this.popover as any)._elementRef.nativeElement.contains(event.target)) {
+
+                    // Hacking typescript to access private member
+                    const popoverWindowRef: ComponentRef<NgbPopoverWindow> = (this.popover as any)._windowRef;
+
+                    // If clicked outside popover window
+                    if (!popoverWindowRef.location.nativeElement.contains(event.target)) {
+                        this.popover.close();
+                    }
+                }
+            }
+        }
+
+        ngOnInit() {
+
+            this.ghciService.rendered(); 
+
+
+            this.haskellService.getArchivos(this.authService.getUser().cedula)
+            .subscribe(
+                archivos => {
+                    //.filter(function(a){return !a.eliminado})
+                    this.buildTreeFromList(archivos);
+
+                }, 
+                error => console.log("Error al obtener los archivos del alumno") 
+                );
+
+            function KeyPress(e) {
+
+                var evtobj = window.event? event : e
+                if (evtobj.keyCode == 90 && evtobj.ctrlKey){
+                    //alert("Ctrl+z")
+                };
+                if(evtobj.key.toLowerCase() ==="a" && evtobj.ctrlKey){
+                    componentRef.seleccionarDirectorio();
+                    return false;
+                }else if(evtobj.key.toLowerCase() ==="e" && evtobj.ctrlKey){
+                    componentRef.downloadFile();
+                    return false;
+                } else if(evtobj.key.toLowerCase() ==="r" && evtobj.ctrlKey){
+                    componentRef.reiniciarInterprete();
+                    return false;
+                } else if(evtobj.key.toLowerCase() ==="g" && evtobj.ctrlKey){
+                    componentRef.guardarArchivo();
+                    return false;
+                } else if(evtobj.key.toLowerCase() ==="o" && evtobj.ctrlKey){
+                    document.getElementById("popover").click();
+                    return false;
+                } else if(evtobj.ctrlKey && evtobj.altKey && evtobj.key.toLowerCase() ==="p"){
+                    document.getElementById("ProgramBtn").click();
+                    var that  = componentRef;
+                    setTimeout(function() {
+                        that.codemirror.instance.focus();
+                    },250);
+                    componentRef.codemirror.instance.focus();
+                    focus ="program";
+                    return false;
+                }  else if(evtobj.ctrlKey && evtobj.altKey && evtobj.key.toLowerCase() ==="c"){
+                    componentRef.ghciService.focusConsole();
+                    focus = "consola";
+                    return false;
+                }  else if(evtobj.ctrlKey && evtobj.altKey && evtobj.key.toLowerCase() ==="f"){
+                    document.getElementById("FigurasBtn").click()
+                    componentRef.ghciService.focusConsole();
+                    focus = "graficas";
+                    return false;
+                }  else if(evtobj.key.toLowerCase() ==="p" && evtobj.ctrlKey && !evtobj.altKey){
+                    componentRef.runCode();
+                    return false;
+                } 
+            }
+            document.onkeydown = KeyPress;
+        }
+        ngAfterViewInit() {
+            componentRef = this;
+            if(this.codemirror.instance!=null && !this.cursorLabelInit){
+                this.cursorLabelInit = true;
+                this.codemirror.instance.setOption('theme', this.configCodeMirror.theme);
+                this.makePanel();    
+            }
+            if(!this.editableLoaded && this.codemirror.instance!=null &&(this.sessionService.archivo.editable!==undefined)){
+                try{
+                    var editable = this.sessionService.archivo.editable && (this.sessionService.archivo.estado == 'Edicion' || this.sessionService.archivo.estado == 'Devuelto');
+                    this.codemirror.instance.options.readOnly = !editable;
+                    this.editableLoaded = true;
+                    
+
+                } catch(err) {
+                    return;
+
+                }
+            }
+
+        }
+        htmlEncode(value:string){
+            return value
+            .replace('Prelude> ','')
+            .replace(/&/g, '&amp;')
+            .replace(/\s/g, '&nbsp;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#39;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;');
+        }
+
+        @ViewChild(CanvasComponent) canvasC: CanvasComponent;
+
+        funcionSTR: string = 'Math.sin(x)*x*x-20';
+        consola: string = '';
+        command: string = '';
+        tipo: number = 1;
+
+        private onKey = function (value: string) {
+            this.funcionSTR = value;
+            this.archivo.contenido = value;
+        }
+
+        private writeCommand = function (value: string){
+            this.command = value.split("\n")[value.split("\n").length - 1];
+        }
+
+        private selectFunction = function() {
+            this.tipo = 1;
+            this.funcionSTR = "Math.sin(x)*x*x-20";
+        }
+
+        private selectElipse = function() {
+            this.tipo = 2;
+            this.funcionSTR = "elipse(x,y,radioX, radioY, rotacion_en_grados)";
+        }
+
+        private selectCircle = function() {
+            this.tipo = 3;
+            this.funcionSTR = "circulo(x,y,radio)";
+        }
+
+        private elipse = function(x: number, y: number, radiusX: number, radiusY: number, rotation: number) {
+            return [x, y, radiusX, radiusY, rotation];
+        }
+
+        private circulo = function(x: number, y: number, radius: number) {
+            return [x, y, radius];
+        }
+
+        inputConsola(text:any){
+            this.entrada = text;            
+        }
+        newFile(){
+            this.archivo = new Archivo();
+            this.archivo.cedulaCreador = this.authService.getUser().cedula;
+            this.archivo.contenido = "";
+            this.archivo.nombre = "";
+            this.copiaNombreArchivo = '';
+            this.copiaContenidoArchivo = '';
+        }
+
+        archivoModificado(){
+            if(this.copiaNombreArchivo!=this.archivo.nombre || this.copiaContenidoArchivo != this.archivo.contenido){
+                this.modificado = true;
+            }else{
+                this.modificado = false;
+            }
+        }
+
+        guardarArchivo(){
+            var regex = /^[A-Z]/
+            if(this.archivo.nombre.trim() == ""){
+                this.notifService.error("Nombre de archivo sin especificar");
+            }else if (!regex.test(this.archivo.nombre)){
+                this.notifService.error("Nombre de archivo debe iniciar con mayusula.")
+            }else{
+                if(this.archivo.id){
+                    this.haskellService.editarArchivo(this.archivo.id, this.archivo)
+                    .subscribe(
+                        archivo => {
+                            //this.ghciService.consoleRef.Write("Editar archivo: " + this.archivo.nombre + "\n");
+                            this.archivo = archivo;
+                            this.lockSaveButton();
+                        }, 
+                        error => {
+                            this.notifService.error(error);
+                        });
+                }else{
+                    this.haskellService.crearArchivo(this.archivo)
+                    .subscribe(
+                        archivo => {
+                            //this.ghciService.consoleRef.Write("Archivo creado: " + this.archivo.nombre + "\n");
+                            this.archivo = archivo;
+                            this.lockSaveButton();
+                        }, 
+                        error => {
+                            this.notifService.error(error);
+                        });
+                    
+                }
+            }
+        }
+        runCode(){
+
+            this.ghciService.setCodemirrorRef(this.codemirror.instance);
+            this.ghciService.resetGutters();
+            var regex = /^[A-Z]/
+            if(this.archivo.nombre.trim() == ""){
+                this.notifService.error("Nombre de archivo sin especificar");
+            }else if (!regex.test(this.archivo.nombre)){
+                this.notifService.error("Nombre de archivo debe iniciar con mayusula.")
+            }else{
+
+                var resultado = this.sessionService.cargarDependencias(this.archivo);
+                if(resultado["status"]==="miss"){
+                    this.ghciService.outputConsole("Error: No se encuentra el archivo " + resultado["nombre"] + "\n");
+                    return;
+                }
+                if(this.archivo.id){
+                    if(this.archivo.editable || this.authService.getUser().tipo == 'docente'){
+                        this.haskellService.editarArchivo(this.archivo.id, this.archivo)
+                        .subscribe(
+                            archivo => {
+                                this.archivo = archivo;
+                                var list = this.sessionService.getDependencias(),
+                                idList = [];
+                                for(var l in list){
+                                    idList.push(list[l].id);
+                                }
+                                if(!idList.some(id => id ==archivo.id)){
+                                    idList.push(archivo.id);
+                                }
+                                this.lockSaveButton();
+                                this.ghciService.loadFile(archivo.id,idList);
+                            }, 
+                            error => {
+                                this.notifService.error(error);
+                            });
+                    }else{
+                        var list = this.sessionService.getDependencias(),
+                        idList = [];
+                        for(var l in list){
+                            idList.push(list[l].id);
+                        }
+                        if(!idList.some(id => id ==this.archivo.id)){
+                            idList.push(this.archivo.id);
+                        }
+                        this.ghciService.loadFile(this.archivo.id,idList);
+                    }
+                }else{
+                    this.haskellService.crearArchivo(this.archivo)
+                    .subscribe(
+                        archivo => {
+                            this.archivo = archivo;
+                            this.lockSaveButton();
+                            this.ghciService.loadFile(archivo.id,[]);
+                        }, 
+                        error => {
+                            this.notifService.error(error);
+                        });
+                }
+            }
+            this.ghciService.focusConsole();
+
+        }
+        download(filename, text) {
+            var element = document.createElement('a');
+            element.setAttribute('href', 'data:application/octet-stream,' + encodeURIComponent(text));
+            element.setAttribute('download', filename+ ".mf");
+
+            element.style.display = 'none';
+            document.body.appendChild(element);
+
+            element.click();
+
+            document.body.removeChild(element);
+        }
+        downloadFile(){
+            var nom = this.archivo.nombre;
+            var content = this.archivo.contenido;
+            if(nom!= undefined && nom!="" && content!= undefined && content !=""){
+                this.download(nom , content);
+            }
+
+        }
+        reiniciarInterprete(){
+            this.ghciService.reiniciarInterprete();
+        }
+
+        toggleConsole(){
+            this.consolaVisible = !this.consolaVisible;
+        }
+
+        seleccionarDirectorio(){
+            this.archivosTree = this.sessionService.getArchivos(undefined);
+            var that = this;
+            let disposable = this.dialogService.addDialog(SeleccionarDirectorioComp, {
+                title:'', 
+                message:'',
+                archivos:this.archivosTree,
+                directorioActual:this.archivosTree,
+                nombre:'',
+                parent:this})
+            .subscribe((isConfirmed)=>{
+
+                if(isConfirmed) {
+
+
+                    //codeMirrorRef.options.readOnly = false;
+                    //componentRef.editDialogFired = true;
+                }
+            });
+        }
+
+
+        buildTreeFromList (archivos){
+
+
+            this.sessionService.setArchivosList(archivos);
+            var root :Archivo;
+
+            for(var a in archivos){
+                var arch = archivos[a];
+                if(arch.padreId===-1){
+                    root = arch;
+                } 
+            }
+            this.idRecorridos = [root.id];
+            var archivos2 = archivos.filter(
+                function(a){
+                    return a.id!==root.id;
+                }
+                );
+            var tree = this.buildTree(archivos2,root);
+            this.archivosTree = tree;
+            this.sessionService.setArchivosTree(tree);
+        }
+
+
+        buildTree(archivos, root){
+            root.archivos = this.getArchivos(root.id,archivos);
+            for(var a in root.archivos){
+                if(root.archivos[a].directorio && this.idRecorridos[root.archivos[a].id] === undefined){
+                    var id = root.archivos[a].id;
+                    var archivos2 = archivos.filter(function(a){return a.id!==id});
+                    root.archivos[a] = this.buildTree(archivos2 ,root.archivos[a]);
+                }
+            }
+            return root;
+        }
+
+        getArchivos(id,archivos){
+            return archivos.filter(
+                function(a){
+                    return a.padreId === id;
+                });
+        }
+
+
+
+    }
diff --git a/src/app/layout/matefun/matefun.module.ts b/src/app/layout/matefun/matefun.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..838a75b5583601ee96c5d6dc3886739cbc39518b
--- /dev/null
+++ b/src/app/layout/matefun/matefun.module.ts
@@ -0,0 +1,34 @@
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { CanvasModule } from '../canvas/canvas.module'
+import { MateFunComponent } from './matefun.component';
+import { FunctionsModule } from '../functions/functions.module'
+import { BootstrapModalModule } from 'ng2-bootstrap-modal';
+import { ConfirmComponent } from './confirm.component';
+import { SeleccionarDirectorioComp } from './seleccionarDirectorio.component';
+import { CommonModule } from '@angular/common';
+import { MateFunRoutingModule } from './matefun-routing.module';
+import { CodemirrorModule } from 'ng2-codemirror';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NotificacionModule } from '../../notificacion/notificacion.module'; 
+@NgModule({
+    imports: [
+    CommonModule, 
+    FormsModule, 
+    CanvasModule, 
+    NotificacionModule,
+    FunctionsModule, 
+    MateFunRoutingModule,
+    CodemirrorModule,
+    NgbModule,    
+    BootstrapModalModule
+    ],      
+    entryComponents: [
+        ConfirmComponent,
+        SeleccionarDirectorioComp
+    ],
+    declarations: [MateFunComponent,ConfirmComponent,SeleccionarDirectorioComp],
+    exports: [MateFunComponent]
+})
+
+export class MateFunModule { }
diff --git a/src/app/layout/matefun/matefun.routes.ts b/src/app/layout/matefun/matefun.routes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..222b5fdaef865844401e09f21c86af1e385cdb22
--- /dev/null
+++ b/src/app/layout/matefun/matefun.routes.ts
@@ -0,0 +1,8 @@
+import { Route } from '@angular/router';
+import { MateFunComponent } from './index';
+export const MateFunRoutes: Route[] = [
+	{
+		path: 'funciones',
+		component: MateFunComponent
+	}
+];
diff --git a/src/app/layout/matefun/seleccionarDirectorio.component.ts b/src/app/layout/matefun/seleccionarDirectorio.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..90625e8808784168852459fff0454f3ad6181bce
--- /dev/null
+++ b/src/app/layout/matefun/seleccionarDirectorio.component.ts
@@ -0,0 +1,99 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+import { Archivo } from '../../shared/objects/archivo';
+export interface ConfirmModel {
+  title:string;
+  message:string;
+  archivos:any;
+  directorioActual:any;
+  parent:any;
+  nombre:string;
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                     <h6 class="modal-title pull-lefth">¿Dónde quieres crear el archivo?</h6> 
+                     <button type="button" class="close" (click)="close()" style="margin-rigth:8px;">&times;</button>
+                   </div>
+                   <div class="modal-body" style="height:350px;overflow-y: scroll;">
+                       <div>
+                         <div class="form-group">
+                            <label for="file-name" class="form-control-label">Nombre:</label>
+                            <input type="text" class="form-control" id="file-name" [(ngModel)]="nombre" >
+                         </div>
+                         <div class="list-group" >
+                            <button *ngFor="let arch of directorioActual.archivos" type="button" (click)="navToDir(arch)" style="cursor:pointer" class="list-group-item list-group-item-action">
+                                  <i *ngIf="arch.directorio" class="fa fa-folder" style="margin-right:10px; font-size: 3em; cursor: pointer;" aria-hidden="true" ></i>
+                                  <i *ngIf="!arch.directorio" class="fa fa-file-text" style="margin-right:10px;font-size: 3em; cursor: pointer;" aria-hidden="true"></i>
+                                 {{arch.nombre}}
+                             </button>
+                          </div>
+                        </div>
+                     </div>
+                   <div class="modal-footer">
+                     <button type="button" class="btn btn-default" (click)="navBack()">Atras</button>
+                     <button type="button" class="btn btn-primary" (click)="confirm()">Crear</button>
+                   </div>
+                 </div>
+              </div>`
+})
+export class SeleccionarDirectorioComp extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  title: string;
+  message: string;
+  archivos:any;
+  directorioActual:any;
+  parent:any;
+  nombre:string;
+
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+  confirm() {
+    // we set dialog result as true on click on confirm button, 
+    // then we can get dialog result from caller code 
+    var regex = /^[A-Z]/
+    if(this.nombre==undefined || this.nombre==""){
+        this.parent.notifService.error("Nombre de archivo invalido.");
+    }else if (!regex.test(this.nombre)){
+        this.parent.notifService.error("Nombre de archivo debe iniciar con mayusula.");
+    }else{
+      var archivo:Archivo = new Archivo();
+      archivo.cedulaCreador = this.parent.authService.getUser().cedula;
+      archivo.contenido = "";
+      archivo.nombre = this.nombre;
+      archivo.directorio = false;
+      archivo.padreId = this.directorioActual.id;
+      archivo.editable = true;
+
+      this.parent.haskellService.crearArchivo(archivo)
+                .subscribe(
+                    archivo => {
+                        this.parent.archivo = archivo;
+                        this.parent.ghciService.loadFile(archivo.id);
+                        this.parent.sessionService.setArchivo(archivo);
+                       
+                    }, 
+                    error => {
+                        this.parent.notifService.error(error);
+                    });
+    
+      this.result = true;
+      this.close();
+    }
+  }
+
+  navToDir(arch){
+    if(arch.directorio){
+      this.directorioActual = arch;  
+    }
+  }
+
+  navBack(){
+    var idPadre = this.directorioActual.padreId;
+    var archivosList = this.parent.sessionService.getArchivosList();
+    var nuevoDirectorioActual = archivosList.filter(function(a){return a.id===idPadre})[0];
+    this.directorioActual=nuevoDirectorioActual;
+  }
+}
\ No newline at end of file
diff --git a/src/app/login/login-routing.module.ts b/src/app/login/login-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0235f7004a94fbde7677a3694ea9187ce3f47f79
--- /dev/null
+++ b/src/app/login/login-routing.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+import { LoginComponent } from './login.component';
+
+const routes: Routes = [
+    { path: '', component: LoginComponent }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class LoginRoutingModule { }
diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..53a6d149833e6b5c4158816c71e3923a3d0cccc2
--- /dev/null
+++ b/src/app/login/login.component.html
@@ -0,0 +1,33 @@
+<div class="login-page" style="background: #004869;">
+    <div class="row">
+        <div class="col-md-4 push-md-4" style="padding: 55px 35px;background: rgba(255,255,255,0.15);border-radius: 8px;">
+            <h1>MateFun</h1>
+            <form role="form">
+                <div class="form-content">
+                    <div class="form-group">
+                        <input type="text" [(ngModel)]=model.cedula  name="cedula" class="form-control input-underline input-lg"  placeholder="Usuario">
+                    </div>
+
+                    <div class="form-group">
+                        <input type="password" [(ngModel)]=model.password (keyup.enter)=login() name="password" class="form-control input-underline input-lg"  placeholder="Contraseña">
+                    </div>
+                    
+                </div>
+
+                <a class="btn rounded-btn" style="background: transparent;color: white;cursor: pointer;width: 159px;margin-right: 3px;" (click)=login()> Iniciar Sesión </a>
+                <a class="btn rounded-btn" style="background: transparent;color: white;cursor: pointer;width: 159px;margin-left: 3px;" (click)=invitado()> Invitado </a>
+                &nbsp;
+                <div class="loading" *ngIf="loading">
+                    <div class="loading-bar"></div>
+                    <div class="loading-bar"></div>
+                    <div class="loading-bar"></div>
+                </div>
+                <div class="login-error" *ngIf="error && !loading">
+                    <ngb-alert [dismissible]="false" [type]="'danger'">
+                        <strong>Error!</strong> {{errorText}}.
+                    </ngb-alert>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
diff --git a/src/app/login/login.component.scss b/src/app/login/login.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..92ee5466c358a42d782a3892bebf02c3424934c1
--- /dev/null
+++ b/src/app/login/login.component.scss
@@ -0,0 +1,105 @@
+$topnav-background-color: #222;
+:host {
+    display: block;
+}
+.login-page {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+    background: $topnav-background-color;
+    text-align: center;
+    color: #fff;
+    padding: 3em;
+    .col-lg-4{
+        padding :0;
+    }
+    .input-lg {
+        height: 46px;
+        padding: 10px 16px;
+        font-size: 18px;
+        line-height: 1.3333333;
+        border-radius: 0;
+    }
+    .input-underline {
+        background: 0 0;
+        border: none;
+        box-shadow: none;
+        border-bottom: 2px solid rgba(255,255,255,.5);
+        color: #FFF;
+        border-radius: 0;
+    }
+    .input-underline:focus {
+        border-bottom: 2px solid #fff;
+        box-shadow: none;
+    }
+    .rounded-btn{
+        -webkit-border-radius: 50px;
+                border-radius: 50px;
+        color: rgba(255,255,255,0.8);
+        background: $topnav-background-color;
+        border: 2px solid rgba(255,255,255,0.8);
+        font-size: 18px;
+        line-height: 40px;
+        padding: 0 25px;
+        cursor: default;
+    }
+    .rounded-btn:hover,.rounded-btn:focus,.rounded-btn:active,.rounded-btn:visited{
+        color: rgba(255,255,255,1);
+        border: 2px solid rgba(255,255,255,1);
+        outline: none;
+    }
+
+    h1 {
+        font-weight: 300;
+        margin-top: 20px;
+        margin-bottom: 10px;
+        font-size: 36px;
+        small {
+            color: rgba(255,255,255,0.7);
+        }
+    }
+
+    .form-group {
+        padding: 8px 0;
+        input::-webkit-input-placeholder {
+        color: rgba(255,255,255,0.6) !important;
+        }
+
+        input:-moz-placeholder { /* Firefox 18- */
+        color: rgba(255,255,255,0.6) !important;
+        }
+
+        input::-moz-placeholder {  /* Firefox 19+ */
+        color: rgba(255,255,255,0.6) !important;
+        }
+
+        input:-ms-input-placeholder {
+        color: rgba(255,255,255,0.6) !important;
+        }
+    }
+    .form-content {
+        padding: 40px 0;
+    }
+    .user-avatar {
+        -webkit-border-radius: 50%;
+                border-radius: 50%;
+        border: 2px solid #FFF;
+    }
+}
+
+.loading {
+    position: relative;
+    top: 50px;
+    left: 49%;
+    .loading-bar {
+        background-color: white;
+    }
+}
+
+.login-error {
+    position: relative;
+    top: 20px;
+}
diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d6d85a8465b79ee37cb75c371f6e6e936997c573
--- /dev/null
+++ b/src/app/login/login.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+  let component: LoginComponent;
+  let fixture: ComponentFixture<LoginComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LoginComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LoginComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2a01b4cef5b2efbcee0dcb1b36d650dacc52a09
--- /dev/null
+++ b/src/app/login/login.component.ts
@@ -0,0 +1,68 @@
+import { Component, OnInit } from '@angular/core';
+import { Router, ActivatedRoute } from '@angular/router';
+import { SessionService } from '../shared/services/session.service';
+import { AuthenticationService } from '../shared/services/authentication.service';
+
+
+@Component({
+    selector: 'app-login',
+    templateUrl: './login.component.html',
+    styleUrls: ['./login.component.scss']
+
+})
+
+export class LoginComponent implements OnInit {
+	model: any = {};
+    loading = false;
+    error = false;
+    errorText = "";
+    returnUrl: string;
+
+    constructor(
+        private route: ActivatedRoute,
+        private router: Router,
+        private sessionService: SessionService,
+        private authenticationService: AuthenticationService,
+        ) { }
+
+    ngOnInit() {
+        // reset login status
+        this.authenticationService.logout();
+
+        // get return url from route parameters or default to '/'
+        this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/matefun';
+    }
+
+    login() {
+    	// this.router.navigate([this.returnUrl]);
+        this.loading = true;
+
+        var that = this;
+        this.authenticationService.login(this.model.cedula, this.model.password)
+            .subscribe(
+                data => {
+                    //resetSession = true;
+                    this.router.navigate([this.returnUrl]);
+                    that.sessionService.reset();
+                },
+                error => {
+                    this.loading = false;
+                    this.error = true;
+                    this.errorText = error.text();
+                });
+    }
+
+    invitado(){
+        this.loading = true;
+        this.authenticationService.login("invitado", "invitado")
+            .subscribe(
+                data => {
+                    this.router.navigate([this.returnUrl]);
+                    this.sessionService.reset();
+                },
+                error => {
+                    this.loading = false;
+                });
+    }
+
+}
diff --git a/src/app/login/login.module.ts b/src/app/login/login.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8d8c187f673ea0d5097a98198783212ed16f67a9
--- /dev/null
+++ b/src/app/login/login.module.ts
@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { LoginRoutingModule } from './login-routing.module';
+import { LoginComponent } from './login.component';
+import { FormsModule } from '@angular/forms';
+import { AuthenticationService } from '../shared/services/authentication.service';
+
+@NgModule({
+  imports: [
+  	FormsModule,
+    CommonModule,
+    LoginRoutingModule,
+    NgbModule.forRoot(),
+  ],
+  declarations: [LoginComponent],
+  providers: [AuthenticationService]
+})
+export class LoginModule { }
diff --git a/src/app/not-found/not-found-routing.module.ts b/src/app/not-found/not-found-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee86f4ae06cb43ea36f9447389b068f566762723
--- /dev/null
+++ b/src/app/not-found/not-found-routing.module.ts
@@ -0,0 +1,14 @@
+import {NgModule} from "@angular/core";
+import {Routes, RouterModule} from "@angular/router";
+import {NotFoundComponent} from "./not-found.component";
+
+const routes: Routes = [
+  {path: '', component: NotFoundComponent}
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class NotFoundRoutingModule {
+}
diff --git a/src/app/not-found/not-found.component.html b/src/app/not-found/not-found.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..10f68541474d3c6853f3644cb1c00486ea8f0aeb
--- /dev/null
+++ b/src/app/not-found/not-found.component.html
@@ -0,0 +1,11 @@
+<div class="welcome-page">
+    <div class="row">
+        <div class="col-md-10 push-md-1">
+            <h1>404 - Page Not Found</h1>
+            <p class="lead">This page does not exist</p>
+            <p class="lead">
+                <a class="btn rounded-btn" [routerLink]="['/login']">Restart</a>
+            </p>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/not-found/not-found.component.scss b/src/app/not-found/not-found.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3149a41815cff7941921441a07891804bb06b4ab
--- /dev/null
+++ b/src/app/not-found/not-found.component.scss
@@ -0,0 +1,41 @@
+$topnav-background-color: #222;
+:host {
+    display: block;
+}
+.welcome-page {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+    background: $topnav-background-color;
+    text-align: center;
+    color: #fff;
+    padding: 10em;
+    .col-lg-8{
+        padding :0;
+    }
+    .rounded-btn{
+        -webkit-border-radius: 50px;
+                border-radius: 50px;
+        color: rgba(255,255,255,0.8);
+        background: $topnav-background-color;
+        border: 2px solid rgba(255,255,255,0.8);
+        font-size: 18px;
+        line-height: 40px;
+        padding: 0 25px;
+    }
+    .rounded-btn:hover,.rounded-btn:focus,.rounded-btn:active,.rounded-btn:visited{
+        color: rgba(255,255,255,1);
+        border: 2px solid rgba(255,255,255,1);
+        outline: none;
+    }
+
+    h1 {
+        font-weight: 300;
+        margin-top: 20px;
+        margin-bottom: 10px;
+        font-size: 36px;
+    }
+}
diff --git a/src/app/not-found/not-found.component.ts b/src/app/not-found/not-found.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..00d72a9cfdfa39fefc5054171b4f208acb2a9f7d
--- /dev/null
+++ b/src/app/not-found/not-found.component.ts
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+    selector: 'app-not-found',
+    templateUrl: './not-found.component.html',
+    styleUrls: ['not-found.component.scss']
+})
+export class NotFoundComponent { }
diff --git a/src/app/not-found/not-found.module.ts b/src/app/not-found/not-found.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..719e5be364cd0d066c51393c6538fb0438cc1727
--- /dev/null
+++ b/src/app/not-found/not-found.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { NotFoundComponent } from './not-found.component';
+import { NotFoundRoutingModule } from './not-found-routing.module';
+
+@NgModule({
+    imports: [
+        NotFoundRoutingModule,
+        RouterModule
+    ],
+    declarations: [NotFoundComponent]
+})
+export class NotFoundModule {}
diff --git a/src/app/notificacion/notificacion.component.html b/src/app/notificacion/notificacion.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6e3bd29413e164bef8653367476677457c861ded
--- /dev/null
+++ b/src/app/notificacion/notificacion.component.html
@@ -0,0 +1,9 @@
+<!--
+<ngb-alert class="alertPosition" *ngFor="let alert of alerts;let i = index" [type]="alert.type" [style.top]="(i*70+50)+'px'" dismissible="true" dismissOnTimeout="5000" (close)="closeAlert(i)">
+				{{ alert?.text }}
+</ngb-alert>
+ -->
+
+<ngb-alert class="alertPosition" *ngFor="let alert of alerts; let i = index" [type]="alert?.type" [style.top]="(i*60+30)+'px'" (close)="closeAlert(i)">
+{{ alert?.text }}
+</ngb-alert>
diff --git a/src/app/notificacion/notificacion.component.ts b/src/app/notificacion/notificacion.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a311f83b0975d1beffe432ef43a7975f2e2be18
--- /dev/null
+++ b/src/app/notificacion/notificacion.component.ts
@@ -0,0 +1,38 @@
+import { Component, OnInit } from '@angular/core';
+
+import { NotificacionService } from '../shared/services/notificacion.service';
+
+import 'rxjs/add/operator/debounceTime';
+
+@Component({
+    moduleId: module.id,
+    selector: 'notificacion',
+    templateUrl: 'notificacion.component.html'
+})
+
+export class NotificacionComponent {
+    message: any;
+
+    constructor(private notifService: NotificacionService) {
+    }
+
+    ngOnInit() {
+    	this.notifService.getMessageSubject().subscribe(message => { 
+            this.alerts.push(message);
+            setTimeout(()=>{
+                this.closeAlert(0);//siempre elimino la 0 dado que es la mas antigua que se agrego. 
+            }, 5000);
+        });
+        //Esto es otra forma que lugo de 5 segundos del ultimo mensaje vacia todo el arreglo. 
+        // this.notifService.getMessageSubject().debounceTime(5000).subscribe(() => this.alerts = []);
+
+    }
+
+   	public alerts: Array<Object> = [];
+
+	// Alert
+	public closeAlert(i:number):void {
+		this.alerts.splice(i, 1);
+	}
+
+}
\ No newline at end of file
diff --git a/src/app/notificacion/notificacion.module.ts b/src/app/notificacion/notificacion.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c385f9f6b0e0026d91bb5b43eba3fc5480f60e75
--- /dev/null
+++ b/src/app/notificacion/notificacion.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { NotificacionComponent } from './notificacion.component';
+
+import { NgbAlertModule } from '@ng-bootstrap/ng-bootstrap';
+
+
+@NgModule({
+    imports: [CommonModule, RouterModule, NgbAlertModule],
+    declarations: [NotificacionComponent],
+    exports: [NotificacionComponent]
+})
+
+export class NotificacionModule { }
diff --git a/src/app/shared/components/header/header.component.html b/src/app/shared/components/header/header.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7b31ad18af849aed73310ab4aeb0a518445af2dd
--- /dev/null
+++ b/src/app/shared/components/header/header.component.html
@@ -0,0 +1,20 @@
+<div class="pos-f-t fixed-top header" style="z-index: 1100;">
+    <nav class="navbar navbar-inverse bg-inverse navbar-toggleable-md" style="background: #0278AE !important;">
+        <button class="navbar-toggler navbar-toggler-left" style="display: block;" (click)="toggleSidebar($event)">
+            <span class="navbar-toggler-icon"></span>
+        </button>
+        <a class="navbar-brand" href="#/matefun" style="margin-left: 3.5em; width: 5em">MateFun</a>
+        <div class="collapse navbar-collapse" style="position: absolute; right: 10px; display: block; width: 15em; text-align: right;" id="navbarTogglerDemo02">
+            <ul class="navbar-nav ml-auto mt-2 mt-md-0">
+                <div class="nav-item dropdown" ngbDropdown>
+                    <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+                        <i class="fa fa-user"></i> {{usuario.nombre+' '+usuario.apellido}}<b class="caret"></b>
+                    </a>
+                    <div class="dropdown-menu dropdown-menu-right">
+                        <a class="dropdown-item" style="cursor: pointer;" (click)=logout() ><i class="fa fa-fw fa-power-off"></i> Salir</a>
+                    </div>
+                </div>
+            </ul>
+        </div>
+    </nav>
+</div>
diff --git a/src/app/shared/components/header/header.component.scss b/src/app/shared/components/header/header.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..0be7ecea4e696e1d7b95c2da4f1bec6ccc24ed14
--- /dev/null
+++ b/src/app/shared/components/header/header.component.scss
@@ -0,0 +1,75 @@
+$topnav-background-color: #222;
+.topnav {
+    background: linear-gradient(#0085ff, #31dc7d);
+    background: linear-gradient(to right, #0085ff, rgb(95, 0, 0)) !important;
+
+    border-radius: 0;
+    background-color: $topnav-background-color;
+    padding : 6px;
+    z-index:2;
+    .text-center{
+        text-align: center;
+        padding-left : 0;
+        cursor: pointer;
+    }
+    .top-right-nav{
+        .buy-now{
+            a{
+                color:#999;
+            }
+        }
+        .dropdown-menu{
+            top: 40px;
+            right: -5px;
+            left : auto;
+            .message-preview{
+                .media{
+                    .media-body{
+                        .media-heading{
+                            font-size: 14px;
+                            font-weight: bold;
+                            margin-bottom : 0;
+                        }
+                        p{
+                            margin : 0;
+                        }
+                        p.last{
+                            font-size : 13px;
+                            margin-bottom: 0;
+                        }
+                    }
+                }
+            }
+            hr {
+                margin-top: 1px;
+                margin-bottom: 4px;
+            }
+        }
+    }
+}
+.messages {
+    width: 300px;
+    .media {
+        border-bottom: 1px solid #DDD;
+        padding: 5px 10px;
+        &:last-child {
+            border-bottom: none;
+        }
+    }
+    .media-body {
+        h5 {
+            font-size: 13px;
+            font-weight: 600;
+        }
+        .small {
+            margin: 0;
+        }
+        .last {
+            font-size: 12px;
+            margin: 0;
+        }
+    }
+}
+.header .navbar {
+    background: $topnav-background-color !important;
+}
diff --git a/src/app/shared/components/header/header.component.spec.ts b/src/app/shared/components/header/header.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d0479d7d071a5e6d19a74d195ff34b2edeb5616
--- /dev/null
+++ b/src/app/shared/components/header/header.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeaderComponent } from './header.component';
+
+describe('HeaderComponent', () => {
+  let component: HeaderComponent;
+  let fixture: ComponentFixture<HeaderComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ HeaderComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(HeaderComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/shared/components/header/header.component.ts b/src/app/shared/components/header/header.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..44ea333e5d7eb6dcb61b83b97d74dc190933c35c
--- /dev/null
+++ b/src/app/shared/components/header/header.component.ts
@@ -0,0 +1,33 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { AuthenticationService } from '../../services/authentication.service';
+import { SessionService } from '../../services/session.service';
+import { Usuario } from '../../objects/usuario';
+
+@Component({
+    selector: 'app-header',
+    templateUrl: './header.component.html',
+    styleUrls: ['./header.component.scss']
+})
+export class HeaderComponent implements OnInit {
+    usuario: Usuario;
+    constructor(private authService: AuthenticationService, private router : Router, private sessionService : SessionService) {
+        this.usuario = authService.getUser();
+    }
+    ngOnInit() {}
+
+    toggleSidebar(event) {
+        event.stopPropagation();
+        const dom: any = document.querySelector('body');
+        dom.classList.toggle('push-right');
+    }
+    rltAndLtr() {
+        const dom: any = document.querySelector('body');
+        dom.classList.toggle('rtl');
+    }
+
+    logout(){
+        this.sessionService.reset();
+        this.router.navigate(['/login']);
+    }
+}
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..85f0512037b4e1a3f2d4f17c30b645b9d7f9578f
--- /dev/null
+++ b/src/app/shared/components/index.ts
@@ -0,0 +1,2 @@
+export * from './header/header.component';
+export * from './sidebar/sidebar.component';
diff --git a/src/app/shared/components/sidebar/sidebar.component.html b/src/app/shared/components/sidebar/sidebar.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..dd605a31eb2c3c7442e5ef503238e3dbcf6845df
--- /dev/null
+++ b/src/app/shared/components/sidebar/sidebar.component.html
@@ -0,0 +1,15 @@
+ <nav class="sidebar" #sidebarNav [ngClass]="{sidebarPushRight: isActive}" style="background: #036b9a !important;">
+    <ul class="list-group">
+        <a [routerLink]="['/matefun']" (click)=toggleSidebar() [routerLinkActive]="['router-link-active']" class="list-group-item"
+            style="color: white;">
+            <i class="fa fa-fw fa-desktop"></i>&nbsp;Programa
+        </a>
+        <a [routerLink]="['/archivos']" (click)=toggleSidebar() [routerLinkActive]="['router-link-active']" class="list-group-item" style="color: white;">
+            <i class="fa fa-fw fa-file-o"></i>&nbsp;Archivos
+        </a>
+        <a *ngIf="esDocente()" [routerLink]="['/grupos']" (click)=toggleSidebar() [routerLinkActive]="['router-link-active']" class="list-group-item" style="color: white;">
+            <i class="fa fa-fw fa-users"></i>&nbsp;Grupos
+        </a>
+        
+    </ul>
+</nav>
diff --git a/src/app/shared/components/sidebar/sidebar.component.scss b/src/app/shared/components/sidebar/sidebar.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..fed31138c6b5e898b9215f4ed06ef5b9709d5960
--- /dev/null
+++ b/src/app/shared/components/sidebar/sidebar.component.scss
@@ -0,0 +1,123 @@
+$topnav-background-color: #036b9a;
+.sidebar{
+    border-radius: 0;
+    position: fixed;
+    z-index: 1000;
+    top: 55px;
+    left: 235px;
+    width: 235px;
+    margin-left: -235px;
+    border: none;
+    border-radius: 0;
+    overflow-y: auto;
+    background-color: $topnav-background-color;
+    bottom: 0;
+    overflow-x: hidden;
+    padding-bottom: 40px;
+    -webkit-transition: all 0.2s ease-in-out;
+       -moz-transition: all 0.2s ease-in-out;
+        -ms-transition: all 0.2s ease-in-out;
+         -o-transition: all 0.2s ease-in-out;
+            transition: all 0.2s ease-in-out;
+    // border-top: 1px solid rgba(255,255,255,0.3);
+    .list-group{
+        a.list-group-item{
+            background: $topnav-background-color;
+            border: 0;
+            border-radius: 0;
+            color: #999;
+            text-decoration: none;
+        }
+        a:hover{
+            background: darken($topnav-background-color, 5%);
+            color: #fff;
+        }
+        a.router-link-active{
+            background: darken($topnav-background-color, 5%);
+            color: #fff;
+        }
+    }
+    .sidebar-dropdown{
+        *:focus{
+            border-radius: none;
+            border:none;
+        }
+        .panel-title{
+            font-size : 1rem;
+            height : 50px;
+            margin-bottom:0;
+            a{
+                color : #999;
+                text-decoration : none;
+                font-weight:400;
+                background:$topnav-background-color;
+                span{
+                    position: relative;
+                    display: block;
+                    padding: .75rem 1.5rem;
+                    padding-top:1rem;
+                }
+            }
+            a:hover,a:focus{
+                color: #fff;
+                outline: none;
+                outline-offset: -2px;
+            }
+        }
+        .panel-title:hover{
+            background: darken($topnav-background-color, 5%);
+        }
+        .panel-collapse{
+            border-radious :0;
+            border : none;
+            .panel-body{
+                .list-group-item{
+                    border-radius : 0;
+                    background-color: $topnav-background-color;
+                    border: 0 solid transparent;
+                    a{
+                        color:#999;
+                    }
+                    a:hover{
+                        color:#FFF;
+                    }
+                }
+                .list-group-item:hover{
+                    background : darken($topnav-background-color, 5%);
+                }
+            }
+        }
+    }
+}
+.nested-menu {
+    .list-group-item {
+        cursor: pointer;
+    }
+    .nested {
+        list-style-type: none;
+    }
+    ul.submenu {
+        display: none;
+        height: 0;
+    }
+    & .expand {
+        ul.submenu {
+            display: block;
+            list-style-type: none;
+            height: auto;
+            li {
+                a {
+                    color: #FFF;
+                    padding: 10px;
+                    display: block;
+                }
+            }
+        }
+    }
+}
+// @media screen and (max-width: 992px) {
+    .sidebar {
+        top: 54px;
+        left: 0px;
+    }
+// }
diff --git a/src/app/shared/components/sidebar/sidebar.component.spec.ts b/src/app/shared/components/sidebar/sidebar.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f29709fd1f42214cfdeca486b3d7e335f99ec858
--- /dev/null
+++ b/src/app/shared/components/sidebar/sidebar.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SidebarComponent } from './sidebar.component';
+
+describe('SidebarComponent', () => {
+  let component: SidebarComponent;
+  let fixture: ComponentFixture<SidebarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ SidebarComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(SidebarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/shared/components/sidebar/sidebar.component.ts b/src/app/shared/components/sidebar/sidebar.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa8516b949e047ad45b9a14d090994b6b88c994e
--- /dev/null
+++ b/src/app/shared/components/sidebar/sidebar.component.ts
@@ -0,0 +1,62 @@
+import { Component, HostListener, ViewChild, ElementRef } from '@angular/core';
+
+import { AuthenticationService } from '../../services/authentication.service';
+import { Usuario } from '../../objects/usuario';
+
+@Component({
+    selector: 'app-sidebar',
+    templateUrl: './sidebar.component.html',
+    styleUrls: ['./sidebar.component.scss']
+})
+export class SidebarComponent {
+    isActive = false;
+    showMenu = '';
+
+    usuario: Usuario;
+
+    constructor(private authService: AuthenticationService) {
+        this.usuario = authService.getUser();
+    }
+
+    eventCalled() {
+        this.isActive = !this.isActive;
+    }
+
+    addExpandClass(element: any) {
+        if (element === this.showMenu) {
+            this.showMenu = '0';
+        } else {
+            this.showMenu = element;
+        }
+    }
+
+    toggleSidebar() {
+        const dom: any = document.querySelector('body');
+        dom.classList.toggle('push-right');
+    }
+
+    esAlumno(){
+        return this.usuario.tipo == "alumno";
+    }
+
+    esDocente(){
+        return this.usuario.tipo == "docente";
+    }
+
+    @ViewChild('sidebarNav') sidebarNav: ElementRef;
+
+    @HostListener('document:click', ['$event'])
+    private documentClicked(event: MouseEvent): void {
+
+        // Nav is open
+        const dom: any = document.querySelector('body');
+        if(dom.classList.contains('push-right')){
+            // Not clicked on self element
+            if (!this.sidebarNav.nativeElement.contains(event.target)) {
+                
+                dom.classList.remove('push-right');
+            }
+        }
+    }
+
+}
diff --git a/src/app/shared/config.ts b/src/app/shared/config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..269b0832a942fff97480651e814f48f506bb2fa2
--- /dev/null
+++ b/src/app/shared/config.ts
@@ -0,0 +1,9 @@
+//export const SERVER = 'https://matefun.mybluemix.net';
+//export const GHCI_URL = 'wss://matefun.mybluemix.net/endpoint';
+
+//export const SERVER = 'http://localhost:9090';
+//export const GHCI_URL = 'ws://localhost:9090/endpoint';
+
+//Configuracion dinamica pensando en servidor con ip dinamica
+export const SERVER = window.location.protocol + '//' + window.location.host;//'http://localhost:9090';
+export const GHCI_URL = window.location.protocol == 'http:'?  'ws://'+window.location.host+'/endpoint': 'wss://'+window.location.host+'/endpoint';
diff --git a/src/app/shared/guards/auth.guard.spec.ts b/src/app/shared/guards/auth.guard.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ed05ee8111b45cec3834ee041fda1ec5a79d210
--- /dev/null
+++ b/src/app/shared/guards/auth.guard.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, async, inject } from '@angular/core/testing';
+
+import { AuthGuard } from './auth.guard';
+
+describe('AuthGuard', () => {
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [AuthGuard]
+    });
+  });
+
+  it('should ...', inject([AuthGuard], (guard: AuthGuard) => {
+    expect(guard).toBeTruthy();
+  }));
+});
diff --git a/src/app/shared/guards/auth.guard.ts b/src/app/shared/guards/auth.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22a684d1c5e31bd9468cf58d13bbb0a4fedf1f7d
--- /dev/null
+++ b/src/app/shared/guards/auth.guard.ts
@@ -0,0 +1,20 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+
+	constructor(private router: Router){
+
+	}
+
+  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
+    
+    if(sessionStorage.getItem('currentUser')){
+    	return true;
+    }
+    this.router.navigate(['/login']);
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/src/app/shared/index.ts b/src/app/shared/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f1bd6837c027e4fe47f9044fbc61921e567901fb
--- /dev/null
+++ b/src/app/shared/index.ts
@@ -0,0 +1,3 @@
+export * from './pipes/shared-pipes.module';
+export * from './components';
+export * from './modules';
diff --git a/src/app/shared/modal/confirm.component.ts b/src/app/shared/modal/confirm.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..24b626427aa060c9faedd0e68e469f7dc5c628cc
--- /dev/null
+++ b/src/app/shared/modal/confirm.component.ts
@@ -0,0 +1,42 @@
+import { Component } from '@angular/core';
+import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
+export interface ConfirmModel {
+  title:string;
+  message:string;
+  confirmText: string;
+  cancelText:string;
+}
+@Component({  
+    selector: 'confirm',
+    template: `<div class="modal-dialog" style="margin-top:100px;">
+                <div class="modal-content">
+                   <div class="modal-header">
+                     <h4 class="modal-title">{{title || 'Confirm'}}</h4>
+                     <button type="button" class="close" (click)="close()" >&times;</button>
+                   </div>
+                   <div class="modal-body">
+                     <p style="white-space: pre;">{{message || ''}}</p>
+                   </div>
+                   <div class="modal-footer">
+                     <button type="button" class="btn btn-primary" (click)="confirm()">{{confirmText || 'Confirmar'}}</button>
+                     <button type="button" class="btn btn-default" (click)="close()" >{{cancelText || 'Cancelar'}}</button>
+                   </div>
+                 </div>
+              </div>`
+})
+export class ConfirmComponent extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
+  title: string;
+  message: string;
+  confirmText: string;
+  cancelText:string;
+
+  constructor(dialogService: DialogService) {
+    super(dialogService);
+  }
+  confirm() {
+    // we set dialog result as true on click on confirm button, 
+    // then we can get dialog result from caller code 
+    this.result = true;
+    this.close();
+  }
+}
\ No newline at end of file
diff --git a/src/app/shared/modules/index.ts b/src/app/shared/modules/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7afde6126c9534188e86e3872197a7e7d91888d4
--- /dev/null
+++ b/src/app/shared/modules/index.ts
@@ -0,0 +1,2 @@
+export * from './stat/stat.module';
+export * from './page-header/page-header.module';
diff --git a/src/app/shared/modules/page-header/page-header.component.html b/src/app/shared/modules/page-header/page-header.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8cd0c21e97990219d1ebe78e58384e42f5da548a
--- /dev/null
+++ b/src/app/shared/modules/page-header/page-header.component.html
@@ -0,0 +1,13 @@
+<div class="row">
+    <div class="col-xl-12">
+        <h2 class="page-header">
+            {{heading}}
+        </h2>
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item">
+                <i class="fa fa-dashboard"></i> <a href="Javascript:void(0)" [routerLink]="['/dashboard']">Dashboard</a>
+            </li>
+            <li class="breadcrumb-item active"><i class="fa {{icon}}"></i> {{heading}}</li>
+        </ol>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/shared/modules/page-header/page-header.component.scss b/src/app/shared/modules/page-header/page-header.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/modules/page-header/page-header.component.spec.ts b/src/app/shared/modules/page-header/page-header.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8b7949dafc8efcd7cd5291e0fa290dccedcce02d
--- /dev/null
+++ b/src/app/shared/modules/page-header/page-header.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageHeaderComponent } from './page-header.component';
+
+describe('PageHeaderComponent', () => {
+  let component: PageHeaderComponent;
+  let fixture: ComponentFixture<PageHeaderComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PageHeaderComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PageHeaderComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/shared/modules/page-header/page-header.component.ts b/src/app/shared/modules/page-header/page-header.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7bc689cd5ff0b25e27790277654228f5264ec1c9
--- /dev/null
+++ b/src/app/shared/modules/page-header/page-header.component.ts
@@ -0,0 +1,11 @@
+import { Component, Input } from '@angular/core';
+
+@Component({
+    selector: 'app-page-header',
+    templateUrl: './page-header.component.html',
+    styleUrls: ['./page-header.component.scss']
+})
+export class PageHeaderComponent {
+    @Input() heading: string;
+    @Input() icon: string;
+}
diff --git a/src/app/shared/modules/page-header/page-header.module.ts b/src/app/shared/modules/page-header/page-header.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6ee69a6695ce903a97f6b73e2cd0fdfd67ed721
--- /dev/null
+++ b/src/app/shared/modules/page-header/page-header.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+
+import { PageHeaderComponent } from './page-header.component';
+
+@NgModule({
+    imports: [
+        CommonModule,
+        RouterModule
+    ],
+    declarations: [PageHeaderComponent],
+    exports: [PageHeaderComponent]
+})
+export class PageHeaderModule { }
diff --git a/src/app/shared/modules/stat/stat.component.html b/src/app/shared/modules/stat/stat.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..08ba30f16e455ea21d244e2529943e26b294c50e
--- /dev/null
+++ b/src/app/shared/modules/stat/stat.component.html
@@ -0,0 +1,19 @@
+<div class="card card-inverse {{bgClass}}">
+    <div class="card-header">
+        <div class="row">
+            <div class="col col-xs-3">
+                <i class="fa {{icon}} fa-5x"></i>
+            </div>
+            <div class="col col-xs-9 text-right">
+                <div class="d-block huge">{{count}}</div>
+                <div class="d-block">{{label}}</div>
+            </div>
+        </div>
+    </div>
+    <div class="card-footer">
+        <span class="float-left">View Details {{data}}</span>
+        <a href="javascript:void(0)" class="float-right card-inverse">
+            <span ><i class="fa fa-arrow-circle-right"></i></span>
+        </a>
+    </div>
+</div>
diff --git a/src/app/shared/modules/stat/stat.component.scss b/src/app/shared/modules/stat/stat.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/modules/stat/stat.component.spec.ts b/src/app/shared/modules/stat/stat.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a9b50f6c6e41afc51dad59d287898b6ce721adc
--- /dev/null
+++ b/src/app/shared/modules/stat/stat.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { StatComponent } from './stat.component';
+
+describe('StatComponent', () => {
+  let component: StatComponent;
+  let fixture: ComponentFixture<StatComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ StatComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(StatComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/shared/modules/stat/stat.component.ts b/src/app/shared/modules/stat/stat.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..12aaca07fe52cf773f0a1cce101d1732c4b2e986
--- /dev/null
+++ b/src/app/shared/modules/stat/stat.component.ts
@@ -0,0 +1,19 @@
+import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
+
+@Component({
+    selector: 'app-stat',
+    templateUrl: './stat.component.html',
+    styleUrls: ['./stat.component.scss']
+})
+export class StatComponent implements OnInit {
+    @Input() bgClass: string;
+    @Input() icon: string;
+    @Input() count: number;
+    @Input() label: string;
+    @Input() data: number;
+    @Output() event: EventEmitter<any> = new EventEmitter();
+
+    constructor() { }
+
+    ngOnInit() {}
+}
diff --git a/src/app/shared/modules/stat/stat.module.ts b/src/app/shared/modules/stat/stat.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b30f1b3d058341d1d4d599d82ba35e894e5c929
--- /dev/null
+++ b/src/app/shared/modules/stat/stat.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { StatComponent } from './stat.component';
+
+@NgModule({
+    imports: [
+        CommonModule
+    ],
+    declarations: [StatComponent],
+    exports: [StatComponent]
+})
+export class StatModule { }
diff --git a/src/app/shared/objects/archivo.ts b/src/app/shared/objects/archivo.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6090668eff1dae65e4c98ad3b0f22e47a5004ab2
--- /dev/null
+++ b/src/app/shared/objects/archivo.ts
@@ -0,0 +1,28 @@
+export class Evaluacion{
+  evaluacionId: number;
+  cedulaDocente: string;
+  fecha: Date;
+  nota: number;
+  descripcion: string;
+}
+
+export class Archivo {
+  id: number;
+  nombre: string;
+  contenido: string;
+  fechaCreacion: Date;
+  cedulaCreador: string;
+  editable: boolean;
+  padreId: number;
+  archivoOrigenId: number;
+  archivos: Archivo[];
+  directorio :boolean;
+  estado: string;
+  eliminado:boolean;
+  evaluacion: Evaluacion;
+  
+  constructor(){
+  	
+  }
+
+}
\ No newline at end of file
diff --git a/src/app/shared/objects/grupo.ts b/src/app/shared/objects/grupo.ts
new file mode 100644
index 0000000000000000000000000000000000000000..43bff4770c2bdd741cb72eca7daaad2695295d47
--- /dev/null
+++ b/src/app/shared/objects/grupo.ts
@@ -0,0 +1,21 @@
+import { Archivo } from './archivo';
+import { Usuario } from './usuario';
+
+export class Grupo {
+  anio: number;
+  grado: number;
+  grupo: string;
+  liceoId: number;
+  archivos: Archivo[];
+  alumnos: Usuario[];
+
+  constructor(anio: number, grado: number, grupo: string,liceoId:number, archivos: Archivo[], alumnos:Usuario[]){
+    this.anio = anio;
+    this.grado = grado;
+    this.grupo = grupo;
+    this.liceoId = liceoId;
+    this.archivos = archivos;    
+    this.alumnos = alumnos;  	
+  }
+
+}
\ No newline at end of file
diff --git a/src/app/shared/objects/usuario.ts b/src/app/shared/objects/usuario.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4218c62bf91b1294d4d0f5a291e1436ba63ce558
--- /dev/null
+++ b/src/app/shared/objects/usuario.ts
@@ -0,0 +1,15 @@
+export class Configuracion{
+	themeEditor: string;
+	fontSizeEditor: number;
+	argumentoI:boolean;
+	argumentoF:boolean;
+}
+
+export class Usuario{
+	token: string;
+	cedula: string;
+    nombre: string;
+    apellido: string;
+    tipo: string;
+    configuracion: Configuracion;
+}
\ No newline at end of file
diff --git a/src/app/shared/pipes/filter.pipe.ts b/src/app/shared/pipes/filter.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0faa74c0d09bbb1f211f23e88718caa4b77126a3
--- /dev/null
+++ b/src/app/shared/pipes/filter.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+    name: 'filter',
+    pure: false
+})
+export class FilterPipe implements PipeTransform {
+   transform(items: any[], field : string, value : string): any[] {  
+        if (!items) return [];        
+        return items.filter(it => it[field].toLowerCase().indexOf(value.toLowerCase()) != -1);
+    }
+}
\ No newline at end of file
diff --git a/src/app/shared/pipes/shared-pipes.module.ts b/src/app/shared/pipes/shared-pipes.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b4b1c16631a25cfbc53c7b5369ee3f52ac24d656
--- /dev/null
+++ b/src/app/shared/pipes/shared-pipes.module.ts
@@ -0,0 +1,10 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+@NgModule({
+    imports: [
+        CommonModule
+    ],
+    declarations: []
+})
+export class SharedPipesModule { }
diff --git a/src/app/shared/services/authentication.service.ts b/src/app/shared/services/authentication.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..23c2a24b908e3431f000a2762e7ab12403c87218
--- /dev/null
+++ b/src/app/shared/services/authentication.service.ts
@@ -0,0 +1,50 @@
+import { Injectable } from '@angular/core';
+import { Http, Headers, Response, RequestOptions } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+import { hash } from '../utils/sha1'
+import { Usuario } from '../objects/usuario';
+import 'rxjs/add/operator/map'
+import { SERVER } from '../config'; 
+
+@Injectable()
+export class AuthenticationService {
+    constructor(private http: Http) { }
+
+    login(cedula: string, password: string) {
+        let headers = new Headers({ 'Content-Type': 'application/json' });
+        let options = new RequestOptions({ headers: headers });
+        return this.http.post(SERVER+'/servicios/login', JSON.stringify({ "cedula": cedula, "password": password }),options)
+            .map((response: Response) => {
+                // login successful if there's a jwt token in the response
+                let user = response.json();
+                // if (user && user.token) {
+                    // store user details and jwt token in local storage to keep user logged in between page refreshes
+                    sessionStorage.setItem('currentUser', JSON.stringify(user));
+                // }
+            });
+    }
+
+    getUser():Usuario{
+        return JSON.parse(sessionStorage.getItem('currentUser'));
+    }
+
+    getUserConfig(){
+         return JSON.parse(sessionStorage.getItem('currentUser')).configuracion;;
+    }
+
+    getToken(){
+        return JSON.parse(sessionStorage.getItem('currentUser')).token;
+    }
+
+    setUserConfig(config){
+        var user = JSON.parse(sessionStorage.getItem('currentUser'));
+        user.configuracion = config;
+        sessionStorage.setItem('currentUser', JSON.stringify(user));
+    }
+
+
+    logout() {
+        // remove user from local storage to log user out
+        sessionStorage.removeItem('currentUser');
+    }
+}
\ No newline at end of file
diff --git a/src/app/shared/services/ghci.service.ts b/src/app/shared/services/ghci.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6bb4f0e80cf57fa91a0cca7509215911eab3fcb2
--- /dev/null
+++ b/src/app/shared/services/ghci.service.ts
@@ -0,0 +1,458 @@
+import { Injectable,ViewChild,ElementRef } from '@angular/core';
+import { Observable, Subject } from 'rxjs/Rx';
+import { WebsocketService } from './websocket.service';
+import { AuthenticationService } from './authentication.service';
+import { GHCI_URL } from '../config';
+
+declare var $:any;
+declare var that :any ;
+const regex = /^color (errores|input|output|logs) (\d)$/g;
+
+import 'tippy.js/dist/tippy';
+
+@Injectable()
+export class GHCIService {
+	public messages: Subject<any> = new Subject<any>();
+	private connection = undefined;
+	private cons = undefined;
+	private modoAvanzado: boolean = false;     // indica si debe mostrarse todo lo que ocurre en el intérprete.
+	private clear:boolean = false;
+	private error :string ="";
+	private warnings: any = [];
+	private codemirrorRef :any = null;
+
+	private waitingForError : boolean = false;
+	private waitingForWarning : boolean = false;
+	private waitingForWarning2 : boolean = false;
+	private warningText :string = "";
+	private lastError : number = -1;
+	private lastWarning :number = -1;
+
+	private console_error_class : string = "jqconsole-asd";
+
+	consoleBuffer = [];
+	
+	constructor(private authService:AuthenticationService){
+
+		this.conectarWS(GHCI_URL, authService.getUser().cedula, authService.getToken());
+		setInterval( this.checkConnection.bind(this), 5000);	
+		setInterval( this.doPing.bind(this), 30000);	
+	}
+
+	setCodemirrorRef(instance){
+		this.codemirrorRef = instance;
+	}
+
+	clearWarnings(){
+		this.warnings = [];
+	}
+
+	getWarnings(){
+		return this.warnings;
+	}
+
+	loadFile(fileId, dependencias) {
+		var message = {
+			'token': this.authService.getToken(),
+			'load': fileId,
+			'dependencias' :[]
+
+		};
+		for(var i in dependencias){
+			message.dependencias.push(dependencias[i]);
+		};
+		this.connection.send(JSON.stringify(message));
+	}
+	copyFile(fileId){
+		var message = {
+			'token': this.authService.getToken(),
+			'copy': fileId
+		};
+		this.connection.send(JSON.stringify(message));
+	}
+	reiniciarInterprete(){
+		var message = {
+			'token': this.authService.getToken(),
+			'restart': ''
+		};
+		console.log(message);
+		this.connection.send(JSON.stringify(message));
+	}
+
+	regex: string  = '/(<svg.*\s*.*<\/svg>)/g';
+	
+	consoleRef:any;
+
+	conectarWS(wsUrl, cedula, token){
+		if(cedula && !this.connection || this.connection.readyState == WebSocket.CLOSED){
+			this.connection = new WebSocket(wsUrl+"/"+cedula+"/"+token);
+
+			this.connection.onopen = function(){
+				console.log('Conexión con web socket exitosa');
+			}
+			this.connection.onclose = function(){
+				console.log('Conexión con web socket cerrada');
+			}
+			this.connection.onmessage = this.onMessage.bind(this);
+		}
+	}
+
+	logConsole(text){
+		if(this.consoleRef){
+			this.consoleRef.Write(text, 'jqconsole-logs'); 
+		}else{
+			this.consoleBuffer.unshift({text: text, type:'jqconsole-logs'})
+			setTimeout(this.checkConsole.bind(this),100);
+		}
+	}
+
+	outputConsole(text){
+		if(this.consoleRef){
+			this.consoleRef.Write(text, 'jqconsole-output'); 
+		}else{
+			this.consoleBuffer.unshift({text: text, type:'jqconsole-output'})
+			setTimeout(this.checkConsole.bind(this),100);
+		}
+	}
+
+	errorConsole(text){
+		if(this.consoleRef){
+			this.consoleRef.Write(text, 'jqconsole-errors'); 
+		}else{
+			this.consoleBuffer.unshift({text: text, type:'jqconsole-errors'})
+			setTimeout(this.checkConsole.bind(this),100);
+		}
+	}
+	hayError(text){
+		var line = -1;
+		if(this.waitingForError){
+			var line = this.lastError;
+			if(this.codemirrorRef!==null){
+		        var makeMarker = function() {
+	              var marker = document.createElement("div");
+	              marker.id = "error_" + line.toString();
+	              marker.style.width = "15px";
+	              marker.title = JSON.parse(text).resultado.split("OUT")[1].trim();
+	              marker.style.height = "15px";
+	              marker.style.marginLeft = "-5px";
+	              marker.style.cursor = "pointer";
+	              marker.style["background-image"] = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=')";
+	              marker.innerHTML = "<a href='@' title='cuidado , advertencia matefun'></a>";
+	              return marker;
+	            }
+	            this.codemirrorRef.setGutterMarker(line, "breakpoints", makeMarker());
+	           	this.waitingForError = false;
+	           	this.lastError = -1;
+			}
+		}else {
+			try{
+			var line = Number(JSON.parse(text).resultado.split("en línea")[1].split(",")[0].trim())-1;
+			this.waitingForError = true;	
+			this.lastError = line;
+			
+		}catch(err){
+		}
+		return false;
+
+		}
+
+	}
+	resetGutters(){
+		if(this.codemirrorRef!==null){
+			this.codemirrorRef.clearGutter("breakpoints");
+		}
+	}
+		hayWarnings(text){
+		var line = -1;
+		if(this.waitingForWarning){
+			if(this.waitingForWarning2){
+				//se completa el mensaje de warning
+				this.warningText = this.warningText.trim() + "\n" + JSON.parse(text).resultado.split("OUT")[1].trim();
+				this.waitingForWarning = false;
+				this.waitingForWarning2 = false;
+				var line = this.lastWarning;
+				this.lastWarning = -1;
+				var title = this.warningText;
+				if(this.codemirrorRef!==null){
+			        var makeMarker = function() {
+		              var marker = document.createElement("div");
+		              marker.style.width = "15px";
+		              marker.style.height = "15px";
+		              marker.style.marginLeft = "-5px";
+		              marker.style.cursor = "pointer";
+		              marker.innerHTML = "<a href='@' title='cuidado , advertencia matefun'></a>";
+		              marker.title = title;
+		              marker.style["background-image"] = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=')";
+		              marker.innerHTML = "";
+		              return marker;
+		            }
+		            this.codemirrorRef.setGutterMarker(line, "breakpoints", makeMarker());
+				}
+			}else {
+				this.waitingForWarning2 = true;
+				this.warningText = JSON.parse(text).resultado.split("OUT")[1];
+			}
+		}else {
+			try{
+				line = Number(JSON.parse(text).resultado.split("OUTAdvertencia:")[1].trim().split("línea:")[1].split(" ")[1])-1;
+				this.waitingForWarning = true;
+				this.waitingForWarning2 = false;
+				this.lastWarning = line;
+				
+			}catch(err){
+				line = -1
+			}
+			this.warnings.push(line);
+			return line!==-1;	
+		}
+		
+	}
+
+	onMessage(e){
+		if(this.modoAvanzado){
+			this.logConsole('Respuesta: ' + e.data + '\n');
+		}
+		if(this.clear){
+			this.clearConsole();
+		}
+		var server_message = e.data;
+
+		if(this.hayError(server_message)){
+			this.error = "Error";
+		} else {
+			this.error = "";
+		}
+		if(this.hayWarnings(server_message)){
+			//parseo los warnings.
+			//los cargo en la variable de warnings.
+		}
+
+		var json_server_message = JSON.parse(server_message);
+		if(json_server_message.tipo=='salida'){
+
+			if(json_server_message.resultado.includes("<svg")){
+				var svg = json_server_message.resultado;
+				$("#svgHaskell").html(svg);
+			} else {
+				var line = json_server_message.resultado.trim();
+				if(line.startsWith("OUT")){
+					this.outputConsole(line.substring(3) + '\n');     
+				}else if(line.startsWith("IN")){
+					var promptText = line.substring(3);
+					if(this.consoleRef===undefined){
+						this.renderConsole();
+					}
+					this.consoleRef.SetPromptLabel(promptText);
+					this.consoleRef.SetPromptText('');
+					this.startPrompt.bind(this);
+					this.startPrompt();
+				}
+				
+			}
+
+		} else if (json_server_message.tipo=='error'){
+			if(this.modoAvanzado){
+				this.errorConsole(json_server_message.resultado  + '\n');	
+			}
+		} else if (json_server_message.tipo == 'prompt'){
+			// console.log(json_server_message.resultado);
+			// console.log(this.consoleRef);
+			// console.log(this.consoleRef.SetPromptLabel);
+			// this.consoleRef.SetPromptLabel.bind(this);
+			this.consoleRef.SetPromptLabel(json_server_message.resultado+'>');
+			this.consoleRef.SetPromptText('');
+			this.startPrompt.bind(this);
+			this.startPrompt();
+			// console.log(x);
+		}else if (json_server_message.tipo == 'canvas' || json_server_message.tipo == 'animacion' || json_server_message.tipo == 'graph'){
+			this.messages.next(json_server_message);
+		}
+
+	}
+
+	checkConsole(){
+		if(this.consoleRef){
+			while(this.consoleBuffer.length > 0){
+				var bufferedMessage = this.consoleBuffer.pop();
+				this.consoleRef.Write(bufferedMessage.text,bufferedMessage.type);
+			}
+		}else{
+			setTimeout(this.checkConsole.bind(this),500);
+		}
+	}
+
+	checkConnection(){
+		if(!this.connection || this.connection.readyState == WebSocket.CLOSED){
+			this.conectarWS(GHCI_URL, this.authService.getUser().cedula, this.authService.getToken());
+		}		
+	}
+
+	doPing(){
+		if(this.connection && this.connection.readyState == WebSocket.OPEN){
+			var message = {
+				'token': this.authService.getToken(),
+				'ping': ''
+			};
+			this.connection.send(JSON.stringify(message));
+		}		
+	}
+
+	sendLine(line) {
+		if(line.trim()!==""){
+			var message = {
+				'token': this.authService.getToken(),
+				'comando': line
+			};
+			console.log(message);
+			if(this.connection && this.connection.readyState == WebSocket.OPEN){
+				this.connection.send(JSON.stringify(message));
+			}else{
+				this.errorConsole("Sin conexión al servidor...");
+			}
+		}
+	}
+
+	startPrompt() {
+		// Start the prompt with history enabled.
+		this.jqconsole.Prompt(true, this.callback.bind(this));
+	};
+
+	focusConsole(){
+		this.jqconsole.Focus();
+	};
+
+	clearConsole(){
+		this.consoleRef.Reset();
+		this.startPrompt.bind(this);
+		this.startPrompt();
+		this.clear = false;
+	};
+
+	callback(input){
+		// Output input with the class jqconsole-output. 
+		var ejecutar: boolean;
+		ejecutar = this.procesarInput(input);
+		if(ejecutar) {
+			if(this.modoAvanzado){
+				this.logConsole("Ejecutar: " + input + '\n');
+			}
+			this.sendLine.bind(this);
+			this.sendLine(input);
+		}
+		this.startPrompt.bind(this);
+		this.startPrompt();
+	}
+
+	procesarInput(input){
+		var _input: string;
+		var send: boolean = false;
+		_input = input.trim().toLocaleLowerCase();
+		if(_input==="limpiar"){
+			this.clearConsole();
+		} else if(_input==="modo avanzado") {
+			this.modoAvanzado= true;
+			this.logConsole("Modo avanzado activado\n");
+		} else if(_input==="modo normal"){
+			this.modoAvanzado= false;
+			this.logConsole("Modo avanzado desactivado\n");
+		} else if(_input==="listar colores"){
+			this.outputConsole("1 - Azul\n");
+			this.outputConsole("2 - Rojo\n");
+			this.outputConsole("3 - Verde\n");
+			this.outputConsole("4 - Verde oscuro\n");
+			this.outputConsole("5 - Blanco\n");
+			this.outputConsole("6 - Naranja\n");
+			this.outputConsole("7 - Gris\n");
+			this.outputConsole("8 - Gris oscuro\n");
+			this.outputConsole("9 - Marrón\n");
+		} else if(_input.match(regex)!==null) {
+			var _tipoTexto :string = _input.split(" ")[1];
+			var _color : string = input.split(" ")[2];
+			this.jqconsoleColor(_color,_tipoTexto);
+			if(this.modoAvanzado){
+				this.logConsole("Color " + _tipoTexto + " seleccionado\n");	
+			}
+		} else {
+			send = true;
+		}	
+		return send;
+	}
+
+	getCSSColorName(n){
+		if(n==="1"){
+			return "rgb(77, 77, 255)";
+		} else if(n==="2"){
+			return "rgb(255, 26, 26)";
+		} else if (n==="3"){
+			return "rgb(0, 179, 60)";
+		} else if(n==="4"){
+			return "rgb(0, 77, 0)";
+		} else if(n==="5"){
+			return "rgb(255, 255, 255)";
+		} else if(n==="6"){
+			return "rgb(255, 133, 51)";
+		} else if(n==="7"){
+			return "rgb(204, 204, 179)";
+		} else if(n==="8"){
+			return "rgb(102, 102, 102)";
+		} else if(n==="9"){
+			return "rgb(101, 27, 27)";
+		}
+	}
+
+	getJQConsoleClass(jqconsoletext){
+		if(jqconsoletext==="input"){
+			return '.jqconsole-prompt';
+		} else if(jqconsoletext==="error"){
+			return '.jqconsole-error';
+		} else if (jqconsoletext==="logs"){
+			return '.jqconsole-logs';
+		} else if(jqconsoletext==="output"){
+			return '.jqconsole-output';
+		}
+	}
+
+	jqconsoleColor(color, clase){
+		var cssColor:string = this.getCSSColorName(color);
+		var jqConsoleClass: string = this.getJQConsoleClass(clase);
+
+
+		var style = document.createElement('style');
+		style.type = 'text/css';
+		style.innerHTML = jqConsoleClass +' { color: ' + cssColor + '; }';
+		document.getElementsByTagName('head')[0].appendChild(style);
+
+		if(jqConsoleClass==='.jqconsole-prompt'){
+			var style2 = document.createElement('style');
+			style2.type = 'text/css';
+			style2.innerHTML = '.jqconsole-old-prompt { color: ' + cssColor + '; }';
+			document.getElementsByTagName('head')[0].appendChild(style2);
+		}
+
+	}
+
+	consola: any=undefined;
+	renderConsole(){
+		if(this.jqconsole){
+			$('#console').replaceWith(this.consola);
+		}else{
+			if ($("#console").jqconsole!=undefined){ // Check if element has been found
+				this.jqconsole = $('#console').jqconsole('');
+				this.consoleRef = this.jqconsole;
+				this.startPrompt.bind(this);
+				this.startPrompt();
+			} else {
+				this.rendered();
+			}
+			this.consola = $("#console");
+		}
+	}
+
+	jqconsole:any = undefined;
+	rendered(){
+		setTimeout(this.renderConsole.bind(this),1000);
+	}
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/shared/services/haskell.service.ts b/src/app/shared/services/haskell.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..97265288dcc157ec0c5e3ce33f44e7b4fe61f186
--- /dev/null
+++ b/src/app/shared/services/haskell.service.ts
@@ -0,0 +1,119 @@
+import { Injectable } from '@angular/core';
+import { Http, Response, Headers, RequestOptions, URLSearchParams } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+import { Archivo, Evaluacion } from '../objects/archivo';
+import { Grupo } from '../objects/grupo';
+
+import { SERVER } from '../config';
+
+import { AuthenticationService } from './authentication.service';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/catch';
+
+@Injectable()
+export class HaskellService {
+
+  /**
+   * Creates a new HaskellService with the injected Http.
+   * @param {Http} http - The injected Http.
+   * @constructor
+   */
+   constructor(private http: Http, private authService: AuthenticationService) {}
+
+   getArchivos(cedula:string): Observable<Archivo[]> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken() });
+     let params: URLSearchParams = new URLSearchParams();
+     params.set('cedula', cedula);
+     let options = new RequestOptions({ headers: headers, search: params });
+     return this.http.get(SERVER+'/servicios/archivo',options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   getArchivosCompartidosAlumno(cedula:string): Observable<Archivo[]> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let params: URLSearchParams = new URLSearchParams();
+     params.set('cedula', cedula);
+     params.set('compartidos','true');
+     let options = new RequestOptions({ headers: headers, search: params });
+     return this.http.get(SERVER+'/servicios/archivo',options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   crearArchivo(archivo: Archivo): Observable<Archivo> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let options = new RequestOptions({ headers: headers });
+     return this.http.post(SERVER+'/servicios/archivo', archivo, options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   editarArchivo(archivoId, archivo: Archivo): Observable<Archivo> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken() });
+     let options = new RequestOptions({ headers: headers });
+     return this.http.put(SERVER+'/servicios/archivo/'+archivoId, archivo, options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   eliminarArchivo(archivoId): Observable<Response> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let options = new RequestOptions({ headers: headers });
+     return this.http.delete(SERVER+'/servicios/archivo/'+archivoId, options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   getCopiaArchivoCompartidoGrupo(cedula, archivoId): Observable<Archivo> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let params: URLSearchParams = new URLSearchParams();
+     params.set('cedula', cedula);
+     let options = new RequestOptions({ headers: headers, search: params });
+     return this.http.get(SERVER+'/servicios/archivo/compartido/'+archivoId, options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   compartirArchivoGrupo(grupo,archivoId): Observable<Archivo> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let options = new RequestOptions({ headers: headers });
+     var archId = {
+       id:archivoId
+     }
+     return this.http.post(SERVER+'/servicios/grupo/'+grupo.liceoId+'/'+grupo.anio+'/'+grupo.grado+'/'+grupo.grupo+'/archivo', archId, options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+   calificarArchivo(archivoId, evaluacion): Observable<Evaluacion> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let options = new RequestOptions({ headers: headers });
+     return this.http.post(SERVER+'/servicios/archivo/'+archivoId+'/evaluacion', evaluacion, options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+
+
+   getGrupos(cedula:string): Observable<Grupo[]> {
+     let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization':'Bearer '+this.authService.getToken()  });
+     let params: URLSearchParams = new URLSearchParams();
+     params.set('cedula', cedula);
+     let options = new RequestOptions({ headers: headers, search: params });
+     return this.http.get(SERVER+'/servicios/grupo',options)
+     .map((res: Response) => res.json())
+     .catch(this.handleError);
+   }
+   
+  /**
+    * Handle HTTP error
+    */
+    private handleError (error: any) {
+      // In a real world app, we might use a remote logging infrastructure
+      // We'd also dig deeper into the error to get a better message
+      let errMsg = (error.message) ? error.message :
+      error.status ? `${error.status} - ${error.statusText}` : 'Server error';
+      console.error(errMsg); // log to console instead
+      return Observable.throw(errMsg);
+    }
+  }
diff --git a/src/app/shared/services/notificacion.service.ts b/src/app/shared/services/notificacion.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d61b31acfffc18fadd507d6501f482b3c79c3ba
--- /dev/null
+++ b/src/app/shared/services/notificacion.service.ts
@@ -0,0 +1,53 @@
+import { Injectable } from '@angular/core';
+import { Router, NavigationStart } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+
+@Injectable()
+export class NotificacionService {
+    private subject = new Subject<any>();
+    private keepAfterNavigationChange = false;
+
+    constructor(private router: Router) {
+        // clear alert message on route change
+        router.events.subscribe(event => {
+            if (event instanceof NavigationStart) {
+                if (this.keepAfterNavigationChange) {
+                    // only keep for a single location change
+                    this.keepAfterNavigationChange = false;
+                } else {
+                    // clear alert
+                    this.subject.next();
+                }
+            }
+        });
+    }
+
+    success(message: string, keepAfterNavigationChange = false) {
+        this.keepAfterNavigationChange = keepAfterNavigationChange;
+        this.subject.next({ type: 'success', text: message });
+    }
+
+    info(message: string, keepAfterNavigationChange = false) {
+        this.keepAfterNavigationChange = keepAfterNavigationChange;
+        this.subject.next({ type: 'info', text: message });
+    }
+
+    warning(message: string, keepAfterNavigationChange = false) {
+        this.keepAfterNavigationChange = keepAfterNavigationChange;
+        this.subject.next({ type: 'warning', text: message });
+    }
+
+    error(message: string, keepAfterNavigationChange = false) {
+        this.keepAfterNavigationChange = keepAfterNavigationChange;
+        this.subject.next({ type: 'danger', text: message });
+    }
+
+    getMessage(): Observable<any> {
+        return this.subject.asObservable();
+    }
+
+    getMessageSubject() {
+        return this.subject;
+    }
+}
\ No newline at end of file
diff --git a/src/app/shared/services/session.service.ts b/src/app/shared/services/session.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..84417842a27f992f6168b0136c1a195b323b65c1
--- /dev/null
+++ b/src/app/shared/services/session.service.ts
@@ -0,0 +1,149 @@
+import { Injectable } from '@angular/core';
+import { Archivo } from '../objects/archivo';
+import { Grupo } from '../objects/grupo';
+
+@Injectable()
+export class SessionService {
+	archivo: Archivo = new Archivo();
+	archivos: any;
+	dependencias :any;
+	archivosList :any;
+	directorioActual : any;
+	grupos: Grupo[];
+
+	public setArchivo(archivo){
+		this.archivo = archivo;
+	}
+	public getArchivo(){
+		return this.archivo;
+	}
+	public setArchivosTree(tree){
+		this.archivos = tree;
+	}
+	public getArchivos(service){
+		if(this.archivos!=undefined){
+			return this.archivos;
+		}
+		if(service!==undefined){
+			return this.archivos;	
+		}		
+	}
+
+	public setGrupos(grupos: Grupo[]){
+		this.grupos = grupos;
+	}
+
+	public getGrupos(){
+		return this.grupos;
+	}
+
+
+	public setArchivosList(l){
+		this.archivosList=l;
+	}
+	public getArchivosList(){
+		return this.archivosList;
+	}
+	public getDependencias(){
+		return this.dependencias;
+	}
+	public setDependencias(d){
+		this.dependencias = d;
+	}
+	public reset(){
+		this.archivo = new Archivo();
+		this.archivos = [];
+		this.dependencias = [];
+		this.archivosList = [];
+		this.grupos = undefined;
+	}
+	public setDirectorioActual(d){
+		this.directorioActual = d;
+	}
+
+	public cargarDependencias(arch){
+			
+			var resultado = this.buildDependenciesTree(arch,[]);
+			if(resultado["status"]==="miss"){
+				return resultado;
+			}
+			var list = this.toList(resultado["dependencies"]);
+			this.setDependencias(list);
+			return resultado;
+	}
+
+	//logica necesaria para cargar un archivo. "resuelve las dependencias 'incluir'"
+	buildDependenciesTree(pivot,archivosRecorridos){
+		var content = pivot.contenido;
+		var contentIncludes = this.extractIncludes(content);
+		var hijos:any = [];
+		var result : any = {};
+		var resultObject = {};
+		result.id = pivot.id;
+		result.nombre = pivot.nombre;
+		result.contenido = pivot.contenido;
+		for(var i in contentIncludes){
+			if(!archivosRecorridos.includes(contentIncludes[i])){
+				try{
+					var archivo = this.directorioActual.archivos.filter(
+					function(a){
+						return a.nombre === contentIncludes[i];
+					}
+					)[0];
+					archivosRecorridos.push(pivot.nombre);	
+				} catch(err){
+					
+				}
+				if(archivo){
+					var resultado_ = this.buildDependenciesTree(archivo,archivosRecorridos);
+					if(resultado_["status"]==="ok"){
+						hijos.push(resultado_["dependencies"]);						
+					} else{
+						return resultado_;
+					}
+
+
+				}else {
+					resultObject["status"] = "miss";
+					resultObject["nombre"] = contentIncludes[i];
+					return resultObject;
+				}
+			}
+		}
+		result.hijos = hijos;
+		resultObject["status"] = "ok";
+		resultObject["dependencies"] = result;
+		return resultObject;
+	} 
+
+	 toList(root){
+		var result:any = [];
+		result.push(root);
+		for(var i in root.hijos){
+			var sub = this.toList(root.hijos[i]);
+			result  = result.concat(sub);
+		}
+		return result;
+	}
+
+	extractIncludes(contenido){
+		var regex = /.*incluir\s*(\w*).*/gm;
+		let m;
+		var includes: any = [];
+
+		while ((m = regex.exec(contenido)) !== null) {
+			// This is necessary to avoid infinite loops with zero-width matches
+			if (m.index === regex.lastIndex) {
+				regex.lastIndex++;
+			}
+
+			// The result can be accessed through the `m`-variable.
+			m.forEach((match, groupIndex) => {
+				if(groupIndex===1){
+					includes.push(match);
+				}
+			});
+		}
+		return includes; 
+	}
+}
diff --git a/src/app/shared/services/usuario.service.ts b/src/app/shared/services/usuario.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1cce787b26cf12c698616c0a161a84f8e2fb102d
--- /dev/null
+++ b/src/app/shared/services/usuario.service.ts
@@ -0,0 +1,34 @@
+import { Injectable } from '@angular/core';
+import { Http, Headers, Response, RequestOptions } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+import { Usuario, Configuracion } from '../objects/usuario';
+
+import 'rxjs/add/operator/map'
+import 'rxjs/add/operator/catch'
+
+import { SERVER } from '../config';
+
+@Injectable()
+export class UsuarioService {
+    constructor(private http: Http) {}
+
+    actualizarConfiguracion(cedula: string, config: Configuracion) {
+        let headers = new Headers({ 'Content-Type': 'application/json' });
+        let options = new RequestOptions({ headers: headers });
+        return this.http.put(SERVER + '/servicios/usuario/' + cedula + "/configuracion", config, options)
+            .map(this.extractData)
+            .catch(this.handleError);
+    }
+
+    private extractData(res: Response) {
+        let body = res.json();
+        return body || [];
+    }
+
+    private handleError(error: any) {
+        let errMsg = (error.message) ? error.message :
+            error.status ? `${error.status} - ${error.statusText}` : 'Server error';
+        console.error(errMsg); // log to console instead
+        return Observable.throw(errMsg);
+    }
+}
diff --git a/src/app/shared/services/websocket.service.ts b/src/app/shared/services/websocket.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f9d9aa801e92ac8d03b84ac8e0b3bd8cf2a07186
--- /dev/null
+++ b/src/app/shared/services/websocket.service.ts
@@ -0,0 +1,41 @@
+import { Injectable } from '@angular/core';
+import * as Rx from 'rxjs/Rx';
+
+@Injectable()
+export class WebsocketService {
+	constructor() { }
+
+	private subject: Rx.Subject<MessageEvent>;
+
+	public connect(url: string): Rx.Subject<MessageEvent> {
+		if (!this.subject) {
+			this.subject = this.create(url);
+			console.log("Successfully connected: " + url);
+		} 
+		return this.subject;
+	}
+
+	private create(url: string): Rx.Subject<MessageEvent> {
+		let ws = new WebSocket(url);
+
+		let observable = Rx.Observable.create(
+			(obs: Rx.Observer<MessageEvent>) => {
+				ws.onmessage = obs.next.bind(obs);
+				ws.onerror = obs.error.bind(obs);
+				ws.onclose = obs.complete.bind(obs);
+				return ws.close.bind(ws);
+			})
+		let observer = {
+			next: (data: Object) => {
+				if (ws.readyState === WebSocket.OPEN) {
+					ws.send(JSON.stringify(data));
+				}else{
+					console.log("Se perdio la conexion");
+				}
+			}
+
+		}
+		return Rx.Subject.create(observer, observable);
+	}
+
+}
\ No newline at end of file
diff --git a/src/app/shared/utils/sha1.ts b/src/app/shared/utils/sha1.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c5bf08221d0940fc4243cd40070b3067d89b8fd6
--- /dev/null
+++ b/src/app/shared/utils/sha1.ts
@@ -0,0 +1,165 @@
+
+
+    var POW_2_24 = Math.pow(2, 24);
+    
+    var POW_2_32 = Math.pow(2, 32);
+    
+    function hex(n: number): string
+    {
+        var s = "",
+            v: number;
+        for (var i = 7; i >= 0; --i)
+        {
+            v = (n >>> (i << 2)) & 0xF;
+            s += v.toString(16);
+        }
+        return s;
+    }
+    
+    function lrot(n: number, bits: number): number
+    {
+        return ((n << bits) | (n >>> (32 - bits)));
+    }
+    
+    class Uint32ArrayBigEndian
+    {
+        bytes: Uint8Array;
+        constructor(length: number)
+        {
+            this.bytes = new Uint8Array(length << 2);
+        }
+        get(index: number): number
+        {
+            index <<= 2;
+            return (this.bytes[index] * POW_2_24)
+                + ((this.bytes[index + 1] << 16)
+                | (this.bytes[index + 2] << 8)
+                | this.bytes[index + 3]);
+        }
+        set(index: number, value: number)
+        {
+            var high = Math.floor(value / POW_2_24),
+                rest = value - (high * POW_2_24);
+            index <<= 2;
+            this.bytes[index] = high;
+            this.bytes[index + 1] = rest >> 16;
+            this.bytes[index + 2] = (rest >> 8) & 0xFF;
+            this.bytes[index + 3] = rest & 0xFF;
+        }
+    }
+
+    function string2ArrayBuffer(s: string): ArrayBuffer
+    {
+        s = s.replace(/[\u0080-\u07ff]/g,
+            function(c: string)
+            { 
+                var code = c.charCodeAt(0);
+                return String.fromCharCode(0xC0 | code >> 6, 0x80 | code & 0x3F);
+            });
+        s = s.replace(/[\u0080-\uffff]/g,
+            function(c: string)
+            { 
+                var code = c.charCodeAt(0);
+                return String.fromCharCode(0xE0 | code >> 12, 0x80 | code >> 6 & 0x3F, 0x80 | code & 0x3F);
+            });
+        var n = s.length,
+            array = new Uint8Array(n);
+        for (var i = 0; i < n; ++i)
+        {
+            array[i] = s.charCodeAt(i);
+        }
+        return array.buffer;
+    }
+
+    export function hash(bufferOrString: any): string
+    {
+        var source: ArrayBuffer;
+        if (bufferOrString instanceof ArrayBuffer)
+        {
+            source = <ArrayBuffer> bufferOrString;
+        }
+        else
+        {
+            source = string2ArrayBuffer(String(bufferOrString));
+        }
+
+        var h0 = 0x67452301,
+            h1 = 0xEFCDAB89,
+            h2 = 0x98BADCFE,
+            h3 = 0x10325476,
+            h4 = 0xC3D2E1F0,
+            i: number,
+            sbytes = source.byteLength,
+            sbits = sbytes << 3,
+            minbits = sbits + 65,
+            bits = Math.ceil(minbits / 512) << 9,
+            bytes = bits >>> 3,
+            slen = bytes >>> 2,
+            s = new Uint32ArrayBigEndian(slen),
+            s8 = s.bytes,
+            j: number,
+            w = new Uint32Array(80),
+            sourceArray = new Uint8Array(source);
+        for (i = 0; i < sbytes; ++i)
+        {
+            s8[i] = sourceArray[i];
+        }
+        s8[sbytes] = 0x80;
+        s.set(slen - 2, Math.floor(sbits / POW_2_32));
+        s.set(slen - 1, sbits & 0xFFFFFFFF);
+        for (i = 0; i < slen; i += 16)
+        {
+            for (j = 0; j < 16; ++j)
+            {
+                w[j] = s.get(i + j);
+            }
+            for ( ; j < 80; ++j)
+            {
+                w[j] = lrot(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
+            }
+            var a = h0,
+                b = h1,
+                c = h2,
+                d = h3,
+                e = h4,
+                f: number,
+                k: number,
+                temp: number;
+            for (j = 0; j < 80; ++j)
+            {
+                if (j < 20)
+                {
+                    f = (b & c) | ((~b) & d);
+                    k = 0x5A827999;
+                }
+                else if (j < 40)
+                {
+                    f = b ^ c ^ d;
+                    k = 0x6ED9EBA1;
+                }
+                else if (j < 60)
+                {
+                    f = (b & c) ^ (b & d) ^ (c & d);
+                    k = 0x8F1BBCDC;
+                }
+                else
+                {
+                    f = b ^ c ^ d;
+                    k = 0xCA62C1D6;
+                }
+
+                temp = (lrot(a, 5) + f + e + k + w[j]) & 0xFFFFFFFF;
+                e = d;
+                d = c;
+                c = lrot(b, 30);
+                b = a;
+                a = temp;
+            }
+            h0 = (h0 + a) & 0xFFFFFFFF;
+            h1 = (h1 + b) & 0xFFFFFFFF;
+            h2 = (h2 + c) & 0xFFFFFFFF;
+            h3 = (h3 + d) & 0xFFFFFFFF;
+            h4 = (h4 + e) & 0xFFFFFFFF;
+        }
+        return hex(h0) + hex(h1) + hex(h2) + hex(h3) + hex(h4);
+    }
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3612073bc31cd4c1f5d6cbb00318521e9a61bd8a
--- /dev/null
+++ b/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+  production: true
+};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7f639aecac5c903f5449c7d14846f92b0a9b342
--- /dev/null
+++ b/src/environments/environment.ts
@@ -0,0 +1,8 @@
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+  production: false
+};
diff --git a/src/favicon.ico b/src/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..444fdd4ab74564eb687272240ec077d93ead5269
Binary files /dev/null and b/src/favicon.ico differ
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..1fccac3c133148367c45a6177348ebab925d15d8
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Proyecto MateFun</title>
+    <base href="/">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="icon" type="image/x-icon" href="favicon.ico">
+    <!-- despues lo saco de aca -->
+    <!-- <link rel="stylesheet" type="text/css" href="node_modules/codemirror/addon/hint/show-hint.css">  -->
+    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
+<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
+
+</head>
+<body>
+    <app-root>
+        <div class="loading">
+            <div class="loading-bar"></div>
+            <div class="loading-bar"></div>
+            <div class="loading-bar"></div>
+            <div class="loading-bar"></div>
+            <div class="loading-bar"></div>
+        </div>
+    </app-root>
+</body>
+
+</html>
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a9ca1caf8ceebeb432bbf02b6a2b18378653fbd0
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,11 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+  enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/src/polyfills.ts b/src/polyfills.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53bdaf1b86424df5230596f8352e78e4a4de66f5
--- /dev/null
+++ b/src/polyfills.ts
@@ -0,0 +1,68 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ *      file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+// import 'core-js/es6/symbol';
+// import 'core-js/es6/object';
+// import 'core-js/es6/function';
+// import 'core-js/es6/parse-int';
+// import 'core-js/es6/parse-float';
+// import 'core-js/es6/number';
+// import 'core-js/es6/math';
+// import 'core-js/es6/string';
+// import 'core-js/es6/date';
+// import 'core-js/es6/array';
+// import 'core-js/es6/regexp';
+// import 'core-js/es6/map';
+// import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js';  // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following to support `@angular/animation`. */
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+
+/** Evergreen browsers require these. **/
+import 'core-js/es6/reflect';
+import 'core-js/es7/reflect';
+
+
+/** ALL Firefox browsers require the following to support `@angular/animation`. **/
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js/dist/zone';  // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+
+/**
+ * Date, currency, decimal and percent pipes.
+ * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+ */
+// import 'intl';  // Run `npm install --save intl`.
diff --git a/src/styles/_responsive.scss b/src/styles/_responsive.scss
new file mode 100644
index 0000000000000000000000000000000000000000..89aacd9b51ee0f41fcf9d5b263700bf43a601c20
--- /dev/null
+++ b/src/styles/_responsive.scss
@@ -0,0 +1,82 @@
+// @media screen and (max-width: 992px) {
+	.push-right {
+		.sidebar {
+			left: 235px !important;
+		}
+	}
+// }
+
+
+.CodeMirror {
+	height: 400px !important;
+}
+
+#console {
+	height: 400px !important;
+}
+
+.contenedor-canvas{
+	height: 400px !important;
+}
+
+.listado-archivos{
+	height: 400px !important;
+}
+
+.previewArchivoNoSeleccionado{
+	height: 400px !important;
+}
+
+.listado-grupos {
+	height: 400px !important;
+}
+
+.previewArchivoNoSeleccionado{
+	height: 400px !important;
+}
+
+.listadoEntregasAlumnoGrupos{
+	height: 400px !important;
+}
+
+@media screen and (min-width: 992px) {
+	.codemirrorArchivo .CodeMirror {
+		height: calc(100vh - 235px) !important;	
+	}
+
+	.codemirrorPrograma .CodeMirror {
+	 	height: calc(100vh - 197px) !important;
+	}
+
+	.codemirrorGrupos .CodeMirror {
+	 	height: calc(100vh - 232px) !important;
+	}
+
+	#console {
+		height: calc(100vh - 79px) !important;
+	}
+
+	.contenedor-canvas{
+		height: calc(100vh - 121px) !important;
+	}
+
+	.listado-archivos{
+		height: calc(100vh - 265px) !important;
+	}
+
+	.previewArchivoNoSeleccionado{
+		height: calc(100vh - 275px) !important;
+	}
+
+	.previewArchivoNoSeleccionadoGrupos{
+		height: calc(100vh - 225px) !important;
+	}
+
+	.listadoEntregasAlumnoGrupos{
+		height: calc(100vh - 225px) !important;
+	}
+
+	.listado-grupos {
+		height: calc(100vh - 270px) !important;
+	}
+}
\ No newline at end of file
diff --git a/src/styles/_rtl.scss b/src/styles/_rtl.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5e8070154d060472198860b3c58d40310ee8483b
--- /dev/null
+++ b/src/styles/_rtl.scss
@@ -0,0 +1,35 @@
+.rtl {
+    .sidebar {
+        left: auto !important;
+        right: 0 !important;
+        > ul.list-group {
+            padding: 0;
+        }
+    }
+    .main-container {
+        margin-left: 0 !important;
+        margin-right: 235px;
+    }
+    * {
+        direction: rtl;
+    }
+}
+@media screen and (max-width: 992px) {
+    .rtl {
+        .navbar-brand {
+            direction: ltr;
+        }
+        .sidebar {
+            right: -235px !important;
+        }
+        .main-container {
+            margin-right: 0;
+        }
+        &.push-right {
+            .sidebar {
+                left: auto !important;
+                right: 0 !important;
+            }
+        }
+    }
+}
diff --git a/src/styles/_spinner.scss b/src/styles/_spinner.scss
new file mode 100644
index 0000000000000000000000000000000000000000..46499815d31a3cfff5b9ff9b7c87eae33ae8be17
--- /dev/null
+++ b/src/styles/_spinner.scss
@@ -0,0 +1,42 @@
+.loading {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    -ms-transform: translate(-50%,-50%); /* IE 9 */
+    -webkit-transform: translate(-50%,-50%); /* Safari */
+    transform: translate(-50%,-50%); /* Standard syntax */
+    .loading-bar {
+        display: inline-block;
+        width: 6px;
+        height: 30px;
+        border-radius: 4px;
+        animation: loading 1s ease-in-out infinite;
+        &:nth-child(1) {
+            background-color: #222;
+            animation-delay: 0;
+        }
+        &:nth-child(2) {
+            background-color: #222;
+            animation-delay: 0.09s;
+        }
+        &:nth-child(3) {
+            background-color: #222;
+            animation-delay: .18s;
+        }
+        &:nth-child(4) {
+            background-color: #222;
+            animation-delay: .27s;
+        }
+    }
+}
+@keyframes loading {
+    0% {
+        transform: scale(1);
+    }
+    20% {
+        transform: scale(1, 2.2);
+    }
+    40% {
+        transform: scale(1);
+    }
+}
diff --git a/src/styles/_utils.scss b/src/styles/_utils.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6d1f1308c386bc23cffd569bf04bb98d07dc39a7
--- /dev/null
+++ b/src/styles/_utils.scss
@@ -0,0 +1,7 @@
+.fs-12 {
+    font-size: 12px;
+}
+
+.errorConsole{
+    color: red;
+}
\ No newline at end of file
diff --git a/src/styles/app.scss b/src/styles/app.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3322214e234fc5cdfc4f44f741a7607e5f05ee5b
--- /dev/null
+++ b/src/styles/app.scss
@@ -0,0 +1,56 @@
+/* You can add global styles to this file, and also import other style files */
+@import "bootstrap/bootstrap";
+@import "spinner";
+@import "utils";
+@import "rtl";
+@import "responsive";
+
+@import "~codemirror/theme/dracula.css";
+@import "~codemirror/theme/3024-day.css";
+@import "~codemirror/theme/3024-night.css";
+@import "~codemirror/theme/abcdef.css";
+@import "~codemirror/theme/ambiance-mobile.css";
+@import "~codemirror/theme/ambiance.css";
+@import "~codemirror/theme/base16-dark.css";
+@import "~codemirror/theme/base16-light.css";
+@import "~codemirror/theme/bespin.css";
+@import "~codemirror/theme/blackboard.css";
+@import "~codemirror/theme/cobalt.css";
+@import "~codemirror/theme/colorforth.css";
+@import "~codemirror/theme/dracula.css";
+@import "~codemirror/theme/duotone-dark.css";
+@import "~codemirror/theme/duotone-light.css";
+@import "~codemirror/theme/eclipse.css";
+@import "~codemirror/theme/elegant.css";
+@import "~codemirror/theme/erlang-dark.css";
+@import "~codemirror/theme/hopscotch.css";
+@import "~codemirror/theme/icecoder.css";
+@import "~codemirror/theme/isotope.css";
+@import "~codemirror/theme/lesser-dark.css";
+@import "~codemirror/theme/liquibyte.css";
+@import "~codemirror/theme/material.css";
+@import "~codemirror/theme/mbo.css";
+@import "~codemirror/theme/mdn-like.css";
+@import "~codemirror/theme/midnight.css";
+@import "~codemirror/theme/monokai.css";
+@import "~codemirror/theme/neat.css";
+@import "~codemirror/theme/neo.css";
+@import "~codemirror/theme/night.css";
+@import "~codemirror/theme/panda-syntax.css";
+@import "~codemirror/theme/paraiso-dark.css";
+@import "~codemirror/theme/paraiso-light.css";
+@import "~codemirror/theme/pastel-on-dark.css";
+@import "~codemirror/theme/railscasts.css";
+@import "~codemirror/theme/rubyblue.css";
+@import "~codemirror/theme/seti.css";
+@import "~codemirror/theme/solarized.css";
+@import "~codemirror/theme/the-matrix.css";
+@import "~codemirror/theme/tomorrow-night-bright.css";
+@import "~codemirror/theme/tomorrow-night-eighties.css";
+@import "~codemirror/theme/ttcn.css";
+@import "~codemirror/theme/twilight.css";
+@import "~codemirror/theme/vibrant-ink.css";
+@import "~codemirror/theme/xq-dark.css";
+@import "~codemirror/theme/xq-light.css";
+@import "~codemirror/theme/yeti.css";
+@import "~codemirror/theme/zenburn.css";
diff --git a/src/styles/bootstrap/_alert.scss b/src/styles/bootstrap/_alert.scss
new file mode 100755
index 0000000000000000000000000000000000000000..d9b4e9b2764828174b288aa04f7a83506b466c47
--- /dev/null
+++ b/src/styles/bootstrap/_alert.scss
@@ -0,0 +1,55 @@
+//
+// Base styles
+//
+
+.alert {
+  padding: $alert-padding-y $alert-padding-x;
+  margin-bottom: $alert-margin-bottom;
+  border: $alert-border-width solid transparent;
+  @include border-radius($alert-border-radius);
+}
+
+// Headings for larger alerts
+.alert-heading {
+  // Specified to prevent conflicts of changing $headings-color
+  color: inherit;
+}
+
+// Provide class for links that match alerts
+.alert-link {
+  font-weight: $alert-link-font-weight;
+}
+
+
+// Dismissible alerts
+//
+// Expand the right padding and account for the close button's positioning.
+
+.alert-dismissible {
+  // Adjust close link position
+  .close {
+    position: relative;
+    top: -$alert-padding-y;
+    right: -$alert-padding-x;
+    padding: $alert-padding-y $alert-padding-x;
+    color: inherit;
+  }
+}
+
+
+// Alternate styles
+//
+// Generate contextual modifier classes for colorizing the alert.
+
+.alert-success {
+  @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text);
+}
+.alert-info {
+  @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text);
+}
+.alert-warning {
+  @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text);
+}
+.alert-danger {
+  @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text);
+}
diff --git a/src/styles/bootstrap/_badge.scss b/src/styles/bootstrap/_badge.scss
new file mode 100755
index 0000000000000000000000000000000000000000..e5a3298937c5ee641cf51637b1945796050da4b9
--- /dev/null
+++ b/src/styles/bootstrap/_badge.scss
@@ -0,0 +1,77 @@
+// Base class
+//
+// Requires one of the contextual, color modifier classes for `color` and
+// `background-color`.
+
+.badge {
+  display: inline-block;
+  padding: $badge-padding-y $badge-padding-x;
+  font-size: $badge-font-size;
+  font-weight: $badge-font-weight;
+  line-height: 1;
+  color: $badge-color;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  @include border-radius();
+
+  // Empty badges collapse automatically
+  &:empty {
+    display: none;
+  }
+}
+
+// Quick fix for badges in buttons
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+
+// scss-lint:disable QualifyingElement
+// Add hover effects, but only for links
+a.badge {
+  @include hover-focus {
+    color: $badge-link-hover-color;
+    text-decoration: none;
+    cursor: pointer;
+  }
+}
+// scss-lint:enable QualifyingElement
+
+// Pill badges
+//
+// Make them extra rounded with a modifier to replace v3's badges.
+
+.badge-pill {
+  padding-right: $badge-pill-padding-x;
+  padding-left: $badge-pill-padding-x;
+  @include border-radius($badge-pill-border-radius);
+}
+
+// Colors
+//
+// Contextual variations (linked badges get darker on :hover).
+
+.badge-default {
+  @include badge-variant($badge-default-bg);
+}
+
+.badge-primary {
+  @include badge-variant($badge-primary-bg);
+}
+
+.badge-success {
+  @include badge-variant($badge-success-bg);
+}
+
+.badge-info {
+  @include badge-variant($badge-info-bg);
+}
+
+.badge-warning {
+  @include badge-variant($badge-warning-bg);
+}
+
+.badge-danger {
+  @include badge-variant($badge-danger-bg);
+}
diff --git a/src/styles/bootstrap/_breadcrumb.scss b/src/styles/bootstrap/_breadcrumb.scss
new file mode 100755
index 0000000000000000000000000000000000000000..1a09bba20a993cdb339d9329e21b83353ee9144b
--- /dev/null
+++ b/src/styles/bootstrap/_breadcrumb.scss
@@ -0,0 +1,38 @@
+.breadcrumb {
+  padding: $breadcrumb-padding-y $breadcrumb-padding-x;
+  margin-bottom: $spacer-y;
+  list-style: none;
+  background-color: $breadcrumb-bg;
+  @include border-radius($border-radius);
+  @include clearfix;
+}
+
+.breadcrumb-item {
+  float: left;
+
+  // The separator between breadcrumbs (by default, a forward-slash: "/")
+  + .breadcrumb-item::before {
+    display: inline-block; // Suppress underlining of the separator in modern browsers
+    padding-right: $breadcrumb-item-padding;
+    padding-left: $breadcrumb-item-padding;
+    color: $breadcrumb-divider-color;
+    content: "#{$breadcrumb-divider}";
+  }
+
+  // IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built
+  // without `<ul>`s. The `::before` pseudo-element generates an element
+  // *within* the .breadcrumb-item and thereby inherits the `text-decoration`.
+  //
+  // To trick IE into suppressing the underline, we give the pseudo-element an
+  // underline and then immediately remove it.
+  + .breadcrumb-item:hover::before {
+    text-decoration: underline;
+  }
+  + .breadcrumb-item:hover::before {
+    text-decoration: none;
+  }
+
+  &.active {
+    color: $breadcrumb-active-color;
+  }
+}
diff --git a/src/styles/bootstrap/_button-group.scss b/src/styles/bootstrap/_button-group.scss
new file mode 100755
index 0000000000000000000000000000000000000000..584ed151377cea71f96024e198def3ce8feaff81
--- /dev/null
+++ b/src/styles/bootstrap/_button-group.scss
@@ -0,0 +1,202 @@
+// scss-lint:disable QualifyingElement
+
+// Make the div behave like a button
+.btn-group,
+.btn-group-vertical {
+  position: relative;
+  display: inline-flex;
+  vertical-align: middle; // match .btn alignment given font-size hack above
+
+  > .btn {
+    position: relative;
+    flex: 0 1 auto;
+
+    // Bring the hover, focused, and "active" buttons to the fron to overlay
+    // the borders properly
+    @include hover {
+      z-index: 2;
+    }
+    &:focus,
+    &:active,
+    &.active {
+      z-index: 2;
+    }
+  }
+
+  // Prevent double borders when buttons are next to each other
+  .btn + .btn,
+  .btn + .btn-group,
+  .btn-group + .btn,
+  .btn-group + .btn-group {
+    margin-left: -$input-btn-border-width;
+  }
+}
+
+// Optional: Group multiple button groups together for a toolbar
+.btn-toolbar {
+  display: flex;
+  justify-content: flex-start;
+
+  .input-group {
+    width: auto;
+  }
+}
+
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+  border-radius: 0;
+}
+
+// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match
+.btn-group > .btn:first-child {
+  margin-left: 0;
+
+  &:not(:last-child):not(.dropdown-toggle) {
+    @include border-right-radius(0);
+  }
+}
+// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+  @include border-left-radius(0);
+}
+
+// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)
+.btn-group > .btn-group {
+  float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group > .btn-group:first-child:not(:last-child) {
+  > .btn:last-child,
+  > .dropdown-toggle {
+    @include border-right-radius(0);
+  }
+}
+.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  @include border-left-radius(0);
+}
+
+// On active and open, don't show outline
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+  outline: 0;
+}
+
+
+// Sizing
+//
+// Remix the default button sizing classes into new ones for easier manipulation.
+
+.btn-group-sm > .btn { @extend .btn-sm; }
+.btn-group-lg > .btn { @extend .btn-lg; }
+
+
+//
+// Split button dropdowns
+//
+
+.btn + .dropdown-toggle-split {
+  padding-right: $btn-padding-x * .75;
+  padding-left: $btn-padding-x * .75;
+
+  &::after {
+    margin-left: 0;
+  }
+}
+
+.btn-sm + .dropdown-toggle-split {
+  padding-right: $btn-padding-x-sm * .75;
+  padding-left: $btn-padding-x-sm * .75;
+}
+
+.btn-lg + .dropdown-toggle-split {
+  padding-right: $btn-padding-x-lg * .75;
+  padding-left: $btn-padding-x-lg * .75;
+}
+
+
+// The clickable button for toggling the menu
+// Remove the gradient and set the same inset shadow as the :active state
+.btn-group.open .dropdown-toggle {
+  @include box-shadow($btn-active-box-shadow);
+
+  // Show no shadow for `.btn-link` since it has no other button styles.
+  &.btn-link {
+    @include box-shadow(none);
+  }
+}
+
+
+//
+// Vertical button groups
+//
+
+.btn-group-vertical {
+  display: inline-flex;
+  flex-direction: column;
+  align-items: flex-start;
+  justify-content: center;
+
+  .btn,
+  .btn-group {
+    width: 100%;
+  }
+
+  > .btn + .btn,
+  > .btn + .btn-group,
+  > .btn-group + .btn,
+  > .btn-group + .btn-group {
+    margin-top: -$input-btn-border-width;
+    margin-left: 0;
+  }
+}
+
+.btn-group-vertical > .btn {
+  &:not(:first-child):not(:last-child) {
+    border-radius: 0;
+  }
+  &:first-child:not(:last-child) {
+    @include border-bottom-radius(0);
+  }
+  &:last-child:not(:first-child) {
+    @include border-top-radius(0);
+  }
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) {
+  > .btn:last-child,
+  > .dropdown-toggle {
+    @include border-bottom-radius(0);
+  }
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  @include border-top-radius(0);
+}
+
+
+// Checkbox and radio options
+//
+// In order to support the browser's form validation feedback, powered by the
+// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
+// `display: none;` or `visibility: hidden;` as that also hides the popover.
+// Simply visually hiding the inputs via `opacity` would leave them clickable in
+// certain cases which is prevented by using `clip` and `pointer-events`.
+// This way, we ensure a DOM element is visible to position the popover from.
+//
+// See https://github.com/twbs/bootstrap/pull/12794 and
+// https://github.com/twbs/bootstrap/pull/14559 for more information.
+
+[data-toggle="buttons"] {
+  > .btn,
+  > .btn-group > .btn {
+    input[type="radio"],
+    input[type="checkbox"] {
+      position: absolute;
+      clip: rect(0,0,0,0);
+      pointer-events: none;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/_buttons.scss b/src/styles/bootstrap/_buttons.scss
new file mode 100755
index 0000000000000000000000000000000000000000..e36ff0f1f6645f1bf8c8bf88a4c81dfb59e4dbb2
--- /dev/null
+++ b/src/styles/bootstrap/_buttons.scss
@@ -0,0 +1,170 @@
+// scss-lint:disable QualifyingElement
+
+//
+// Base styles
+//
+
+.btn {
+  display: inline-block;
+  font-weight: $btn-font-weight;
+  line-height: $btn-line-height;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  user-select: none;
+  border: $input-btn-border-width solid transparent;
+  @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-border-radius);
+  @include transition($btn-transition);
+
+  // Share hover and focus styles
+  @include hover-focus {
+    text-decoration: none;
+  }
+  &:focus,
+  &.focus {
+    outline: 0;
+    box-shadow: $btn-focus-box-shadow;
+  }
+
+  // Disabled comes first so active can properly restyle
+  &.disabled,
+  &:disabled {
+    cursor: $cursor-disabled;
+    opacity: .65;
+    @include box-shadow(none);
+  }
+
+  &:active,
+  &.active {
+    background-image: none;
+    @include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
+  }
+}
+
+// Future-proof disabling of clicks on `<a>` elements
+a.btn.disabled,
+fieldset[disabled] a.btn {
+  pointer-events: none;
+}
+
+
+//
+// Alternate buttons
+//
+
+.btn-primary {
+  @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border);
+}
+.btn-secondary {
+  @include button-variant($btn-secondary-color, $btn-secondary-bg, $btn-secondary-border);
+}
+.btn-info {
+  @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border);
+}
+.btn-success {
+  @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border);
+}
+.btn-warning {
+  @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border);
+}
+.btn-danger {
+  @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border);
+}
+
+// Remove all backgrounds
+.btn-outline-primary {
+  @include button-outline-variant($btn-primary-bg);
+}
+.btn-outline-secondary {
+  @include button-outline-variant($btn-secondary-border);
+}
+.btn-outline-info {
+  @include button-outline-variant($btn-info-bg);
+}
+.btn-outline-success {
+  @include button-outline-variant($btn-success-bg);
+}
+.btn-outline-warning {
+  @include button-outline-variant($btn-warning-bg);
+}
+.btn-outline-danger {
+  @include button-outline-variant($btn-danger-bg);
+}
+
+
+//
+// Link buttons
+//
+
+// Make a button look and behave like a link
+.btn-link {
+  font-weight: $font-weight-normal;
+  color: $link-color;
+  border-radius: 0;
+
+  &,
+  &:active,
+  &.active,
+  &:disabled {
+    background-color: transparent;
+    @include box-shadow(none);
+  }
+  &,
+  &:focus,
+  &:active {
+    border-color: transparent;
+  }
+  @include hover {
+    border-color: transparent;
+  }
+  @include hover-focus {
+    color: $link-hover-color;
+    text-decoration: $link-hover-decoration;
+    background-color: transparent;
+  }
+  &:disabled {
+    color: $btn-link-disabled-color;
+
+    @include hover-focus {
+      text-decoration: none;
+    }
+  }
+}
+
+
+//
+// Button Sizes
+//
+
+.btn-lg {
+  // line-height: ensure even-numbered height of button next to large input
+  @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-border-radius-lg);
+}
+.btn-sm {
+  // line-height: ensure proper height of button next to small input
+  @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius-sm);
+}
+
+
+//
+// Block button
+//
+
+.btn-block {
+  display: block;
+  width: 100%;
+}
+
+// Vertically space out multiple block buttons
+.btn-block + .btn-block {
+  margin-top: $btn-block-spacing-y;
+}
+
+// Specificity overrides
+input[type="submit"],
+input[type="reset"],
+input[type="button"] {
+  &.btn-block {
+    width: 100%;
+  }
+}
diff --git a/src/styles/bootstrap/_card.scss b/src/styles/bootstrap/_card.scss
new file mode 100755
index 0000000000000000000000000000000000000000..9fe70e8cf6d262e4c6de6e8529c9b74f65c7897e
--- /dev/null
+++ b/src/styles/bootstrap/_card.scss
@@ -0,0 +1,276 @@
+//
+// Base styles
+//
+
+.card {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  background-color: $card-bg;
+  border: $card-border-width solid $card-border-color;
+  @include border-radius($card-border-radius);
+}
+
+.card-block {
+  // Enable `flex-grow: 1` for decks and groups so that card blocks take up
+  // as much space as possible, ensuring footers are aligned to the bottom.
+  flex: 1 1 auto;
+  padding: $card-spacer-x;
+}
+
+.card-title {
+  margin-bottom: $card-spacer-y;
+}
+
+.card-subtitle {
+  margin-top: -($card-spacer-y / 2);
+  margin-bottom: 0;
+}
+
+.card-text:last-child {
+  margin-bottom: 0;
+}
+
+.card-link {
+  @include hover {
+    text-decoration: none;
+  }
+
+  + .card-link {
+    margin-left: $card-spacer-x;
+  }
+}
+
+.card {
+  > .list-group:first-child {
+    .list-group-item:first-child {
+      @include border-top-radius($card-border-radius);
+    }
+  }
+
+  > .list-group:last-child {
+    .list-group-item:last-child {
+      @include border-bottom-radius($card-border-radius);
+    }
+  }
+}
+
+
+//
+// Optional textual caps
+//
+
+.card-header {
+  padding: $card-spacer-y $card-spacer-x;
+  margin-bottom: 0; // Removes the default margin-bottom of <hN>
+  background-color: $card-cap-bg;
+  border-bottom: $card-border-width solid $card-border-color;
+
+  &:first-child {
+    @include border-radius($card-border-radius-inner $card-border-radius-inner 0 0);
+  }
+}
+
+.card-footer {
+  padding: $card-spacer-y $card-spacer-x;
+  background-color: $card-cap-bg;
+  border-top: $card-border-width solid $card-border-color;
+
+  &:last-child {
+    @include border-radius(0 0 $card-border-radius-inner $card-border-radius-inner);
+  }
+}
+
+
+//
+// Header navs
+//
+
+.card-header-tabs {
+  margin-right: -($card-spacer-x / 2);
+  margin-bottom: -$card-spacer-y;
+  margin-left: -($card-spacer-x / 2);
+  border-bottom: 0;
+}
+
+.card-header-pills {
+  margin-right: -($card-spacer-x / 2);
+  margin-left: -($card-spacer-x / 2);
+}
+
+
+//
+// Background variations
+//
+
+.card-primary {
+  @include card-variant($brand-primary, $brand-primary);
+}
+.card-success {
+  @include card-variant($brand-success, $brand-success);
+}
+.card-info {
+  @include card-variant($brand-info, $brand-info);
+}
+.card-warning {
+  @include card-variant($brand-warning, $brand-warning);
+}
+.card-danger {
+  @include card-variant($brand-danger, $brand-danger);
+}
+
+// Remove all backgrounds
+.card-outline-primary {
+  @include card-outline-variant($btn-primary-bg);
+}
+.card-outline-secondary {
+  @include card-outline-variant($btn-secondary-border);
+}
+.card-outline-info {
+  @include card-outline-variant($btn-info-bg);
+}
+.card-outline-success {
+  @include card-outline-variant($btn-success-bg);
+}
+.card-outline-warning {
+  @include card-outline-variant($btn-warning-bg);
+}
+.card-outline-danger {
+  @include card-outline-variant($btn-danger-bg);
+}
+
+//
+// Inverse text within a card for use with dark backgrounds
+//
+
+.card-inverse {
+  @include card-inverse;
+}
+
+//
+// Blockquote
+//
+
+.card-blockquote {
+  padding: 0;
+  margin-bottom: 0;
+  border-left: 0;
+}
+
+// Card image
+.card-img {
+  // margin: -1.325rem;
+  @include border-radius($card-border-radius-inner);
+}
+.card-img-overlay {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  padding: $card-img-overlay-padding;
+}
+
+
+
+// Card image caps
+.card-img-top {
+  @include border-top-radius($card-border-radius-inner);
+}
+.card-img-bottom {
+  @include border-bottom-radius($card-border-radius-inner);
+}
+
+
+// Card deck
+
+@include media-breakpoint-up(sm) {
+  .card-deck {
+    display: flex;
+    flex-flow: row wrap;
+
+    .card {
+      display: flex;
+      flex: 1 0 0;
+      flex-direction: column;
+
+      // Selectively apply horizontal margins to cards to avoid doing the
+      // negative margin dance like our grid. This differs from the grid
+      // due to the use of margins as gutters instead of padding.
+      &:not(:first-child) { margin-left: $card-deck-margin; }
+      &:not(:last-child) { margin-right: $card-deck-margin; }
+    }
+  }
+}
+
+
+//
+// Card groups
+//
+
+@include media-breakpoint-up(sm) {
+  .card-group {
+    display: flex;
+    flex-flow: row wrap;
+
+    .card {
+      flex: 1 0 0;
+
+      + .card {
+        margin-left: 0;
+        border-left: 0;
+      }
+
+      // Handle rounded corners
+      @if $enable-rounded {
+        &:first-child {
+          @include border-right-radius(0);
+
+          .card-img-top {
+            border-top-right-radius: 0;
+          }
+          .card-img-bottom {
+            border-bottom-right-radius: 0;
+          }
+        }
+        &:last-child {
+          @include border-left-radius(0);
+
+          .card-img-top {
+            border-top-left-radius: 0;
+          }
+          .card-img-bottom {
+            border-bottom-left-radius: 0;
+          }
+        }
+
+        &:not(:first-child):not(:last-child) {
+          border-radius: 0;
+
+          .card-img-top,
+          .card-img-bottom {
+            border-radius: 0;
+          }
+        }
+      }
+    }
+  }
+}
+
+
+//
+// Columns
+//
+
+@include media-breakpoint-up(sm) {
+  .card-columns {
+    column-count: $card-columns-count;
+    column-gap: $card-columns-gap;
+
+    .card {
+      display: inline-block; // Don't let them vertically span multiple columns
+      width: 100%; // Don't let their width change
+      margin-bottom: $card-columns-margin;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/_carousel.scss b/src/styles/bootstrap/_carousel.scss
new file mode 100755
index 0000000000000000000000000000000000000000..54478e450425672eb7e4de7838a9ac14da8b5533
--- /dev/null
+++ b/src/styles/bootstrap/_carousel.scss
@@ -0,0 +1,178 @@
+// Wrapper for the slide container and indicators
+.carousel {
+  position: relative;
+}
+
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+
+.carousel-item {
+  position: relative;
+  display: none;
+  width: 100%;
+
+  @include if-supports-3d-transforms() {
+    @include transition($carousel-transition);
+    backface-visibility: hidden;
+    perspective: 1000px;
+  }
+}
+
+.carousel-item.active,
+.carousel-item-next,
+.carousel-item-prev {
+  display: flex;
+}
+
+.carousel-item-next,
+.carousel-item-prev {
+  position: absolute;
+  top: 0;
+}
+
+// CSS3 transforms when supported by the browser
+@include if-supports-3d-transforms() {
+  .carousel-item-next.carousel-item-left,
+  .carousel-item-prev.carousel-item-right {
+    transform: translate3d(0, 0, 0);
+  }
+
+  .carousel-item-next,
+  .active.carousel-item-right {
+    transform: translate3d(100%, 0, 0);
+  }
+
+  .carousel-item-prev,
+  .active.carousel-item-left {
+    transform: translate3d(-100%, 0, 0);
+  }
+}
+
+
+//
+// Left/right controls for nav
+//
+
+.carousel-control-prev,
+.carousel-control-next {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  // Use flex for alignment (1-3)
+  display: flex; // 1. allow flex styles
+  align-items: center; // 2. vertically center contents
+  justify-content: center; // 3. horizontally center contents
+  width: $carousel-control-width;
+  color: $carousel-control-color;
+  text-align: center;
+  opacity: $carousel-control-opacity;
+  // We can't have a transition here because WebKit cancels the carousel
+  // animation if you trip this while in the middle of another animation.
+
+  // Hover/focus state
+  @include hover-focus {
+    color: $carousel-control-color;
+    text-decoration: none;
+    outline: 0;
+    opacity: .9;
+  }
+}
+.carousel-control-prev {
+  left: 0;
+}
+.carousel-control-next {
+  right: 0;
+}
+
+// Icons for within
+.carousel-control-prev-icon,
+.carousel-control-next-icon {
+  display: inline-block;
+  width: $carousel-control-icon-width;
+  height: $carousel-control-icon-width;
+  background: transparent no-repeat center center;
+  background-size: 100% 100%;
+}
+.carousel-control-prev-icon {
+  background-image: $carousel-control-prev-icon-bg;
+}
+.carousel-control-next-icon {
+  background-image: $carousel-control-next-icon-bg;
+}
+
+
+// Optional indicator pips
+//
+// Add an ordered list with the following class and add a list item for each
+// slide your carousel holds.
+
+.carousel-indicators {
+  position: absolute;
+  right: 0;
+  bottom: 10px;
+  left: 0;
+  z-index: 15;
+  display: flex;
+  justify-content: center;
+  padding-left: 0; // override <ol> default
+  // Use the .carousel-control's width as margin so we don't overlay those
+  margin-right: $carousel-control-width;
+  margin-left: $carousel-control-width;
+  list-style: none;
+
+  li {
+    position: relative;
+    flex: 1 0 auto;
+    max-width: $carousel-indicator-width;
+    height: $carousel-indicator-height;
+    margin-right: $carousel-indicator-spacer;
+    margin-left: $carousel-indicator-spacer;
+    text-indent: -999px;
+    cursor: pointer;
+    background-color: rgba($carousel-indicator-active-bg, .5);
+
+    // Use pseudo classes to increase the hit area by 10px on top and bottom.
+    &::before {
+      position: absolute;
+      top: -10px;
+      left: 0;
+      display: inline-block;
+      width: 100%;
+      height: 10px;
+      content: "";
+    }
+    &::after {
+      position: absolute;
+      bottom: -10px;
+      left: 0;
+      display: inline-block;
+      width: 100%;
+      height: 10px;
+      content: "";
+    }
+  }
+
+  .active {
+    background-color: $carousel-indicator-active-bg;
+  }
+}
+
+
+// Optional captions
+//
+//
+
+.carousel-caption {
+  position: absolute;
+  right: ((100% - $carousel-caption-width) / 2);
+  bottom: 20px;
+  left: ((100% - $carousel-caption-width) / 2);
+  z-index: 10;
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: $carousel-caption-color;
+  text-align: center;
+}
diff --git a/src/styles/bootstrap/_close.scss b/src/styles/bootstrap/_close.scss
new file mode 100755
index 0000000000000000000000000000000000000000..859990e3105abe1506516e1c7cdfeb44422711c8
--- /dev/null
+++ b/src/styles/bootstrap/_close.scss
@@ -0,0 +1,31 @@
+.close {
+  float: right;
+  font-size: $close-font-size;
+  font-weight: $close-font-weight;
+  line-height: 1;
+  color: $close-color;
+  text-shadow: $close-text-shadow;
+  opacity: .5;
+
+  @include hover-focus {
+    color: $close-color;
+    text-decoration: none;
+    cursor: pointer;
+    opacity: .75;
+  }
+}
+
+// Additional properties for button version
+// iOS requires the button element instead of an anchor tag.
+// If you want the anchor version, it requires `href="#"`.
+// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
+
+// scss-lint:disable QualifyingElement
+button.close {
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+  -webkit-appearance: none;
+}
+// scss-lint:enable QualifyingElement
diff --git a/src/styles/bootstrap/_code.scss b/src/styles/bootstrap/_code.scss
new file mode 100755
index 0000000000000000000000000000000000000000..759da15b791e70d7f7f9e8f9ea8877e2449a74a1
--- /dev/null
+++ b/src/styles/bootstrap/_code.scss
@@ -0,0 +1,64 @@
+// Inline and block code styles
+code,
+kbd,
+pre,
+samp {
+  font-family: $font-family-monospace;
+}
+
+// Inline code
+code {
+  padding: $code-padding-y $code-padding-x;
+  font-size: $code-font-size;
+  color: $code-color;
+  background-color: $code-bg;
+  @include border-radius($border-radius);
+
+  // Streamline the style when inside anchors to avoid broken underline and more
+  a > & {
+    padding: 0;
+    color: inherit;
+    background-color: inherit;
+  }
+}
+
+// User input typically entered via keyboard
+kbd {
+  padding: $code-padding-y $code-padding-x;
+  font-size: $code-font-size;
+  color: $kbd-color;
+  background-color: $kbd-bg;
+  @include border-radius($border-radius-sm);
+  @include box-shadow($kbd-box-shadow);
+
+  kbd {
+    padding: 0;
+    font-size: 100%;
+    font-weight: $nested-kbd-font-weight;
+    @include box-shadow(none);
+  }
+}
+
+// Blocks of code
+pre {
+  display: block;
+  margin-top: 0;
+  margin-bottom: 1rem;
+  font-size: $code-font-size;
+  color: $pre-color;
+
+  // Account for some code outputs that place code tags in pre tags
+  code {
+    padding: 0;
+    font-size: inherit;
+    color: inherit;
+    background-color: transparent;
+    border-radius: 0;
+  }
+}
+
+// Enable scrollable blocks of code
+.pre-scrollable {
+  max-height: $pre-scrollable-max-height;
+  overflow-y: scroll;
+}
diff --git a/src/styles/bootstrap/_custom-forms.scss b/src/styles/bootstrap/_custom-forms.scss
new file mode 100755
index 0000000000000000000000000000000000000000..ef2aab3544d0376b681652fbcdd6b4b0e7dbbe22
--- /dev/null
+++ b/src/styles/bootstrap/_custom-forms.scss
@@ -0,0 +1,263 @@
+// scss-lint:disable PropertyCount
+
+// Embedded icons from Open Iconic.
+// Released under MIT and copyright 2014 Waybury.
+// https://useiconic.com/open
+
+
+// Checkboxes and radios
+//
+// Base class takes care of all the key behavioral aspects.
+
+.custom-control {
+  position: relative;
+  display: inline-flex;
+  min-height: (1rem * $line-height-base);
+  padding-left: $custom-control-gutter;
+  margin-right: $custom-control-spacer-x;
+  cursor: pointer;
+}
+
+.custom-control-input {
+  position: absolute;
+  z-index: -1; // Put the input behind the label so it doesn't overlay text
+  opacity: 0;
+
+  &:checked ~ .custom-control-indicator {
+    color: $custom-control-checked-indicator-color;
+    background-color: $custom-control-checked-indicator-bg;
+    @include box-shadow($custom-control-checked-indicator-box-shadow);
+  }
+
+  &:focus ~ .custom-control-indicator {
+    // the mixin is not used here to make sure there is feedback
+    box-shadow: $custom-control-focus-indicator-box-shadow;
+  }
+
+  &:active ~ .custom-control-indicator {
+    color: $custom-control-active-indicator-color;
+    background-color: $custom-control-active-indicator-bg;
+    @include box-shadow($custom-control-active-indicator-box-shadow);
+  }
+
+  &:disabled {
+    ~ .custom-control-indicator {
+      cursor: $custom-control-disabled-cursor;
+      background-color: $custom-control-disabled-indicator-bg;
+    }
+
+    ~ .custom-control-description {
+      color: $custom-control-disabled-description-color;
+      cursor: $custom-control-disabled-cursor;
+    }
+  }
+}
+
+// Custom indicator
+//
+// Generates a shadow element to create our makeshift checkbox/radio background.
+
+.custom-control-indicator {
+  position: absolute;
+  top: (($line-height-base - $custom-control-indicator-size) / 2);
+  left: 0;
+  display: block;
+  width: $custom-control-indicator-size;
+  height: $custom-control-indicator-size;
+  pointer-events: none;
+  user-select: none;
+  background-color: $custom-control-indicator-bg;
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: $custom-control-indicator-bg-size;
+  @include box-shadow($custom-control-indicator-box-shadow);
+}
+
+// Checkboxes
+//
+// Tweak just a few things for checkboxes.
+
+.custom-checkbox {
+  .custom-control-indicator {
+    @include border-radius($custom-checkbox-radius);
+  }
+
+  .custom-control-input:checked ~ .custom-control-indicator {
+    background-image: $custom-checkbox-checked-icon;
+  }
+
+  .custom-control-input:indeterminate ~ .custom-control-indicator {
+    background-color: $custom-checkbox-indeterminate-bg;
+    background-image: $custom-checkbox-indeterminate-icon;
+    @include box-shadow($custom-checkbox-indeterminate-box-shadow);
+  }
+}
+
+// Radios
+//
+// Tweak just a few things for radios.
+
+.custom-radio {
+  .custom-control-indicator {
+    border-radius: $custom-radio-radius;
+  }
+
+  .custom-control-input:checked ~ .custom-control-indicator {
+    background-image: $custom-radio-checked-icon;
+  }
+}
+
+
+// Layout options
+//
+// By default radios and checkboxes are `inline-block` with no additional spacing
+// set. Use these optional classes to tweak the layout.
+
+.custom-controls-stacked {
+  display: flex;
+  flex-direction: column;
+
+  .custom-control {
+    margin-bottom: $custom-control-spacer-y;
+
+    + .custom-control {
+      margin-left: 0;
+    }
+  }
+}
+
+
+// Select
+//
+// Replaces the browser default select with a custom one, mostly pulled from
+// http://primercss.io.
+//
+
+.custom-select {
+  display: inline-block;
+  max-width: 100%;
+  $select-border-width: ($border-width * 2);
+  height: calc(#{$input-height} + #{$select-border-width});
+  padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x;
+  line-height: $custom-select-line-height;
+  color: $custom-select-color;
+  vertical-align: middle;
+  background: $custom-select-bg $custom-select-indicator no-repeat right $custom-select-padding-x center;
+  background-size: $custom-select-bg-size;
+  border: $custom-select-border-width solid $custom-select-border-color;
+  @include border-radius($custom-select-border-radius);
+  // Use vendor prefixes as `appearance` isn't part of the CSS spec.
+  -moz-appearance: none;
+  -webkit-appearance: none;
+
+  &:focus {
+    border-color: $custom-select-focus-border-color;
+    outline: none;
+    @include box-shadow($custom-select-focus-box-shadow);
+
+    &::-ms-value {
+      // For visual consistency with other platforms/browsers,
+      // supress the default white text on blue background highlight given to
+      // the selected option text when the (still closed) <select> receives focus
+      // in IE and (under certain conditions) Edge.
+      // See https://github.com/twbs/bootstrap/issues/19398.
+      color: $input-color;
+      background-color: $input-bg;
+    }
+  }
+
+  &:disabled {
+    color: $custom-select-disabled-color;
+    cursor: $cursor-disabled;
+    background-color: $custom-select-disabled-bg;
+  }
+
+  // Hides the default caret in IE11
+  &::-ms-expand {
+    opacity: 0;
+  }
+}
+
+.custom-select-sm {
+  padding-top: $custom-select-padding-y;
+  padding-bottom: $custom-select-padding-y;
+  font-size: $custom-select-sm-font-size;
+
+  // &:not([multiple]) {
+  //   height: 26px;
+  //   min-height: 26px;
+  // }
+}
+
+
+// File
+//
+// Custom file input.
+
+.custom-file {
+  position: relative;
+  display: inline-block;
+  max-width: 100%;
+  height: $custom-file-height;
+  margin-bottom: 0;
+  cursor: pointer;
+}
+
+.custom-file-input {
+  min-width: $custom-file-width;
+  max-width: 100%;
+  height: $custom-file-height;
+  margin: 0;
+  filter: alpha(opacity = 0);
+  opacity: 0;
+
+  &:focus ~ .custom-file-control {
+    @include box-shadow($custom-file-focus-box-shadow);
+  }
+}
+
+.custom-file-control {
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  z-index: 5;
+  height: $custom-file-height;
+  padding: $custom-file-padding-x $custom-file-padding-y;
+  line-height: $custom-file-line-height;
+  color: $custom-file-color;
+  pointer-events: none;
+  user-select: none;
+  background-color: $custom-file-bg;
+  border: $custom-file-border-width solid $custom-file-border-color;
+  @include border-radius($custom-file-border-radius);
+  @include box-shadow($custom-file-box-shadow);
+
+  @each $lang, $text in map-get($custom-file-text, placeholder) {
+    &:lang(#{$lang})::after {
+      content: $text;
+    }
+  }
+
+  &::before {
+    position: absolute;
+    top: -$custom-file-border-width;
+    right: -$custom-file-border-width;
+    bottom: -$custom-file-border-width;
+    z-index: 6;
+    display: block;
+    height: $custom-file-height;
+    padding: $custom-file-padding-x $custom-file-padding-y;
+    line-height: $custom-file-line-height;
+    color: $custom-file-button-color;
+    background-color: $custom-file-button-bg;
+    border: $custom-file-border-width solid $custom-file-border-color;
+    @include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0);
+  }
+
+  @each $lang, $text in map-get($custom-file-text, button-label) {
+    &:lang(#{$lang})::before {
+      content: $text;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/_custom.scss b/src/styles/bootstrap/_custom.scss
new file mode 100755
index 0000000000000000000000000000000000000000..88ccf202e4462a04df892f8558b127f083ae55a8
--- /dev/null
+++ b/src/styles/bootstrap/_custom.scss
@@ -0,0 +1,4 @@
+// Bootstrap overrides
+//
+// Copy variables from `_variables.scss` to this file to override default values
+// without modifying source files.
diff --git a/src/styles/bootstrap/_dropdown.scss b/src/styles/bootstrap/_dropdown.scss
new file mode 100755
index 0000000000000000000000000000000000000000..1c2741a2e36a2d3bdba7dfed81d0d4e01e814422
--- /dev/null
+++ b/src/styles/bootstrap/_dropdown.scss
@@ -0,0 +1,161 @@
+// The dropdown wrapper (`<div>`)
+.dropup,
+.dropdown {
+  position: relative;
+}
+
+.dropdown-toggle {
+  // Generate the caret automatically
+  &::after {
+    display: inline-block;
+    width: 0;
+    height: 0;
+    margin-left: $caret-width;
+    vertical-align: middle;
+    content: "";
+    border-top: $caret-width solid;
+    border-right: $caret-width solid transparent;
+    border-left: $caret-width solid transparent;
+  }
+
+  // Prevent the focus on the dropdown toggle when closing dropdowns
+  &:focus {
+    outline: 0;
+  }
+}
+
+.dropup {
+  .dropdown-toggle {
+    &::after {
+      border-top: 0;
+      border-bottom: $caret-width solid;
+    }
+  }
+}
+
+// The dropdown menu
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: $zindex-dropdown;
+  display: none; // none by default, but block on "open" of the menu
+  float: left;
+  min-width: $dropdown-min-width;
+  padding: $dropdown-padding-y 0;
+  margin: $dropdown-margin-top 0 0; // override default ul
+  font-size: $font-size-base; // Redeclare because nesting can cause inheritance issues
+  color: $body-color;
+  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
+  list-style: none;
+  background-color: $dropdown-bg;
+  background-clip: padding-box;
+  border: $dropdown-border-width solid $dropdown-border-color;
+  @include border-radius($border-radius);
+  @include box-shadow($dropdown-box-shadow);
+}
+
+// Dividers (basically an `<hr>`) within the dropdown
+.dropdown-divider {
+  @include nav-divider($dropdown-divider-bg);
+}
+
+// Links, buttons, and more within the dropdown menu
+//
+// `<button>`-specific styles are denoted with `// For <button>s`
+.dropdown-item {
+  display: block;
+  width: 100%; // For `<button>`s
+  padding: 3px $dropdown-item-padding-x;
+  clear: both;
+  font-weight: $font-weight-normal;
+  color: $dropdown-link-color;
+  text-align: inherit; // For `<button>`s
+  white-space: nowrap; // prevent links from randomly breaking onto new lines
+  background: none; // For `<button>`s
+  border: 0; // For `<button>`s
+
+  @include hover-focus {
+    color: $dropdown-link-hover-color;
+    text-decoration: none;
+    background-color: $dropdown-link-hover-bg;
+  }
+
+  &.active,
+  &:active {
+    color: $dropdown-link-active-color;
+    text-decoration: none;
+    background-color: $dropdown-link-active-bg;
+  }
+
+  &.disabled,
+  &:disabled {
+    color: $dropdown-link-disabled-color;
+    cursor: $cursor-disabled;
+    background-color: transparent;
+    // Remove CSS gradients if they're enabled
+    @if $enable-gradients {
+      background-image: none;
+    }
+  }
+}
+
+// Open state for the dropdown
+.show {
+  // Show the menu
+  > .dropdown-menu {
+    display: block;
+  }
+
+  // Remove the outline when :focus is triggered
+  > a {
+    outline: 0;
+  }
+}
+
+// Menu positioning
+//
+// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown
+// menu with the parent.
+.dropdown-menu-right {
+  right: 0;
+  left: auto; // Reset the default from `.dropdown-menu`
+}
+
+.dropdown-menu-left {
+  right: auto;
+  left: 0;
+}
+
+// Dropdown section headers
+.dropdown-header {
+  display: block;
+  padding: $dropdown-padding-y $dropdown-item-padding-x;
+  margin-bottom: 0; // for use with heading elements
+  font-size: $font-size-sm;
+  color: $dropdown-header-color;
+  white-space: nowrap; // as with > li > a
+}
+
+// Backdrop to catch body clicks on mobile, etc.
+.dropdown-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-dropdown-backdrop;
+}
+
+// Allow for dropdowns to go bottom up (aka, dropup-menu)
+//
+// Just add .dropup after the standard .dropdown class and you're set.
+
+.dropup {
+  // Different positioning for bottom up menu
+  .dropdown-menu {
+    top: auto;
+    bottom: 100%;
+    margin-bottom: $dropdown-margin-top;
+  }
+}
diff --git a/src/styles/bootstrap/_forms.scss b/src/styles/bootstrap/_forms.scss
new file mode 100755
index 0000000000000000000000000000000000000000..7be62bde662e83cb996c84cbeccfc2e1f98d3fd8
--- /dev/null
+++ b/src/styles/bootstrap/_forms.scss
@@ -0,0 +1,388 @@
+// scss-lint:disable QualifyingElement
+
+//
+// Textual form controls
+//
+
+.form-control {
+  display: block;
+  width: 100%;
+  // // Make inputs at least the height of their button counterpart (base line-height + padding + border)
+  // height: $input-height;
+  padding: $input-padding-y $input-padding-x;
+  font-size: $font-size-base;
+  line-height: $input-line-height;
+  color: $input-color;
+  background-color: $input-bg;
+  // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214.
+  background-image: none;
+  background-clip: padding-box;
+  border: $input-btn-border-width solid $input-border-color;
+
+  // Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
+  @if $enable-rounded {
+    // Manually use the if/else instead of the mixin to account for iOS override
+    border-radius: $input-border-radius;
+  } @else {
+    // Otherwise undo the iOS default
+    border-radius: 0;
+  }
+
+  @include box-shadow($input-box-shadow);
+  @include transition($input-transition);
+
+  // Unstyle the caret on `<select>`s in IE10+.
+  &::-ms-expand {
+    background-color: transparent;
+    border: 0;
+  }
+
+  // Customize the `:focus` state to imitate native WebKit styles.
+  @include form-control-focus();
+
+  // Placeholder
+  &::placeholder {
+    color: $input-color-placeholder;
+    // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
+    opacity: 1;
+  }
+
+  // Disabled and read-only inputs
+  //
+  // HTML5 says that controls under a fieldset > legend:first-child won't be
+  // disabled if the fieldset is disabled. Due to implementation difficulty, we
+  // don't honor that edge case; we style them as disabled anyway.
+  &:disabled,
+  &[readonly] {
+    background-color: $input-bg-disabled;
+    // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
+    opacity: 1;
+  }
+
+  &:disabled {
+    cursor: $cursor-disabled;
+  }
+}
+
+select.form-control {
+  &:not([size]):not([multiple]) {
+    $select-border-width: ($border-width * 2);
+    height: calc(#{$input-height} + #{$select-border-width});
+  }
+
+  &:focus::-ms-value {
+    // Suppress the nested default white text on blue background highlight given to
+    // the selected option text when the (still closed) <select> receives focus
+    // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to
+    // match the appearance of the native widget.
+    // See https://github.com/twbs/bootstrap/issues/19398.
+    color: $input-color;
+    background-color: $input-bg;
+  }
+}
+
+// Make file inputs better match text inputs by forcing them to new lines.
+.form-control-file,
+.form-control-range {
+  display: block;
+}
+
+
+//
+// Labels
+//
+
+// For use with horizontal and inline forms, when you need the label text to
+// align with the form controls.
+.col-form-label {
+  padding-top: calc(#{$input-padding-y} - #{$input-btn-border-width} * 2);
+  padding-bottom: calc(#{$input-padding-y} - #{$input-btn-border-width} * 2);
+  margin-bottom: 0; // Override the `<label>` default
+}
+
+.col-form-label-lg {
+  padding-top: calc(#{$input-padding-y-lg} - #{$input-btn-border-width} * 2);
+  padding-bottom: calc(#{$input-padding-y-lg} - #{$input-btn-border-width} * 2);
+  font-size: $font-size-lg;
+}
+
+.col-form-label-sm {
+  padding-top: calc(#{$input-padding-y-sm} - #{$input-btn-border-width} * 2);
+  padding-bottom: calc(#{$input-padding-y-sm} - #{$input-btn-border-width} * 2);
+  font-size: $font-size-sm;
+}
+
+
+//
+// Legends
+//
+
+// For use with horizontal and inline forms, when you need the legend text to
+// be the same size as regular labels, and to align with the form controls.
+.col-form-legend {
+  padding-top: $input-padding-y;
+  padding-bottom: $input-padding-y;
+  margin-bottom: 0;
+  font-size: $font-size-base;
+}
+
+
+// Static form control text
+//
+// Apply class to an element to make any string of text align with labels in a
+// horizontal form layout.
+
+.form-control-static {
+  padding-top: $input-padding-y;
+  padding-bottom: $input-padding-y;
+  margin-bottom: 0; // match inputs if this class comes on inputs with default margins
+  line-height: $input-line-height;
+  border: solid transparent;
+  border-width: $input-btn-border-width 0;
+
+  &.form-control-sm,
+  &.form-control-lg {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+
+
+// Form control sizing
+//
+// Build on `.form-control` with modifier classes to decrease or increase the
+// height and font-size of form controls.
+//
+// The `.form-group-* form-control` variations are sadly duplicated to avoid the
+// issue documented in https://github.com/twbs/bootstrap/issues/15074.
+
+.form-control-sm {
+  padding: $input-padding-y-sm $input-padding-x-sm;
+  font-size: $font-size-sm;
+  @include border-radius($input-border-radius-sm);
+}
+
+select.form-control-sm {
+  &:not([size]):not([multiple]) {
+    height: $input-height-sm;
+  }
+}
+
+.form-control-lg {
+  padding: $input-padding-y-lg $input-padding-x-lg;
+  font-size: $font-size-lg;
+  @include border-radius($input-border-radius-lg);
+}
+
+select.form-control-lg {
+  &:not([size]):not([multiple]) {
+    height: $input-height-lg;
+  }
+}
+
+
+// Form groups
+//
+// Designed to help with the organization and spacing of vertical forms. For
+// horizontal forms, use the predefined grid classes.
+
+.form-group {
+  margin-bottom: $form-group-margin-bottom;
+}
+
+.form-text {
+  display: block;
+  margin-top: $form-text-margin-top;
+}
+
+
+// Checkboxes and radios
+//
+// Indent the labels to position radios/checkboxes as hanging controls.
+
+.form-check {
+  position: relative;
+  display: block;
+  margin-bottom: $form-check-margin-bottom;
+
+  &.disabled {
+    .form-check-label {
+      color: $text-muted;
+      cursor: $cursor-disabled;
+    }
+  }
+}
+
+.form-check-label {
+  padding-left: $form-check-input-gutter;
+  margin-bottom: 0; // Override default `<label>` bottom margin
+  cursor: pointer;
+}
+
+.form-check-input {
+  position: absolute;
+  margin-top: $form-check-input-margin-y;
+  margin-left: -$form-check-input-gutter;
+
+  &:only-child {
+    position: static;
+  }
+}
+
+// Radios and checkboxes on same line
+.form-check-inline {
+  display: inline-block;
+
+  .form-check-label {
+    vertical-align: middle;
+  }
+
+  + .form-check-inline {
+    margin-left: $form-check-inline-margin-x;
+  }
+}
+
+
+// Form control feedback states
+//
+// Apply contextual and semantic states to individual form controls.
+
+.form-control-feedback {
+  margin-top: $form-feedback-margin-top;
+}
+
+.form-control-success,
+.form-control-warning,
+.form-control-danger {
+  padding-right: ($input-padding-x * 3);
+  background-repeat: no-repeat;
+  background-position: center right ($input-height / 4);
+  background-size: ($input-height / 2) ($input-height / 2);
+}
+
+// Form validation states
+.has-success {
+  @include form-control-validation($brand-success);
+
+  .form-control-success {
+    background-image: $form-icon-success;
+  }
+}
+
+.has-warning {
+  @include form-control-validation($brand-warning);
+
+  .form-control-warning {
+    background-image: $form-icon-warning;
+  }
+}
+
+.has-danger {
+  @include form-control-validation($brand-danger);
+
+  .form-control-danger {
+    background-image: $form-icon-danger;
+  }
+}
+
+
+// Inline forms
+//
+// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
+// forms begin stacked on extra small (mobile) devices and then go inline when
+// viewports reach <768px.
+//
+// Requires wrapping inputs and labels with `.form-group` for proper display of
+// default HTML form controls and our custom form controls (e.g., input groups).
+
+.form-inline {
+  display: flex;
+  flex-flow: row wrap;
+  align-items: center; // Prevent shorter elements from growing to same height as others (e.g., small buttons growing to normal sized button height)
+
+  // Because we use flex, the initial sizing of checkboxes is collapsed and
+  // doesn't occupy the full-width (which is what we want for xs grid tier),
+  // so we force that here.
+  .form-check {
+    width: 100%;
+  }
+
+  // Kick in the inline
+  @include media-breakpoint-up(sm) {
+    label {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-bottom: 0;
+    }
+
+    // Inline-block all the things for "inline"
+    .form-group {
+      display: flex;
+      flex: 0 0 auto;
+      flex-flow: row wrap;
+      align-items: center;
+      margin-bottom: 0;
+    }
+
+    // Allow folks to *not* use `.form-group`
+    .form-control {
+      display: inline-block;
+      width: auto; // Prevent labels from stacking above inputs in `.form-group`
+      vertical-align: middle;
+    }
+
+    // Make static controls behave like regular ones
+    .form-control-static {
+      display: inline-block;
+    }
+
+    .input-group {
+      width: auto;
+    }
+
+    .form-control-label {
+      margin-bottom: 0;
+      vertical-align: middle;
+    }
+
+    // Remove default margin on radios/checkboxes that were used for stacking, and
+    // then undo the floating of radios and checkboxes to match.
+    .form-check {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: auto;
+      margin-top: 0;
+      margin-bottom: 0;
+    }
+    .form-check-label {
+      padding-left: 0;
+    }
+    .form-check-input {
+      position: relative;
+      margin-top: 0;
+      margin-right: $form-check-input-margin-x;
+      margin-left: 0;
+    }
+
+    // Custom form controls
+    .custom-control {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding-left: 0;
+    }
+    .custom-control-indicator {
+      position: static;
+      display: inline-block;
+      margin-right: $form-check-input-margin-x; // Flexbox alignment means we lose our HTML space here, so we compensate.
+      vertical-align: text-bottom;
+    }
+
+    // Re-override the feedback icon.
+    .has-feedback .form-control-feedback {
+      top: 0;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/_grid.scss b/src/styles/bootstrap/_grid.scss
new file mode 100755
index 0000000000000000000000000000000000000000..8c7a9ee3188e183f5240c2ef6432bc4e5954ac60
--- /dev/null
+++ b/src/styles/bootstrap/_grid.scss
@@ -0,0 +1,52 @@
+// Container widths
+//
+// Set the container width, and override it for fixed navbars in media queries.
+
+@if $enable-grid-classes {
+  .container {
+    @include make-container();
+    @include make-container-max-widths();
+  }
+}
+
+// Fluid container
+//
+// Utilizes the mixin meant for fixed width containers, but without any defined
+// width for fluid, full width layouts.
+
+@if $enable-grid-classes {
+  .container-fluid {
+    @include make-container();
+  }
+}
+
+// Row
+//
+// Rows contain and clear the floats of your columns.
+
+@if $enable-grid-classes {
+  .row {
+    @include make-row();
+  }
+
+  // Remove the negative margin from default .row, then the horizontal padding
+  // from all immediate children columns (to prevent runaway style inheritance).
+  .no-gutters {
+    margin-right: 0;
+    margin-left: 0;
+
+    > .col,
+    > [class*="col-"] {
+      padding-right: 0;
+      padding-left: 0;
+    }
+  }
+}
+
+// Columns
+//
+// Common styles for small and large grid columns
+
+@if $enable-grid-classes {
+  @include make-grid-columns();
+}
diff --git a/src/styles/bootstrap/_images.scss b/src/styles/bootstrap/_images.scss
new file mode 100755
index 0000000000000000000000000000000000000000..a8135a6c35d4f4ec2e6a951ab0a35b56e0bcc30c
--- /dev/null
+++ b/src/styles/bootstrap/_images.scss
@@ -0,0 +1,43 @@
+// Responsive images (ensure images don't scale beyond their parents)
+//
+// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s.
+// We previously tried the "images are responsive by default" approach in Bootstrap v2,
+// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)
+// which weren't expecting the images within themselves to be involuntarily resized.
+// See also https://github.com/twbs/bootstrap/issues/18178
+.img-fluid {
+  @include img-fluid;
+}
+
+
+// Image thumbnails
+.img-thumbnail {
+  padding: $thumbnail-padding;
+  background-color: $thumbnail-bg;
+  border: $thumbnail-border-width solid $thumbnail-border-color;
+  @include border-radius($thumbnail-border-radius);
+  @include transition($thumbnail-transition);
+  @include box-shadow($thumbnail-box-shadow);
+
+  // Keep them at most 100% wide
+  @include img-fluid;
+}
+
+//
+// Figures
+//
+
+.figure {
+  // Ensures the caption's text aligns with the image.
+  display: inline-block;
+}
+
+.figure-img {
+  margin-bottom: ($spacer-y / 2);
+  line-height: 1;
+}
+
+.figure-caption {
+  font-size: $figure-caption-font-size;
+  color: $figure-caption-color;
+}
diff --git a/src/styles/bootstrap/_input-group.scss b/src/styles/bootstrap/_input-group.scss
new file mode 100755
index 0000000000000000000000000000000000000000..ab44883bd1960ff63a5dcd58226f6ddc33c618c0
--- /dev/null
+++ b/src/styles/bootstrap/_input-group.scss
@@ -0,0 +1,178 @@
+//
+// Base styles
+//
+
+.input-group {
+  position: relative;
+  display: flex;
+  width: 100%;
+
+  .form-control {
+    // Ensure that the input is always above the *appended* addon button for
+    // proper border colors.
+    position: relative;
+    z-index: 2;
+    flex: 1 1 auto;
+    // Add width 1% and flex-basis auto to ensure that button will not wrap out
+    // the column. Applies to IE Edge+ and Firefox. Chrome does not require this.
+    width: 1%;
+    margin-bottom: 0;
+
+    // Bring the "active" form control to the front
+    @include hover-focus-active {
+      z-index: 3;
+    }
+  }
+}
+
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+  // Vertically centers the content of the addons within the input group
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+
+  &:not(:first-child):not(:last-child) {
+    @include border-radius(0);
+  }
+}
+
+.input-group-addon,
+.input-group-btn {
+  white-space: nowrap;
+  vertical-align: middle; // Match the inputs
+}
+
+
+// Sizing options
+//
+// Remix the default form control sizing classes into new ones for easier
+// manipulation.
+
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+  @extend .form-control-lg;
+}
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+  @extend .form-control-sm;
+}
+
+
+//
+// Text input groups
+//
+
+.input-group-addon {
+  padding: $input-padding-y $input-padding-x;
+  margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
+  font-size: $font-size-base; // Match inputs
+  font-weight: $font-weight-normal;
+  line-height: $input-line-height;
+  color: $input-color;
+  text-align: center;
+  background-color: $input-group-addon-bg;
+  border: $input-btn-border-width solid $input-group-addon-border-color;
+  @include border-radius($input-border-radius);
+
+  // Sizing
+  &.form-control-sm {
+    padding: $input-padding-y-sm $input-padding-x-sm;
+    font-size: $font-size-sm;
+    @include border-radius($input-border-radius-sm);
+  }
+  &.form-control-lg {
+    padding: $input-padding-y-lg $input-padding-x-lg;
+    font-size: $font-size-lg;
+    @include border-radius($input-border-radius-lg);
+  }
+
+  // scss-lint:disable QualifyingElement
+  // Nuke default margins from checkboxes and radios to vertically center within.
+  input[type="radio"],
+  input[type="checkbox"] {
+    margin-top: 0;
+  }
+  // scss-lint:enable QualifyingElement
+}
+
+
+//
+// Reset rounded corners
+//
+
+.input-group .form-control:not(:last-child),
+.input-group-addon:not(:last-child),
+.input-group-btn:not(:last-child) > .btn,
+.input-group-btn:not(:last-child) > .btn-group > .btn,
+.input-group-btn:not(:last-child) > .dropdown-toggle,
+.input-group-btn:not(:first-child) > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:not(:first-child) > .btn-group:not(:last-child) > .btn {
+  @include border-right-radius(0);
+}
+.input-group-addon:not(:last-child) {
+  border-right: 0;
+}
+.input-group .form-control:not(:first-child),
+.input-group-addon:not(:first-child),
+.input-group-btn:not(:first-child) > .btn,
+.input-group-btn:not(:first-child) > .btn-group > .btn,
+.input-group-btn:not(:first-child) > .dropdown-toggle,
+.input-group-btn:not(:last-child) > .btn:not(:first-child),
+.input-group-btn:not(:last-child) > .btn-group:not(:first-child) > .btn {
+  @include border-left-radius(0);
+}
+.form-control + .input-group-addon:not(:first-child) {
+  border-left: 0;
+}
+
+//
+// Button input groups
+//
+
+.input-group-btn {
+  position: relative;
+  // Jankily prevent input button groups from wrapping with `white-space` and
+  // `font-size` in combination with `inline-block` on buttons.
+  font-size: 0;
+  white-space: nowrap;
+
+  // Negative margin for spacing, position for bringing hovered/focused/actived
+  // element above the siblings.
+  > .btn {
+    position: relative;
+    // Vertically stretch the button and center its content
+    flex: 1;
+
+    + .btn {
+      margin-left: (-$input-btn-border-width);
+    }
+
+    // Bring the "active" button to the front
+    @include hover-focus-active {
+      z-index: 3;
+    }
+  }
+
+  // Negative margin to only have a single, shared border between the two
+  &:not(:last-child) {
+    > .btn,
+    > .btn-group {
+      margin-right: (-$input-btn-border-width);
+    }
+  }
+  &:not(:first-child) {
+    > .btn,
+    > .btn-group {
+      z-index: 2;
+      margin-left: (-$input-btn-border-width);
+      // Because specificity
+      @include hover-focus-active {
+        z-index: 3;
+      }
+    }
+  }
+}
diff --git a/src/styles/bootstrap/_jumbotron.scss b/src/styles/bootstrap/_jumbotron.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b12d465d973790bfda1fc2ddc9512e21115fadc9
--- /dev/null
+++ b/src/styles/bootstrap/_jumbotron.scss
@@ -0,0 +1,20 @@
+.jumbotron {
+  padding: $jumbotron-padding ($jumbotron-padding / 2);
+  margin-bottom: $jumbotron-padding;
+  background-color: $jumbotron-bg;
+  @include border-radius($border-radius-lg);
+
+  @include media-breakpoint-up(sm) {
+    padding: ($jumbotron-padding * 2) $jumbotron-padding;
+  }
+}
+
+.jumbotron-hr {
+  border-top-color: darken($jumbotron-bg, 10%);
+}
+
+.jumbotron-fluid {
+  padding-right: 0;
+  padding-left: 0;
+  @include border-radius(0);
+}
diff --git a/src/styles/bootstrap/_list-group.scss b/src/styles/bootstrap/_list-group.scss
new file mode 100755
index 0000000000000000000000000000000000000000..ec813c807ed91e412fd5c236a1a0b8ec5a525e9d
--- /dev/null
+++ b/src/styles/bootstrap/_list-group.scss
@@ -0,0 +1,141 @@
+// Base class
+//
+// Easily usable on <ul>, <ol>, or <div>.
+
+.list-group {
+  display: flex;
+  flex-direction: column;
+
+  // No need to set list-style: none; since .list-group-item is block level
+  padding-left: 0; // reset padding because ul and ol
+  margin-bottom: 0;
+}
+
+
+// Interactive list items
+//
+// Use anchor or button elements instead of `li`s or `div`s to create interactive
+// list items. Includes an extra `.active` modifier class for selected items.
+
+.list-group-item-action {
+  width: 100%; // For `<button>`s (anchors become 100% by default though)
+  color: $list-group-link-color;
+  text-align: inherit; // For `<button>`s (anchors inherit)
+
+  .list-group-item-heading {
+    color: $list-group-link-heading-color;
+  }
+
+  // Hover state
+  @include hover-focus {
+    color: $list-group-link-hover-color;
+    text-decoration: none;
+    background-color: $list-group-hover-bg;
+  }
+
+  &:active {
+    color: $list-group-link-active-color;
+    background-color: $list-group-link-active-bg;
+  }
+}
+
+
+// Individual list items
+//
+// Use on `li`s or `div`s within the `.list-group` parent.
+
+.list-group-item {
+  position: relative;
+  display: flex;
+  flex-flow: row wrap;
+  align-items: center;
+  padding: $list-group-item-padding-y $list-group-item-padding-x;
+  // Place the border on the list items and negative margin up for better styling
+  margin-bottom: -$list-group-border-width;
+  background-color: $list-group-bg;
+  border: $list-group-border-width solid $list-group-border-color;
+
+  &:first-child {
+    @include border-top-radius($list-group-border-radius);
+  }
+
+  &:last-child {
+    margin-bottom: 0;
+    @include border-bottom-radius($list-group-border-radius);
+  }
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+
+  &.disabled,
+  &:disabled {
+    color: $list-group-disabled-color;
+    cursor: $cursor-disabled;
+    background-color: $list-group-disabled-bg;
+
+    // Force color to inherit for custom content
+    .list-group-item-heading {
+      color: inherit;
+    }
+    .list-group-item-text {
+      color: $list-group-disabled-text-color;
+    }
+  }
+
+  // Include both here for `<a>`s and `<button>`s
+  &.active {
+    z-index: 2; // Place active items above their siblings for proper border styling
+    color: $list-group-active-color;
+    background-color: $list-group-active-bg;
+    border-color: $list-group-active-border;
+
+    // Force color to inherit for custom content
+    .list-group-item-heading,
+    .list-group-item-heading > small,
+    .list-group-item-heading > .small {
+      color: inherit;
+    }
+
+    .list-group-item-text {
+      color: $list-group-active-text-color;
+    }
+  }
+}
+
+
+// Flush list items
+//
+// Remove borders and border-radius to keep list group items edge-to-edge. Most
+// useful within other components (e.g., cards).
+
+.list-group-flush {
+  .list-group-item {
+    border-right: 0;
+    border-left: 0;
+    border-radius: 0;
+  }
+
+  &:first-child {
+    .list-group-item:first-child {
+      border-top: 0;
+    }
+  }
+
+  &:last-child {
+    .list-group-item:last-child {
+      border-bottom: 0;
+    }
+  }
+}
+
+
+// Contextual variants
+//
+// Add modifier classes to change text and background color on individual items.
+// Organizationally, this must come after the `:hover` states.
+
+@include list-group-item-variant(success, $state-success-bg, $state-success-text);
+@include list-group-item-variant(info, $state-info-bg, $state-info-text);
+@include list-group-item-variant(warning, $state-warning-bg, $state-warning-text);
+@include list-group-item-variant(danger, $state-danger-bg, $state-danger-text);
diff --git a/src/styles/bootstrap/_media.scss b/src/styles/bootstrap/_media.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b573052c14affa5bdca02ac9e3e7a4168768925b
--- /dev/null
+++ b/src/styles/bootstrap/_media.scss
@@ -0,0 +1,8 @@
+.media {
+  display: flex;
+  align-items: flex-start;
+}
+
+.media-body {
+  flex: 1;
+}
diff --git a/src/styles/bootstrap/_mixins.scss b/src/styles/bootstrap/_mixins.scss
new file mode 100755
index 0000000000000000000000000000000000000000..da4738297cf3714ca5f9dc98ff6cc1d6ecdcb98e
--- /dev/null
+++ b/src/styles/bootstrap/_mixins.scss
@@ -0,0 +1,57 @@
+// Toggles
+//
+// Used in conjunction with global variables to enable certain theme features.
+
+@mixin box-shadow($shadow...) {
+  @if $enable-shadows {
+    box-shadow: $shadow;
+  }
+}
+
+@mixin transition($transition...) {
+  @if $enable-transitions {
+    @if length($transition) == 0 {
+      transition: $transition-base;
+    } @else {
+      transition: $transition;
+    }
+  }
+}
+
+// Utilities
+@import "mixins/breakpoints";
+@import "mixins/hover";
+@import "mixins/image";
+@import "mixins/badge";
+@import "mixins/resize";
+@import "mixins/screen-reader";
+@import "mixins/size";
+@import "mixins/reset-text";
+@import "mixins/text-emphasis";
+@import "mixins/text-hide";
+@import "mixins/text-truncate";
+@import "mixins/transforms";
+@import "mixins/visibility";
+
+// // Components
+@import "mixins/alert";
+@import "mixins/buttons";
+@import "mixins/cards";
+@import "mixins/pagination";
+@import "mixins/lists";
+@import "mixins/list-group";
+@import "mixins/nav-divider";
+@import "mixins/forms";
+@import "mixins/table-row";
+
+// // Skins
+@import "mixins/background-variant";
+@import "mixins/border-radius";
+@import "mixins/gradients";
+
+// // Layout
+@import "mixins/clearfix";
+// @import "mixins/navbar-align";
+@import "mixins/grid-framework";
+@import "mixins/grid";
+@import "mixins/float";
diff --git a/src/styles/bootstrap/_modal.scss b/src/styles/bootstrap/_modal.scss
new file mode 100755
index 0000000000000000000000000000000000000000..9d2a86776da60bd0c5ed98723b8941901d10ec19
--- /dev/null
+++ b/src/styles/bootstrap/_modal.scss
@@ -0,0 +1,142 @@
+// .modal-open      - body class for killing the scroll
+// .modal           - container to scroll within
+// .modal-dialog    - positioning shell for the actual modal
+// .modal-content   - actual modal w/ bg and corners and stuff
+
+
+// Kill the scroll on the body
+.modal-open {
+  overflow: hidden;
+}
+
+// Container that the modal scrolls within
+.modal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-modal;
+  display: none;
+  overflow: hidden;
+  // Prevent Chrome on Windows from adding a focus outline. For details, see
+  // https://github.com/twbs/bootstrap/pull/10951.
+  outline: 0;
+  // We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
+  // gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
+  // See also https://github.com/twbs/bootstrap/issues/17695
+
+  // When fading in the modal, animate it to slide down
+  &.fade .modal-dialog {
+    @include transition($modal-transition);
+    transform: translate(0, -25%);
+  }
+  &.show .modal-dialog { transform: translate(0, 0); }
+}
+.modal-open .modal {
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+// Shell div to position the modal with bottom padding
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: $modal-dialog-margin;
+}
+
+// Actual modal
+.modal-content {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  background-color: $modal-content-bg;
+  background-clip: padding-box;
+  border: $modal-content-border-width solid $modal-content-border-color;
+  @include border-radius($border-radius-lg);
+  @include box-shadow($modal-content-xs-box-shadow);
+  // Remove focus outline from opened modal
+  outline: 0;
+}
+
+// Modal background
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-modal-backdrop;
+  background-color: $modal-backdrop-bg;
+
+  // Fade for backdrop
+  &.fade { opacity: 0; }
+  &.show { opacity: $modal-backdrop-opacity; }
+}
+
+// Modal header
+// Top section of the modal w/ title and dismiss
+.modal-header {
+  display: flex;
+  align-items: center; // vertically center it
+  justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
+  padding: $modal-header-padding;
+  border-bottom: $modal-header-border-width solid $modal-header-border-color;
+}
+
+// Title text within header
+.modal-title {
+  margin-bottom: 0;
+  line-height: $modal-title-line-height;
+}
+
+// Modal body
+// Where all modal content resides (sibling of .modal-header and .modal-footer)
+.modal-body {
+  position: relative;
+  // Enable `flex-grow: 1` so that the body take up as much space as possible
+  // when should there be a fixed height on `.modal-dialog`.
+  flex: 1 1 auto;
+  padding: $modal-inner-padding;
+}
+
+// Footer (for actions)
+.modal-footer {
+  display: flex;
+  align-items: center; // vertically center
+  justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
+  padding: $modal-inner-padding;
+  border-top: $modal-footer-border-width solid $modal-footer-border-color;
+
+  // Easily place margin between footer elements
+  > :not(:first-child) { margin-left: .25rem; }
+  > :not(:last-child) { margin-right: .25rem; }
+}
+
+// Measure scrollbar width for padding body during modal show/hide
+.modal-scrollbar-measure {
+  position: absolute;
+  top: -9999px;
+  width: 50px;
+  height: 50px;
+  overflow: scroll;
+}
+
+// Scale up the modal
+@include media-breakpoint-up(sm) {
+  // Automatically set modal's width for larger viewports
+  .modal-dialog {
+    max-width: $modal-md;
+    margin: $modal-dialog-sm-up-margin-y auto;
+  }
+
+  .modal-content {
+    @include box-shadow($modal-content-sm-up-box-shadow);
+  }
+
+  .modal-sm { max-width: $modal-sm; }
+}
+
+@include media-breakpoint-up(lg) {
+  .modal-lg { max-width: $modal-lg; }
+}
diff --git a/src/styles/bootstrap/_nav.scss b/src/styles/bootstrap/_nav.scss
new file mode 100755
index 0000000000000000000000000000000000000000..eb316bb27b33ca1531cca5b11984a399e1477654
--- /dev/null
+++ b/src/styles/bootstrap/_nav.scss
@@ -0,0 +1,119 @@
+// Base class
+//
+// Kickstart any navigation component with a set of style resets. Works with
+// `<nav>`s or `<ul>`s.
+
+.nav {
+  display: flex;
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+
+.nav-link {
+  display: block;
+  padding: $nav-link-padding;
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+
+  // Disabled state lightens text and removes hover/tab effects
+  &.disabled {
+    color: $nav-disabled-link-color;
+    cursor: $cursor-disabled;
+  }
+}
+
+
+//
+// Tabs
+//
+
+.nav-tabs {
+  border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
+
+  .nav-item {
+    margin-bottom: -$nav-tabs-border-width;
+  }
+
+  .nav-link {
+    border: $nav-tabs-border-width solid transparent;
+    @include border-top-radius($nav-tabs-border-radius);
+
+    @include hover-focus {
+      border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color;
+    }
+
+    &.disabled {
+      color: $nav-disabled-link-color;
+      background-color: transparent;
+      border-color: transparent;
+    }
+  }
+
+  .nav-link.active,
+  .nav-item.show .nav-link {
+    color: $nav-tabs-active-link-hover-color;
+    background-color: $nav-tabs-active-link-hover-bg;
+    border-color: $nav-tabs-active-link-hover-border-color $nav-tabs-active-link-hover-border-color $nav-tabs-active-link-hover-bg;
+  }
+
+  .dropdown-menu {
+    // Make dropdown border overlap tab border
+    margin-top: -$nav-tabs-border-width;
+    // Remove the top rounded corners here since there is a hard edge above the menu
+    @include border-top-radius(0);
+  }
+}
+
+
+//
+// Pills
+//
+
+.nav-pills {
+  .nav-link {
+    @include border-radius($nav-pills-border-radius);
+  }
+
+  .nav-link.active,
+  .nav-item.show .nav-link {
+    color: $nav-pills-active-link-color;
+    cursor: default;
+    background-color: $nav-pills-active-link-bg;
+  }
+}
+
+
+//
+// Justified variants
+//
+
+.nav-fill {
+  .nav-item {
+    flex: 1 1 auto;
+    text-align: center;
+  }
+}
+
+.nav-justified {
+  .nav-item {
+    flex: 1 1 100%;
+    text-align: center;
+  }
+}
+
+
+// Tabbable tabs
+//
+// Hide tabbable panes to start, show them when `.active`
+
+.tab-content {
+  > .tab-pane {
+    display: none;
+  }
+  > .active {
+    display: block;
+  }
+}
diff --git a/src/styles/bootstrap/_navbar.scss b/src/styles/bootstrap/_navbar.scss
new file mode 100755
index 0000000000000000000000000000000000000000..80beec8f3e1caba8547150591a24747a8db9566c
--- /dev/null
+++ b/src/styles/bootstrap/_navbar.scss
@@ -0,0 +1,268 @@
+// Contents
+//
+// Navbar
+// Navbar brand
+// Navbar nav
+// Navbar text
+// Navbar divider
+// Responsive navbar
+// Navbar position
+// Navbar themes
+
+
+// Navbar
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  padding: $navbar-padding-y $navbar-padding-x;
+}
+
+
+// Navbar brand
+//
+// Used for brand, project, or site names.
+
+.navbar-brand {
+  display: inline-block;
+  padding-top: .25rem;
+  padding-bottom: .25rem;
+  margin-right: $navbar-padding-x;
+  font-size: $font-size-lg;
+  line-height: inherit;
+  white-space: nowrap;
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+}
+
+
+// Navbar nav
+//
+// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
+
+.navbar-nav {
+  display: flex;
+  flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+
+  .nav-link {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+
+
+// Navbar text
+//
+//
+
+.navbar-text {
+  display: inline-block;
+  padding-top:    .425rem;
+  padding-bottom: .425rem;
+}
+
+
+// Responsive navbar
+//
+// Custom styles for responsive collapsing and toggling of navbar contents.
+// Powered by the collapse Bootstrap JavaScript plugin.
+
+// Button for toggling the navbar when in its collapsed state
+.navbar-toggler {
+  align-self: flex-start; // Prevent toggler from growing to full width when it's the only visible navbar child
+  padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
+  font-size: $navbar-toggler-font-size;
+  line-height: 1;
+  background: transparent; // remove default button style
+  border: $border-width solid transparent; // remove default button style
+  @include border-radius($navbar-toggler-border-radius);
+
+  @include hover-focus {
+    text-decoration: none;
+  }
+}
+
+// Keep as a separate element so folks can easily override it with another icon
+// or image file as needed.
+.navbar-toggler-icon {
+  display: inline-block;
+  width: 1.5em;
+  height: 1.5em;
+  vertical-align: middle;
+  content: "";
+  background: no-repeat center center;
+  background-size: 100% 100%;
+}
+
+// Use `position` on the toggler to prevent it from being auto placed as a flex
+// item and allow easy placement.
+.navbar-toggler-left {
+  position: absolute;
+  left: $navbar-padding-x;
+}
+.navbar-toggler-right {
+  position: absolute;
+  right: $navbar-padding-x;
+}
+
+// Generate series of `.navbar-toggleable-*` responsive classes for configuring
+// where your navbar collapses.
+.navbar-toggleable {
+  @each $breakpoint in map-keys($grid-breakpoints) {
+    $next: breakpoint-next($breakpoint, $grid-breakpoints);
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    &#{$infix} {
+      @include media-breakpoint-down($breakpoint) {
+        .navbar-nav {
+          .dropdown-menu {
+            position: static;
+            float: none;
+          }
+        }
+
+        > .container {
+          padding-right: 0;
+          padding-left: 0;
+        }
+      }
+
+      @include media-breakpoint-up($next) {
+        flex-direction: row;
+        flex-wrap: nowrap;
+        align-items: center;
+
+        .navbar-nav {
+          flex-direction: row;
+
+          .nav-link {
+            padding-right: .5rem;
+            padding-left: .5rem;
+          }
+        }
+
+        // For nesting containers, have to redeclare for alignment purposes
+        > .container {
+          display: flex;
+          flex-wrap: nowrap;
+          align-items: center;
+        }
+
+        // scss-lint:disable ImportantRule
+        .navbar-collapse {
+          display: flex !important;
+          width: 100%;
+        }
+        // scss-lint:enable ImportantRule
+
+        .navbar-toggler {
+          display: none;
+        }
+      }
+    }
+  }
+}
+
+
+// Navbar themes
+//
+// Styles for switching between navbars with light or dark background.
+
+// Dark links against a light background
+.navbar-light {
+  .navbar-brand,
+  .navbar-toggler {
+    color: $navbar-light-active-color;
+
+    @include hover-focus {
+      color: $navbar-light-active-color;
+    }
+  }
+
+  .navbar-nav {
+    .nav-link {
+      color: $navbar-light-color;
+
+      @include hover-focus {
+        color: $navbar-light-hover-color;
+      }
+
+      &.disabled {
+        color: $navbar-light-disabled-color;
+      }
+    }
+
+    .open > .nav-link,
+    .active > .nav-link,
+    .nav-link.open,
+    .nav-link.active {
+      color: $navbar-light-active-color;
+    }
+  }
+
+  .navbar-toggler {
+    border-color: $navbar-light-toggler-border;
+  }
+
+  .navbar-toggler-icon {
+    background-image: $navbar-light-toggler-bg;
+  }
+
+  .navbar-text {
+    color: $navbar-light-color;
+  }
+}
+
+// White links against a dark background
+.navbar-inverse {
+  .navbar-brand,
+  .navbar-toggler {
+    color: $navbar-inverse-active-color;
+
+    @include hover-focus {
+      color: $navbar-inverse-active-color;
+    }
+  }
+
+  .navbar-nav {
+    .nav-link {
+      color: $navbar-inverse-color;
+
+      @include hover-focus {
+        color: $navbar-inverse-hover-color;
+      }
+
+      &.disabled {
+        color: $navbar-inverse-disabled-color;
+      }
+    }
+
+    .open > .nav-link,
+    .active > .nav-link,
+    .nav-link.open,
+    .nav-link.active {
+      color: $navbar-inverse-active-color;
+    }
+  }
+
+  .navbar-toggler {
+    border-color: $navbar-inverse-toggler-border;
+  }
+
+  .navbar-toggler-icon {
+    background-image: $navbar-inverse-toggler-bg;
+  }
+
+  .navbar-text {
+    color: $navbar-inverse-color;
+  }
+}
diff --git a/src/styles/bootstrap/_normalize.scss b/src/styles/bootstrap/_normalize.scss
new file mode 100755
index 0000000000000000000000000000000000000000..6bafd53f63a5a3ceb166648590131ad332fe48f4
--- /dev/null
+++ b/src/styles/bootstrap/_normalize.scss
@@ -0,0 +1,461 @@
+/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
+
+//
+// 1. Change the default font family in all browsers (opinionated).
+// 2. Correct the line height in all browsers.
+// 3. Prevent adjustments of font size after orientation changes in
+//    IE on Windows Phone and in iOS.
+//
+
+// Document
+// ==========================================================================
+
+html {
+  font-family: sans-serif; // 1
+  line-height: 1.15; // 2
+  -ms-text-size-adjust: 100%; // 3
+  -webkit-text-size-adjust: 100%; // 3
+}
+
+// Sections
+// ==========================================================================
+
+//
+// Remove the margin in all browsers (opinionated).
+//
+
+body {
+  margin: 0;
+}
+
+//
+// Add the correct display in IE 9-.
+//
+
+article,
+aside,
+footer,
+header,
+nav,
+section {
+  display: block;
+}
+
+//
+// Correct the font size and margin on `h1` elements within `section` and
+// `article` contexts in Chrome, Firefox, and Safari.
+//
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+// Grouping content
+// ==========================================================================
+
+//
+// Add the correct display in IE 9-.
+// 1. Add the correct display in IE.
+//
+
+figcaption,
+figure,
+main { // 1
+  display: block;
+}
+
+//
+// Add the correct margin in IE 8.
+//
+
+figure {
+  margin: 1em 40px;
+}
+
+//
+// 1. Add the correct box sizing in Firefox.
+// 2. Show the overflow in Edge and IE.
+//
+
+hr {
+  box-sizing: content-box; // 1
+  height: 0; // 1
+  overflow: visible; // 2
+}
+
+//
+// 1. Correct the inheritance and scaling of font size in all browsers.
+// 2. Correct the odd `em` font sizing in all browsers.
+//
+
+pre {
+  font-family: monospace, monospace; // 1
+  font-size: 1em; // 2
+}
+
+// Text-level semantics
+// ==========================================================================
+
+//
+// 1. Remove the gray background on active links in IE 10.
+// 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
+//
+
+a {
+  background-color: transparent; // 1
+  -webkit-text-decoration-skip: objects; // 2
+}
+
+//
+// Remove the outline on focused links when they are also active or hovered
+// in all browsers (opinionated).
+//
+
+a:active,
+a:hover {
+  outline-width: 0;
+}
+
+//
+// 1. Remove the bottom border in Firefox 39-.
+// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+//
+
+abbr[title] {
+  border-bottom: none; // 1
+  text-decoration: underline; // 2
+  text-decoration: underline dotted; // 2
+}
+
+//
+// Prevent the duplicate application of `bolder` by the next rule in Safari 6.
+//
+
+b,
+strong {
+  font-weight: inherit;
+}
+
+//
+// Add the correct font weight in Chrome, Edge, and Safari.
+//
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+//
+// 1. Correct the inheritance and scaling of font size in all browsers.
+// 2. Correct the odd `em` font sizing in all browsers.
+//
+
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; // 1
+  font-size: 1em; // 2
+}
+
+//
+// Add the correct font style in Android 4.3-.
+//
+
+dfn {
+  font-style: italic;
+}
+
+//
+// Add the correct background and color in IE 9-.
+//
+
+mark {
+  background-color: #ff0;
+  color: #000;
+}
+
+//
+// Add the correct font size in all browsers.
+//
+
+small {
+  font-size: 80%;
+}
+
+//
+// Prevent `sub` and `sup` elements from affecting the line height in
+// all browsers.
+//
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+// Embedded content
+// ==========================================================================
+
+//
+// Add the correct display in IE 9-.
+//
+
+audio,
+video {
+  display: inline-block;
+}
+
+//
+// Add the correct display in iOS 4-7.
+//
+
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+
+//
+// Remove the border on images inside links in IE 10-.
+//
+
+img {
+  border-style: none;
+}
+
+//
+// Hide the overflow in IE.
+//
+
+svg:not(:root) {
+  overflow: hidden;
+}
+
+// Forms
+// ==========================================================================
+
+//
+// 1. Change the font styles in all browsers (opinionated).
+// 2. Remove the margin in Firefox and Safari.
+//
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: sans-serif; // 1
+  font-size: 100%; // 1
+  line-height: 1.15; // 1
+  margin: 0; // 2
+}
+
+//
+// Show the overflow in IE.
+// 1. Show the overflow in Edge.
+//
+
+button,
+input { // 1
+  overflow: visible;
+}
+
+//
+// Remove the inheritance of text transform in Edge, Firefox, and IE.
+// 1. Remove the inheritance of text transform in Firefox.
+//
+
+button,
+select { // 1
+  text-transform: none;
+}
+
+//
+// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+//    controls in Android 4.
+// 2. Correct the inability to style clickable types in iOS and Safari.
+//
+
+button,
+html [type="button"], // 1
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button; // 2
+}
+
+//
+// Remove the inner border and padding in Firefox.
+//
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+//
+// Restore the focus styles unset by the previous rule.
+//
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+//
+// Change the border, margin, and padding in all browsers (opinionated).
+//
+
+fieldset {
+  border: 1px solid #c0c0c0;
+  margin: 0 2px;
+  padding: 0.35em 0.625em 0.75em;
+}
+
+//
+// 1. Correct the text wrapping in Edge and IE.
+// 2. Correct the color inheritance from `fieldset` elements in IE.
+// 3. Remove the padding so developers are not caught out when they zero out
+//    `fieldset` elements in all browsers.
+//
+
+legend {
+  box-sizing: border-box; // 1
+  color: inherit; // 2
+  display: table; // 1
+  max-width: 100%; // 1
+  padding: 0; // 3
+  white-space: normal; // 1
+}
+
+//
+// 1. Add the correct display in IE 9-.
+// 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
+//
+
+progress {
+  display: inline-block; // 1
+  vertical-align: baseline; // 2
+}
+
+//
+// Remove the default vertical scrollbar in IE.
+//
+
+textarea {
+  overflow: auto;
+}
+
+//
+// 1. Add the correct box sizing in IE 10-.
+// 2. Remove the padding in IE 10-.
+//
+
+[type="checkbox"],
+[type="radio"] {
+  box-sizing: border-box; // 1
+  padding: 0; // 2
+}
+
+//
+// Correct the cursor style of increment and decrement buttons in Chrome.
+//
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+//
+// 1. Correct the odd appearance in Chrome and Safari.
+// 2. Correct the outline style in Safari.
+//
+
+[type="search"] {
+  -webkit-appearance: textfield; // 1
+  outline-offset: -2px; // 2
+}
+
+//
+// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+//
+
+[type="search"]::-webkit-search-cancel-button,
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+//
+// 1. Correct the inability to style clickable types in iOS and Safari.
+// 2. Change font properties to `inherit` in Safari.
+//
+
+::-webkit-file-upload-button {
+  -webkit-appearance: button; // 1
+  font: inherit; // 2
+}
+
+// Interactive
+// ==========================================================================
+
+//
+// Add the correct display in IE 9-.
+// 1. Add the correct display in Edge, IE, and Firefox.
+//
+
+details, // 1
+menu {
+  display: block;
+}
+
+//
+// Add the correct display in all browsers.
+//
+
+summary {
+  display: list-item;
+}
+
+// Scripting
+// ==========================================================================
+
+//
+// Add the correct display in IE 9-.
+//
+
+canvas {
+  display: inline-block;
+}
+
+//
+// Add the correct display in IE.
+//
+
+template {
+  display: none;
+}
+
+// Hidden
+// ==========================================================================
+
+//
+// Add the correct display in IE 10-.
+//
+
+[hidden] {
+  display: none;
+}
diff --git a/src/styles/bootstrap/_pagination.scss b/src/styles/bootstrap/_pagination.scss
new file mode 100755
index 0000000000000000000000000000000000000000..24aa028d1f901a12378339b4f3334e8a44027b6a
--- /dev/null
+++ b/src/styles/bootstrap/_pagination.scss
@@ -0,0 +1,67 @@
+.pagination {
+  display: flex;
+  // 1-2: Disable browser default list styles
+  padding-left: 0; // 1
+  list-style: none; // 2
+  @include border-radius();
+}
+
+.page-item {
+  &:first-child {
+    .page-link {
+      margin-left: 0;
+      @include border-left-radius($border-radius);
+    }
+  }
+  &:last-child {
+    .page-link {
+      @include border-right-radius($border-radius);
+    }
+  }
+
+  &.active .page-link {
+    z-index: 2;
+    color: $pagination-active-color;
+    background-color: $pagination-active-bg;
+    border-color: $pagination-active-border;
+  }
+
+  &.disabled .page-link {
+    color: $pagination-disabled-color;
+    pointer-events: none;
+    cursor: $cursor-disabled; // While `pointer-events: none` removes the cursor in modern browsers, we provide a disabled cursor as a fallback.
+    background-color: $pagination-disabled-bg;
+    border-color: $pagination-disabled-border;
+  }
+}
+
+.page-link {
+  position: relative;
+  display: block;
+  padding: $pagination-padding-y $pagination-padding-x;
+  margin-left: -1px;
+  line-height: $pagination-line-height;
+  color: $pagination-color;
+  background-color: $pagination-bg;
+  border: $pagination-border-width solid $pagination-border-color;
+
+  @include hover-focus {
+    color: $pagination-hover-color;
+    text-decoration: none;
+    background-color: $pagination-hover-bg;
+    border-color: $pagination-hover-border;
+  }
+}
+
+
+//
+// Sizing
+//
+
+.pagination-lg {
+  @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $border-radius-lg);
+}
+
+.pagination-sm {
+  @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $border-radius-sm);
+}
diff --git a/src/styles/bootstrap/_popover.scss b/src/styles/bootstrap/_popover.scss
new file mode 100755
index 0000000000000000000000000000000000000000..1b6363405ce939ff4b07e672542a0b15a82c3d28
--- /dev/null
+++ b/src/styles/bootstrap/_popover.scss
@@ -0,0 +1,171 @@
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: $zindex-popover;
+  display: block;
+  max-width: $popover-max-width;
+  padding: $popover-inner-padding;
+  // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+  // So reset our font and text properties to avoid inheriting weird values.
+  @include reset-text();
+  font-size: $font-size-sm;
+  // Allow breaking very long words so they don't overflow the popover's bounds
+  word-wrap: break-word;
+  background-color: $popover-bg;
+  background-clip: padding-box;
+  border: $popover-border-width solid $popover-border-color;
+  @include border-radius($border-radius-lg);
+  @include box-shadow($popover-box-shadow);
+
+
+  // Popover directions
+
+  &.popover-top,
+  &.bs-tether-element-attached-bottom {
+    margin-top: -$popover-arrow-width;
+
+    &::before,
+    &::after {
+      left: 50%;
+      border-bottom-width: 0;
+    }
+
+    &::before {
+      bottom: -$popover-arrow-outer-width;
+      margin-left: -$popover-arrow-outer-width;
+      border-top-color: $popover-arrow-outer-color;
+    }
+
+    &::after {
+      bottom: -($popover-arrow-outer-width - 1);
+      margin-left: -$popover-arrow-width;
+      border-top-color: $popover-arrow-color;
+    }
+  }
+
+  &.popover-right,
+  &.bs-tether-element-attached-left {
+    margin-left: $popover-arrow-width;
+
+    &::before,
+    &::after {
+      top: 50%;
+      border-left-width: 0;
+    }
+
+    &::before {
+      left: -$popover-arrow-outer-width;
+      margin-top: -$popover-arrow-outer-width;
+      border-right-color: $popover-arrow-outer-color;
+    }
+
+    &::after {
+      left: -($popover-arrow-outer-width - 1);
+      margin-top: -($popover-arrow-outer-width - 1);
+      border-right-color: $popover-arrow-color;
+    }
+  }
+
+  &.popover-bottom,
+  &.bs-tether-element-attached-top {
+    margin-top: $popover-arrow-width;
+
+    &::before,
+    &::after {
+      left: 50%;
+      border-top-width: 0;
+    }
+
+    &::before {
+      top: -$popover-arrow-outer-width;
+      margin-left: -$popover-arrow-outer-width;
+      border-bottom-color: $popover-arrow-outer-color;
+    }
+
+    &::after {
+      top: -($popover-arrow-outer-width - 1);
+      margin-left: -$popover-arrow-width;
+      border-bottom-color: $popover-title-bg;
+    }
+
+    // This will remove the popover-title's border just below the arrow
+    .popover-title::before {
+      position: absolute;
+      top: 0;
+      left: 50%;
+      display: block;
+      width: 20px;
+      margin-left: -10px;
+      content: "";
+      border-bottom: 1px solid $popover-title-bg;
+    }
+  }
+
+  &.popover-left,
+  &.bs-tether-element-attached-right {
+    margin-left: -$popover-arrow-width;
+
+    &::before,
+    &::after {
+      top: 50%;
+      border-right-width: 0;
+    }
+
+    &::before {
+      right: -$popover-arrow-outer-width;
+      margin-top: -$popover-arrow-outer-width;
+      border-left-color: $popover-arrow-outer-color;
+    }
+
+    &::after {
+      right: -($popover-arrow-outer-width - 1);
+      margin-top: -($popover-arrow-outer-width - 1);
+      border-left-color: $popover-arrow-color;
+    }
+  }
+}
+
+
+// Offset the popover to account for the popover arrow
+.popover-title {
+  padding: $popover-title-padding-y $popover-title-padding-x;
+  margin-bottom: 0; // Reset the default from Reboot
+  font-size: $font-size-base;
+  background-color: $popover-title-bg;
+  border-bottom: $popover-border-width solid darken($popover-title-bg, 5%);
+  $offset-border-width: calc(#{$border-radius-lg} - #{$popover-border-width});
+  @include border-top-radius($offset-border-width);
+
+  &:empty {
+    display: none;
+  }
+}
+
+.popover-content {
+  padding: $popover-content-padding-y $popover-content-padding-x;
+}
+
+
+// Arrows
+//
+// .popover-arrow is outer, .popover-arrow::after is inner
+
+.popover::before,
+.popover::after {
+  position: absolute;
+  display: block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+
+.popover::before {
+  content: "";
+  border-width: $popover-arrow-outer-width;
+}
+.popover::after {
+  content: "";
+  border-width: $popover-arrow-width;
+}
diff --git a/src/styles/bootstrap/_print.scss b/src/styles/bootstrap/_print.scss
new file mode 100755
index 0000000000000000000000000000000000000000..e20219a38bd7c8924fb7013d278b608afe6b3a5e
--- /dev/null
+++ b/src/styles/bootstrap/_print.scss
@@ -0,0 +1,119 @@
+// scss-lint:disable QualifyingElement
+
+// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
+
+// ==========================================================================
+// Print styles.
+// Inlined to avoid the additional HTTP request:
+// http://www.phpied.com/delay-loading-your-print-css/
+// ==========================================================================
+
+@if $enable-print-styles {
+  @media print {
+    *,
+    *::before,
+    *::after,
+    p::first-letter,
+    div::first-letter,
+    blockquote::first-letter,
+    li::first-letter,
+    p::first-line,
+    div::first-line,
+    blockquote::first-line,
+    li::first-line {
+      // Bootstrap specific; comment out `color` and `background`
+      //color: #000 !important; // Black prints faster:
+                                //   http://www.sanbeiji.com/archives/953
+      text-shadow: none !important;
+      //background: transparent !important;
+      box-shadow: none !important;
+    }
+
+    a,
+    a:visited {
+      text-decoration: underline;
+    }
+
+    // Bootstrap specific; comment the following selector out
+    //a[href]::after {
+    //  content: " (" attr(href) ")";
+    //}
+
+    abbr[title]::after {
+      content: " (" attr(title) ")";
+    }
+
+    // Bootstrap specific; comment the following selector out
+    //
+    // Don't show links that are fragment identifiers,
+    // or use the `javascript:` pseudo protocol
+    //
+
+    //a[href^="#"]::after,
+    //a[href^="javascript:"]::after {
+    // content: "";
+    //}
+
+    pre {
+      white-space: pre-wrap !important;
+    }
+    pre,
+    blockquote {
+      border: $border-width solid #999;   // Bootstrap custom code; using `$border-width` instead of 1px
+      page-break-inside: avoid;
+    }
+
+    //
+    // Printing Tables:
+    // http://css-discuss.incutio.com/wiki/Printing_Tables
+    //
+
+    thead {
+      display: table-header-group;
+    }
+
+    tr,
+    img {
+      page-break-inside: avoid;
+    }
+
+    p,
+    h2,
+    h3 {
+      orphans: 3;
+      widows: 3;
+    }
+
+    h2,
+    h3 {
+      page-break-after: avoid;
+    }
+
+    // Bootstrap specific changes start
+
+    // Bootstrap components
+    .navbar {
+      display: none;
+    }
+    .badge {
+      border: $border-width solid #000;
+    }
+
+    .table {
+      border-collapse: collapse !important;
+
+      td,
+      th {
+        background-color: #fff !important;
+      }
+    }
+    .table-bordered {
+      th,
+      td {
+        border: 1px solid #ddd !important;
+      }
+    }
+
+    // Bootstrap specific changes end
+  }
+}
diff --git a/src/styles/bootstrap/_progress.scss b/src/styles/bootstrap/_progress.scss
new file mode 100755
index 0000000000000000000000000000000000000000..02e4c3bd2db8b1b95e5b6d71873b57f187ef7c0f
--- /dev/null
+++ b/src/styles/bootstrap/_progress.scss
@@ -0,0 +1,32 @@
+// Progress animations
+@keyframes progress-bar-stripes {
+  from { background-position: $progress-height 0; }
+  to { background-position: 0 0; }
+}
+
+// Basic progress bar
+.progress {
+  display: flex;
+  overflow: hidden; // force rounded corners by cropping it
+  font-size: $progress-font-size;
+  line-height: $progress-height;
+  text-align: center;
+  background-color: $progress-bg;
+  @include border-radius($progress-border-radius);
+}
+.progress-bar {
+  height: $progress-height;
+  color: $progress-bar-color;
+  background-color: $progress-bar-bg;
+}
+
+// Striped
+.progress-bar-striped {
+  @include gradient-striped();
+  background-size: $progress-height $progress-height;
+}
+
+// Animated
+.progress-bar-animated {
+  animation: progress-bar-stripes $progress-bar-animation-timing;
+}
diff --git a/src/styles/bootstrap/_reboot.scss b/src/styles/bootstrap/_reboot.scss
new file mode 100755
index 0000000000000000000000000000000000000000..557829f25ce0d493486cabf271e12f0bdfec3607
--- /dev/null
+++ b/src/styles/bootstrap/_reboot.scss
@@ -0,0 +1,389 @@
+// scss-lint:disable QualifyingElement, DuplicateProperty
+
+// Reboot
+//
+// Global resets to common HTML elements and more for easier usage by Bootstrap.
+// Adds additional rules on top of Normalize.css, including several overrides.
+
+
+// Reset the box-sizing
+//
+// Change from `box-sizing: content-box` to `border-box` so that when you add
+// `padding` or `border`s to an element, the overall declared `width` does not
+// change. For example, `width: 100px;` will always be `100px` despite the
+// `border: 10px solid red;` and `padding: 20px;`.
+//
+// Heads up! This reset may cause conflicts with some third-party widgets. For
+// recommendations on resolving such conflicts, see
+// https://getbootstrap.com/getting-started/#third-box-sizing.
+//
+// Credit: https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/
+
+html {
+  box-sizing: border-box;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: inherit;
+}
+
+
+// Make viewport responsive
+//
+// @viewport is needed because IE 10+ doesn't honor <meta name="viewport"> in
+// some cases. See https://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/.
+// Eventually @viewport will replace <meta name="viewport">.
+//
+// However, `device-width` is broken on IE 10 on Windows (Phone) 8,
+// (see https://timkadlec.com/2013/01/windows-phone-8-and-device-width/ and https://github.com/twbs/bootstrap/issues/10497)
+// and the fix for that involves a snippet of JavaScript to sniff the user agent
+// and apply some conditional CSS.
+//
+// See https://getbootstrap.com/getting-started/#support-ie10-width for the relevant hack.
+//
+// Wrap `@viewport` with `@at-root` for when folks do a nested import (e.g.,
+// `.class-name { @import "bootstrap"; }`).
+@at-root {
+  @-ms-viewport { width: device-width; }
+}
+
+
+//
+// Reset HTML, body, and more
+//
+
+html {
+  // We assume no initial pixel `font-size` for accessibility reasons. This
+  // allows web visitors to customize their browser default font-size, making
+  // your project more inclusive and accessible to everyone.
+
+  // As a side-effect of setting the @viewport above,
+  // IE11 & Edge make the scrollbar overlap the content and automatically hide itself when not in use.
+  // Unfortunately, the auto-showing of the scrollbar is sometimes too sensitive,
+  // thus making it hard to click on stuff near the right edge of the page.
+  // So we add this style to force IE11 & Edge to use a "normal", non-overlapping, non-auto-hiding scrollbar.
+  // See https://github.com/twbs/bootstrap/issues/18543
+  // and https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7165383/
+  -ms-overflow-style: scrollbar;
+
+  // Changes the default tap highlight to be completely transparent in iOS.
+  -webkit-tap-highlight-color: rgba(0,0,0,0);
+}
+
+body {
+  font-family: $font-family-base;
+  font-size: $font-size-base;
+  font-weight: $font-weight-base;
+  line-height: $line-height-base;
+  // Go easy on the eyes and use something other than `#000` for text
+  color: $body-color;
+  // By default, `<body>` has no `background-color` so we set one as a best practice.
+  background-color: $body-bg;
+}
+
+// Suppress the focus outline on elements that cannot be accessed via keyboard.
+// This prevents an unwanted focus outline from appearing around elements that
+// might still respond to pointer events.
+//
+// Credit: https://github.com/suitcss/base
+[tabindex="-1"]:focus {
+  outline: none !important;
+}
+
+
+//
+// Typography
+//
+
+// Remove top margins from headings
+//
+// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
+// margin for easier control within type scales as it avoids margin collapsing.
+h1, h2, h3, h4, h5, h6 {
+  margin-top: 0;
+  margin-bottom: .5rem;
+}
+
+// Reset margins on paragraphs
+//
+// Similarly, the top margin on `<p>`s get reset. However, we also reset the
+// bottom margin to use `rem` units instead of `em`.
+p {
+  margin-top: 0;
+  margin-bottom: 1rem;
+}
+
+// Abbreviations
+abbr[title],
+// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257
+abbr[data-original-title] {
+  cursor: help;
+}
+
+address {
+  margin-bottom: 1rem;
+  font-style: normal;
+  line-height: inherit;
+}
+
+ol,
+ul,
+dl {
+  margin-top: 0;
+  margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+  margin-bottom: 0;
+}
+
+dt {
+  font-weight: $dt-font-weight;
+}
+
+dd {
+  margin-bottom: .5rem;
+  margin-left: 0; // Undo browser default
+}
+
+blockquote {
+  margin: 0 0 1rem;
+}
+
+
+//
+// Links
+//
+
+a {
+  color: $link-color;
+  text-decoration: $link-decoration;
+
+  @include hover-focus {
+    color: $link-hover-color;
+    text-decoration: $link-hover-decoration;
+  }
+}
+
+// And undo these styles for placeholder links/named anchors (without href)
+// which have not been made explicitly keyboard-focusable (without tabindex).
+// It would be more straightforward to just use a[href] in previous block, but that
+// causes specificity issues in many other styles that are too complex to fix.
+// See https://github.com/twbs/bootstrap/issues/19402
+
+a:not([href]):not([tabindex]) {
+  color: inherit;
+  text-decoration: none;
+
+  @include hover-focus {
+    color: inherit;
+    text-decoration: none;
+  }
+
+  &:focus {
+    outline: 0;
+  }
+}
+
+
+//
+// Code
+//
+
+pre {
+  // Remove browser default top margin
+  margin-top: 0;
+  // Reset browser default of `1em` to use `rem`s
+  margin-bottom: 1rem;
+  // Normalize v4 removed this property, causing `<pre>` content to break out of wrapping code snippets
+  overflow: auto;
+}
+
+
+//
+// Figures
+//
+
+figure {
+  // Normalize adds `margin` to `figure`s as browsers apply it inconsistently.
+  // We reset that to create a better flow in-page.
+  margin: 0 0 1rem;
+}
+
+
+//
+// Images
+//
+
+img {
+  // By default, `<img>`s are `inline-block`. This assumes that, and vertically
+  // centers them. This won't apply should you reset them to `block` level.
+  vertical-align: middle;
+  // Note: `<img>`s are deliberately not made responsive by default.
+  // For the rationale behind this, see the comments on the `.img-fluid` class.
+}
+
+
+// iOS "clickable elements" fix for role="button"
+//
+// Fixes "clickability" issue (and more generally, the firing of events such as focus as well)
+// for traditionally non-focusable elements with role="button"
+// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
+
+[role="button"] {
+  cursor: pointer;
+}
+
+
+// Avoid 300ms click delay on touch devices that support the `touch-action` CSS property.
+//
+// In particular, unlike most other browsers, IE11+Edge on Windows 10 on touch devices and IE Mobile 10-11
+// DON'T remove the click delay when `<meta name="viewport" content="width=device-width">` is present.
+// However, they DO support removing the click delay via `touch-action: manipulation`.
+// See:
+// * https://v4-alpha.getbootstrap.com/content/reboot/#click-delay-optimization-for-touch
+// * http://caniuse.com/#feat=css-touch-action
+// * https://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay
+
+a,
+area,
+button,
+[role="button"],
+input,
+label,
+select,
+summary,
+textarea {
+  touch-action: manipulation;
+}
+
+
+//
+// Tables
+//
+
+table {
+  // No longer part of Normalize since v4
+  border-collapse: collapse;
+  // Reset for nesting within parents with `background-color`.
+  background-color: $table-bg;
+}
+
+caption {
+  padding-top: $table-cell-padding;
+  padding-bottom: $table-cell-padding;
+  color: $text-muted;
+  text-align: left;
+  caption-side: bottom;
+}
+
+th {
+  // Centered by default, but left-align-ed to match the `td`s below.
+  text-align: left;
+}
+
+
+//
+// Forms
+//
+
+label {
+  // Allow labels to use `margin` for spacing.
+  display: inline-block;
+  margin-bottom: .5rem;
+}
+
+// Work around a Firefox/IE bug where the transparent `button` background
+// results in a loss of the default `button` focus styles.
+//
+// Credit: https://github.com/suitcss/base/
+button:focus {
+  outline: 1px dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+}
+
+input,
+button,
+select,
+textarea {
+  // Normalize includes `font: inherit;`, so `font-family`. `font-size`, etc are
+  // properly inherited. However, `line-height` isn't inherited there.
+  line-height: inherit;
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+  // Apply a disabled cursor for radios and checkboxes.
+  //
+  // Note: Neither radios nor checkboxes can be readonly.
+  &:disabled {
+    cursor: $cursor-disabled;
+  }
+}
+
+
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+  // Remove the default appearance of temporal inputs to avoid a Mobile Safari
+  // bug where setting a custom line-height prevents text from being vertically
+  // centered within the input.
+  // See https://bugs.webkit.org/show_bug.cgi?id=139848
+  // and https://github.com/twbs/bootstrap/issues/11266
+  -webkit-appearance: listbox;
+}
+
+textarea {
+  // Textareas should really only resize vertically so they don't break their (horizontal) containers.
+  resize: vertical;
+}
+
+fieldset {
+  // Browsers set a default `min-width: min-content;` on fieldsets,
+  // unlike e.g. `<div>`s, which have `min-width: 0;` by default.
+  // So we reset that to ensure fieldsets behave more like a standard block element.
+  // See https://github.com/twbs/bootstrap/issues/12359
+  // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
+  min-width: 0;
+  // Reset the default outline behavior of fieldsets so they don't affect page layout.
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+
+legend {
+  // Reset the entire legend element to match the `fieldset`
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: .5rem;
+  font-size: 1.5rem;
+  line-height: inherit;
+}
+
+input[type="search"] {
+  // This overrides the extra rounded corners on search inputs in iOS so that our
+  // `.form-control` class can properly style them. Note that this cannot simply
+  // be added to `.form-control` as it's not specific enough. For details, see
+  // https://github.com/twbs/bootstrap/issues/11586.
+  -webkit-appearance: none;
+}
+
+// todo: needed?
+output {
+  display: inline-block;
+//  font-size: $font-size-base;
+//  line-height: $line-height;
+//  color: $input-color;
+}
+
+// Always hide an element with the `hidden` HTML attribute (from PureCSS).
+[hidden] {
+  display: none !important;
+}
diff --git a/src/styles/bootstrap/_responsive-embed.scss b/src/styles/bootstrap/_responsive-embed.scss
new file mode 100755
index 0000000000000000000000000000000000000000..d3362b6fdb19fff6c8d5998ceefdb41c7538eb5c
--- /dev/null
+++ b/src/styles/bootstrap/_responsive-embed.scss
@@ -0,0 +1,52 @@
+// Credit: Nicolas Gallagher and SUIT CSS.
+
+.embed-responsive {
+  position: relative;
+  display: block;
+  width: 100%;
+  padding: 0;
+  overflow: hidden;
+
+  &::before {
+    display: block;
+    content: "";
+  }
+
+  .embed-responsive-item,
+  iframe,
+  embed,
+  object,
+  video {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    border: 0;
+  }
+}
+
+.embed-responsive-21by9 {
+  &::before {
+    padding-top: percentage(9 / 21);
+  }
+}
+
+.embed-responsive-16by9 {
+  &::before {
+    padding-top: percentage(9 / 16);
+  }
+}
+
+.embed-responsive-4by3 {
+  &::before {
+    padding-top: percentage(3 / 4);
+  }
+}
+
+.embed-responsive-1by1 {
+  &::before {
+    padding-top: percentage(1 / 1);
+  }
+}
diff --git a/src/styles/bootstrap/_tables.scss b/src/styles/bootstrap/_tables.scss
new file mode 100755
index 0000000000000000000000000000000000000000..47be2c508474848260aea0850105898310db31e0
--- /dev/null
+++ b/src/styles/bootstrap/_tables.scss
@@ -0,0 +1,153 @@
+//
+// Basic Bootstrap table
+//
+
+.table {
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: $spacer;
+
+  th,
+  td {
+    padding: $table-cell-padding;
+    vertical-align: top;
+    border-top: $table-border-width solid $table-border-color;
+  }
+
+  thead th {
+    vertical-align: bottom;
+    border-bottom: (2 * $table-border-width) solid $table-border-color;
+  }
+
+  tbody + tbody {
+    border-top: (2 * $table-border-width) solid $table-border-color;
+  }
+
+  .table {
+    background-color: $body-bg;
+  }
+}
+
+
+//
+// Condensed table w/ half padding
+//
+
+.table-sm {
+  th,
+  td {
+    padding: $table-sm-cell-padding;
+  }
+}
+
+
+// Bordered version
+//
+// Add borders all around the table and between all the columns.
+
+.table-bordered {
+  border: $table-border-width solid $table-border-color;
+
+  th,
+  td {
+    border: $table-border-width solid $table-border-color;
+  }
+
+  thead {
+    th,
+    td {
+      border-bottom-width: (2 * $table-border-width);
+    }
+  }
+}
+
+
+// Zebra-striping
+//
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+
+.table-striped {
+  tbody tr:nth-of-type(odd) {
+    background-color: $table-bg-accent;
+  }
+}
+
+
+// Hover effect
+//
+// Placed here since it has to come after the potential zebra striping
+
+.table-hover {
+  tbody tr {
+    @include hover {
+      background-color: $table-bg-hover;
+    }
+  }
+}
+
+
+// Table backgrounds
+//
+// Exact selectors below required to override `.table-striped` and prevent
+// inheritance to nested tables.
+
+// Generate the contextual variants
+@include table-row-variant(active, $table-bg-active);
+@include table-row-variant(success, $state-success-bg);
+@include table-row-variant(info, $state-info-bg);
+@include table-row-variant(warning, $state-warning-bg);
+@include table-row-variant(danger, $state-danger-bg);
+
+
+// Inverse styles
+//
+// Same table markup, but inverted color scheme: dark background and light text.
+
+.thead-inverse {
+  th {
+    color: $table-inverse-color;
+    background-color: $table-inverse-bg;
+  }
+}
+
+.thead-default {
+  th {
+    color: $table-head-color;
+    background-color: $table-head-bg;
+  }
+}
+
+.table-inverse {
+  color: $table-inverse-color;
+  background-color: $table-inverse-bg;
+
+  th,
+  td,
+  thead th {
+    border-color: $body-bg;
+  }
+
+  &.table-bordered {
+    border: 0;
+  }
+}
+
+
+
+// Responsive tables
+//
+// Add `.table-responsive` to `.table`s and we'll make them mobile friendly by
+// enabling horizontal scrolling. Only applies <768px. Everything above that
+// will display normally.
+
+.table-responsive {
+  display: block;
+  width: 100%;
+  overflow-x: auto;
+  -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
+
+  // Prevent double border on horizontal scroll due to use of `display: block;`
+  &.table-bordered {
+    border: 0;
+  }
+}
diff --git a/src/styles/bootstrap/_tooltip.scss b/src/styles/bootstrap/_tooltip.scss
new file mode 100755
index 0000000000000000000000000000000000000000..24e198d464e2907d1817ddd0975b046652cbb92b
--- /dev/null
+++ b/src/styles/bootstrap/_tooltip.scss
@@ -0,0 +1,90 @@
+// Base class
+.tooltip {
+  position: absolute;
+  z-index: $zindex-tooltip;
+  display: block;
+  // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+  // So reset our font and text properties to avoid inheriting weird values.
+  @include reset-text();
+  font-size: $font-size-sm;
+  // Allow breaking very long words so they don't overflow the tooltip's bounds
+  word-wrap: break-word;
+  opacity: 0;
+
+  &.show { opacity: $tooltip-opacity; }
+
+  &.tooltip-top,
+  &.bs-tether-element-attached-bottom {
+    padding: $tooltip-arrow-width 0;
+    margin-top: -$tooltip-margin;
+
+    .tooltip-inner::before {
+      bottom: 0;
+      left: 50%;
+      margin-left: -$tooltip-arrow-width;
+      content: "";
+      border-width: $tooltip-arrow-width $tooltip-arrow-width 0;
+      border-top-color: $tooltip-arrow-color;
+    }
+  }
+  &.tooltip-right,
+  &.bs-tether-element-attached-left {
+    padding: 0 $tooltip-arrow-width;
+    margin-left: $tooltip-margin;
+
+    .tooltip-inner::before {
+      top: 50%;
+      left: 0;
+      margin-top: -$tooltip-arrow-width;
+      content: "";
+      border-width: $tooltip-arrow-width $tooltip-arrow-width $tooltip-arrow-width 0;
+      border-right-color: $tooltip-arrow-color;
+    }
+  }
+  &.tooltip-bottom,
+  &.bs-tether-element-attached-top {
+    padding: $tooltip-arrow-width 0;
+    margin-top: $tooltip-margin;
+
+    .tooltip-inner::before {
+      top: 0;
+      left: 50%;
+      margin-left: -$tooltip-arrow-width;
+      content: "";
+      border-width: 0 $tooltip-arrow-width $tooltip-arrow-width;
+      border-bottom-color: $tooltip-arrow-color;
+    }
+  }
+  &.tooltip-left,
+  &.bs-tether-element-attached-right {
+    padding: 0 $tooltip-arrow-width;
+    margin-left: -$tooltip-margin;
+
+    .tooltip-inner::before {
+      top: 50%;
+      right: 0;
+      margin-top: -$tooltip-arrow-width;
+      content: "";
+      border-width: $tooltip-arrow-width 0 $tooltip-arrow-width $tooltip-arrow-width;
+      border-left-color: $tooltip-arrow-color;
+    }
+  }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+  max-width: $tooltip-max-width;
+  padding: $tooltip-padding-y $tooltip-padding-x;
+  color: $tooltip-color;
+  text-align: center;
+  background-color: $tooltip-bg;
+  @include border-radius($border-radius);
+
+  &::before {
+    position: absolute;
+    width: 0;
+    height: 0;
+    border-color: transparent;
+    border-style: solid;
+  }
+}
diff --git a/src/styles/bootstrap/_transitions.scss b/src/styles/bootstrap/_transitions.scss
new file mode 100755
index 0000000000000000000000000000000000000000..86c04a5f8b20c4fc580bee9e60c227ae61846394
--- /dev/null
+++ b/src/styles/bootstrap/_transitions.scss
@@ -0,0 +1,34 @@
+.fade {
+  opacity: 0;
+  @include transition($transition-fade);
+
+  &.show {
+    opacity: 1;
+  }
+}
+
+.collapse {
+  display: none;
+  &.show {
+    display: block;
+  }
+}
+
+tr {
+  &.collapse.show {
+    display: table-row;
+  }
+}
+
+tbody {
+  &.collapse.show {
+    display: table-row-group;
+  }
+}
+
+.collapsing {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  @include transition($transition-collapse);
+}
diff --git a/src/styles/bootstrap/_type.scss b/src/styles/bootstrap/_type.scss
new file mode 100755
index 0000000000000000000000000000000000000000..13a64b06f38a14123822fb4fae7cc128680dd8f7
--- /dev/null
+++ b/src/styles/bootstrap/_type.scss
@@ -0,0 +1,143 @@
+//
+// Headings
+//
+
+h1, h2, h3, h4, h5, h6,
+.h1, .h2, .h3, .h4, .h5, .h6 {
+  margin-bottom: $headings-margin-bottom;
+  font-family: $headings-font-family;
+  font-weight: $headings-font-weight;
+  line-height: $headings-line-height;
+  color: $headings-color;
+}
+
+h1, .h1 { font-size: $font-size-h1; }
+h2, .h2 { font-size: $font-size-h2; }
+h3, .h3 { font-size: $font-size-h3; }
+h4, .h4 { font-size: $font-size-h4; }
+h5, .h5 { font-size: $font-size-h5; }
+h6, .h6 { font-size: $font-size-h6; }
+
+.lead {
+  font-size: $lead-font-size;
+  font-weight: $lead-font-weight;
+}
+
+// Type display classes
+.display-1 {
+  font-size: $display1-size;
+  font-weight: $display1-weight;
+  line-height: $display-line-height;
+}
+.display-2 {
+  font-size: $display2-size;
+  font-weight: $display2-weight;
+  line-height: $display-line-height;
+}
+.display-3 {
+  font-size: $display3-size;
+  font-weight: $display3-weight;
+  line-height: $display-line-height;
+}
+.display-4 {
+  font-size: $display4-size;
+  font-weight: $display4-weight;
+  line-height: $display-line-height;
+}
+
+
+//
+// Horizontal rules
+//
+
+hr {
+  margin-top: $spacer-y;
+  margin-bottom: $spacer-y;
+  border: 0;
+  border-top: $hr-border-width solid $hr-border-color;
+}
+
+
+//
+// Emphasis
+//
+
+small,
+.small {
+  font-size: $small-font-size;
+  font-weight: $font-weight-normal;
+}
+
+mark,
+.mark {
+  padding: $mark-padding;
+  background-color: $mark-bg;
+}
+
+
+//
+// Lists
+//
+
+.list-unstyled {
+  @include list-unstyled;
+}
+
+// Inline turns list items into inline-block
+.list-inline {
+  @include list-unstyled;
+}
+.list-inline-item {
+  display: inline-block;
+
+  &:not(:last-child) {
+    margin-right: $list-inline-padding;
+  }
+}
+
+
+//
+// Misc
+//
+
+// Builds on `abbr`
+.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+
+// Blockquotes
+.blockquote {
+  padding: ($spacer / 2) $spacer;
+  margin-bottom: $spacer;
+  font-size: $blockquote-font-size;
+  border-left: $blockquote-border-width solid $blockquote-border-color;
+}
+
+.blockquote-footer {
+  display: block;
+  font-size: 80%; // back to default font-size
+  color: $blockquote-small-color;
+
+  &::before {
+    content: "\2014 \00A0"; // em dash, nbsp
+  }
+}
+
+// Opposite alignment of blockquote
+.blockquote-reverse {
+  padding-right: $spacer;
+  padding-left: 0;
+  text-align: right;
+  border-right: $blockquote-border-width solid $blockquote-border-color;
+  border-left: 0;
+}
+
+.blockquote-reverse .blockquote-footer {
+  &::before {
+    content: "";
+  }
+  &::after {
+    content: "\00A0 \2014"; // nbsp, em dash
+  }
+}
diff --git a/src/styles/bootstrap/_utilities.scss b/src/styles/bootstrap/_utilities.scss
new file mode 100755
index 0000000000000000000000000000000000000000..7d08ff2f8ca0c158ff8abaeaa99ab927c6453c72
--- /dev/null
+++ b/src/styles/bootstrap/_utilities.scss
@@ -0,0 +1,13 @@
+@import "utilities/align";
+@import "utilities/background";
+@import "utilities/borders";
+@import "utilities/clearfix";
+@import "utilities/display";
+@import "utilities/flex";
+@import "utilities/float";
+@import "utilities/position";
+@import "utilities/screenreaders";
+@import "utilities/sizing";
+@import "utilities/spacing";
+@import "utilities/text";
+@import "utilities/visibility";
diff --git a/src/styles/bootstrap/_variables.scss b/src/styles/bootstrap/_variables.scss
new file mode 100755
index 0000000000000000000000000000000000000000..386f287a0535dfab680a130bae037437f1306fca
--- /dev/null
+++ b/src/styles/bootstrap/_variables.scss
@@ -0,0 +1,962 @@
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,400,300,600);
+// Variables
+//
+// Copy settings from this file into the provided `_custom.scss` to override
+// the Bootstrap defaults without modifying key, versioned files.
+
+
+// Table of Contents
+//
+// Colors
+// Options
+// Spacing
+// Body
+// Links
+// Grid breakpoints
+// Grid containers
+// Grid columns
+// Fonts
+// Components
+// Tables
+// Buttons
+// Forms
+// Dropdowns
+// Z-index master list
+// Navbar
+// Navs
+// Pagination
+// Jumbotron
+// Form states and alerts
+// Cards
+// Tooltips
+// Popovers
+// Badges
+// Modals
+// Alerts
+// Progress bars
+// List group
+// Image thumbnails
+// Figures
+// Breadcrumbs
+// Carousel
+// Close
+// Code
+
+@mixin _assert-ascending($map, $map-name) {
+  $prev-key: null;
+  $prev-num: null;
+  @each $key, $num in $map {
+    @if $prev-num == null {
+      // Do nothing
+    } @else if not comparable($prev-num, $num) {
+      @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
+    } @else if $prev-num >= $num {
+      @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
+    }
+    $prev-key: $key;
+    $prev-num: $num;
+  }
+}
+
+// Replace `$search` with `$replace` in `$string`
+// @author Hugo Giraudel
+// @param {String} $string - Initial string
+// @param {String} $search - Substring to replace
+// @param {String} $replace ('') - New value
+// @return {String} - Updated string
+@function str-replace($string, $search, $replace: "") {
+  $index: str-index($string, $search);
+
+  @if $index {
+    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
+  }
+
+  @return $string;
+}
+
+@mixin _assert-starts-at-zero($map) {
+  $values: map-values($map);
+  $first-value: nth($values, 1);
+  @if $first-value != 0 {
+    @warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.";
+  }
+}
+
+
+// General variable structure
+//
+// Variable format should follow the `$component-modifier-state-property` order.
+
+
+// Colors
+//
+// Grayscale and brand colors for use across Bootstrap.
+
+// Start with assigning color names to specific hex values.
+$white:  #fff !default;
+$black:  #000 !default;
+$red:    #d9534f !default;
+$orange: #f0ad4e !default;
+$yellow: #ffd500 !default;
+$green:  #5cb85c !default;
+$blue:   #0275d8 !default;
+$teal:   #5bc0de !default;
+$pink:   #ff5b77 !default;
+$purple: #613d7c !default;
+
+// Create grayscale
+$gray-dark:                 #292b2c !default;
+$gray:                      #464a4c !default;
+$gray-light:                #636c72 !default;
+$gray-lighter:              #eceeef !default;
+$gray-lightest:             #f7f7f9 !default;
+
+// Reassign color vars to semantic color scheme
+$brand-primary:             $blue !default;
+$brand-success:             $green !default;
+$brand-info:                $teal !default;
+$brand-warning:             $orange !default;
+$brand-danger:              $red !default;
+$brand-inverse:             $gray-dark !default;
+
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+$enable-rounded:            true !default;
+$enable-shadows:            false !default;
+$enable-gradients:          false !default;
+$enable-transitions:        true !default;
+$enable-hover-media-query:  false !default;
+$enable-grid-classes:       true !default;
+$enable-print-styles:       true !default;
+
+
+// Spacing
+//
+// Control the default styling of most Bootstrap elements by modifying these
+// variables. Mostly focused on spacing.
+// You can add more entries to the $spacers map, should you need more variation.
+
+$spacer:   1rem !default;
+$spacer-x: $spacer !default;
+$spacer-y: $spacer !default;
+$spacers: (
+  0: (
+    x: 0,
+    y: 0
+  ),
+  1: (
+    x: ($spacer-x * .25),
+    y: ($spacer-y * .25)
+  ),
+  2: (
+    x: ($spacer-x * .5),
+    y: ($spacer-y * .5)
+  ),
+  3: (
+    x: $spacer-x,
+    y: $spacer-y
+  ),
+  4: (
+    x: ($spacer-x * 1.5),
+    y: ($spacer-y * 1.5)
+  ),
+  5: (
+    x: ($spacer-x * 3),
+    y: ($spacer-y * 3)
+  )
+) !default;
+$border-width: 1px !default;
+
+// This variable affects the `.h-*` and `.w-*` classes.
+$sizes: (
+  25: 25%,
+  50: 50%,
+  75: 75%,
+  100: 100%
+) !default;
+
+// Body
+//
+// Settings for the `<body>` element.
+
+$body-bg:       $white !default;
+$body-color:    $gray-dark !default;
+$inverse-bg:    $gray-dark !default;
+$inverse-color: $gray-lighter !default;
+
+
+// Links
+//
+// Style anchor elements.
+
+$link-color:            $brand-primary !default;
+$link-decoration:       none !default;
+$link-hover-color:      darken($link-color, 15%) !default;
+$link-hover-decoration: underline !default;
+
+
+// Grid breakpoints
+//
+// Define the minimum dimensions at which your layout will change,
+// adapting to different screen sizes, for use in media queries.
+
+$grid-breakpoints: (
+  xs: 0,
+  sm: 576px,
+  md: 768px,
+  lg: 992px,
+  xl: 1200px
+) !default;
+@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
+@include _assert-starts-at-zero($grid-breakpoints);
+
+
+// Grid containers
+//
+// Define the maximum width of `.container` for different screen sizes.
+
+$container-max-widths: (
+  sm: 540px,
+  md: 720px,
+  lg: 960px,
+  xl: 1140px
+) !default;
+@include _assert-ascending($container-max-widths, "$container-max-widths");
+
+
+// Grid columns
+//
+// Set the number of columns and specify the width of the gutters.
+
+$grid-columns:               12 !default;
+$grid-gutter-width-base:     15px !default;
+$grid-gutter-widths: (
+  xs: $grid-gutter-width-base,
+  sm: $grid-gutter-width-base,
+  md: $grid-gutter-width-base,
+  lg: $grid-gutter-width-base,
+  xl: $grid-gutter-width-base
+) !default;
+
+// Fonts
+//
+// Font, line-height, and color for body text, headings, and more.
+
+$font-family-sans-serif: "Open Sans", -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default;
+$font-family-serif:      "Open Sans", Georgia, "Times New Roman", Times, serif !default;
+$font-family-monospace:  Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
+$font-family-base:       $font-family-sans-serif !default;
+
+$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`
+$font-size-lg:   1.25rem !default;
+$font-size-sm:   .875rem !default;
+$font-size-xs:   .75rem !default;
+
+$font-weight-normal: normal !default;
+$font-weight-bold: bold !default;
+
+$font-weight-base: $font-weight-normal !default;
+$line-height-base: 1.5 !default;
+
+$font-size-h1: 2.5rem !default;
+$font-size-h2: 2rem !default;
+$font-size-h3: 1.75rem !default;
+$font-size-h4: 1.5rem !default;
+$font-size-h5: 1.25rem !default;
+$font-size-h6: 1rem !default;
+
+$headings-margin-bottom: ($spacer / 2) !default;
+$headings-font-family:   inherit !default;
+$headings-font-weight:   500 !default;
+$headings-line-height:   1.1 !default;
+$headings-color:         inherit !default;
+
+$display1-size: 6rem !default;
+$display2-size: 5.5rem !default;
+$display3-size: 4.5rem !default;
+$display4-size: 3.5rem !default;
+
+$display1-weight:     300 !default;
+$display2-weight:     300 !default;
+$display3-weight:     300 !default;
+$display4-weight:     300 !default;
+$display-line-height: $headings-line-height !default;
+
+$lead-font-size:   1.25rem !default;
+$lead-font-weight: 300 !default;
+
+$small-font-size: 80% !default;
+
+$text-muted: $gray-light !default;
+
+$abbr-border-color: $gray-light !default;
+
+$blockquote-small-color:  $gray-light !default;
+$blockquote-font-size:    ($font-size-base * 1.25) !default;
+$blockquote-border-color: $gray-lighter !default;
+$blockquote-border-width: .25rem !default;
+
+$hr-border-color: rgba($black,.1) !default;
+$hr-border-width: $border-width !default;
+
+$mark-padding: .2em !default;
+
+$dt-font-weight: $font-weight-bold !default;
+
+$kbd-box-shadow:         inset 0 -.1rem 0 rgba($black,.25) !default;
+$nested-kbd-font-weight: $font-weight-bold !default;
+
+$list-inline-padding: 5px !default;
+
+
+// Components
+//
+// Define common padding and border radius sizes and more.
+
+$line-height-lg:         (4 / 3) !default;
+$line-height-sm:         1.5 !default;
+
+$border-radius:          .25rem !default;
+$border-radius-lg:       .3rem !default;
+$border-radius-sm:       .2rem !default;
+
+$component-active-color: $white !default;
+$component-active-bg:    $brand-primary !default;
+
+$caret-width:            .3em !default;
+
+$transition-base:        all .2s ease-in-out !default;
+$transition-fade:        opacity .15s linear !default;
+$transition-collapse:    height .35s ease !default;
+
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+$table-cell-padding:            .75rem !default;
+$table-sm-cell-padding:         .3rem !default;
+
+$table-bg:                      transparent !default;
+
+$table-inverse-bg:              $gray-dark !default;
+$table-inverse-color:           $body-bg !default;
+
+$table-bg-accent:               rgba($black,.05) !default;
+$table-bg-hover:                rgba($black,.075) !default;
+$table-bg-active:               $table-bg-hover !default;
+
+$table-head-bg:                 $gray-lighter !default;
+$table-head-color:              $gray !default;
+
+$table-border-width:            $border-width !default;
+$table-border-color:            $gray-lighter !default;
+
+
+// Buttons
+//
+// For each of Bootstrap's buttons, define text, background and border color.
+
+$btn-padding-x:                  1rem !default;
+$btn-padding-y:                  .5rem !default;
+$btn-line-height:                1.25 !default;
+$btn-font-weight:                $font-weight-normal !default;
+$btn-box-shadow:                 inset 0 1px 0 rgba($white,.15), 0 1px 1px rgba($black,.075) !default;
+$btn-focus-box-shadow:           0 0 0 2px rgba($brand-primary, .25) !default;
+$btn-active-box-shadow:          inset 0 3px 5px rgba($black,.125) !default;
+
+$btn-primary-color:              $white !default;
+$btn-primary-bg:                 $brand-primary !default;
+$btn-primary-border:             $btn-primary-bg !default;
+
+$btn-secondary-color:            $gray-dark !default;
+$btn-secondary-bg:               $white !default;
+$btn-secondary-border:           #ccc !default;
+
+$btn-info-color:                 $white !default;
+$btn-info-bg:                    $brand-info !default;
+$btn-info-border:                $btn-info-bg !default;
+
+$btn-success-color:              $white !default;
+$btn-success-bg:                 $brand-success !default;
+$btn-success-border:             $btn-success-bg !default;
+
+$btn-warning-color:              $white !default;
+$btn-warning-bg:                 $brand-warning !default;
+$btn-warning-border:             $btn-warning-bg !default;
+
+$btn-danger-color:               $white !default;
+$btn-danger-bg:                  $brand-danger !default;
+$btn-danger-border:              $btn-danger-bg !default;
+
+$btn-link-disabled-color:        $gray-light !default;
+
+$btn-padding-x-sm:               .5rem !default;
+$btn-padding-y-sm:               .25rem !default;
+
+$btn-padding-x-lg:               1.5rem !default;
+$btn-padding-y-lg:               .75rem !default;
+
+$btn-block-spacing-y:            .5rem !default;
+$btn-toolbar-margin:             .5rem !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius:              $border-radius !default;
+$btn-border-radius-lg:           $border-radius-lg !default;
+$btn-border-radius-sm:           $border-radius-sm !default;
+
+$btn-transition:                 all .2s ease-in-out !default;
+
+
+// Forms
+
+$input-padding-x:                .75rem !default;
+$input-padding-y:                .5rem !default;
+$input-line-height:              1.25 !default;
+
+$input-bg:                       $white !default;
+$input-bg-disabled:              $gray-lighter !default;
+
+$input-color:                    $gray !default;
+$input-border-color:             rgba($black,.15) !default;
+$input-btn-border-width:         $border-width !default; // For form controls and buttons
+$input-box-shadow:               inset 0 1px 1px rgba($black,.075) !default;
+
+$input-border-radius:            $border-radius !default;
+$input-border-radius-lg:         $border-radius-lg !default;
+$input-border-radius-sm:         $border-radius-sm !default;
+
+$input-bg-focus:                 $input-bg !default;
+$input-border-focus:             lighten($brand-primary, 25%) !default;
+$input-box-shadow-focus:         $input-box-shadow, rgba($input-border-focus, .6) !default;
+$input-color-focus:              $input-color !default;
+
+$input-color-placeholder:        $gray-light !default;
+
+$input-padding-x-sm:             .5rem !default;
+$input-padding-y-sm:             .25rem !default;
+
+$input-padding-x-lg:             1.5rem !default;
+$input-padding-y-lg:             .75rem !default;
+
+$input-height:                   (($font-size-base * $input-line-height) + ($input-padding-y * 2)) !default;
+$input-height-lg:                (($font-size-lg * $line-height-lg) + ($input-padding-y-lg * 2)) !default;
+$input-height-sm:                (($font-size-sm * $line-height-sm) + ($input-padding-y-sm * 2)) !default;
+
+$input-transition:               border-color ease-in-out .15s, box-shadow ease-in-out .15s !default;
+
+$form-text-margin-top:     .25rem !default;
+$form-feedback-margin-top: $form-text-margin-top !default;
+
+$form-check-margin-bottom:  .5rem !default;
+$form-check-input-gutter:   1.25rem !default;
+$form-check-input-margin-y: .25rem !default;
+$form-check-input-margin-x: .25rem !default;
+
+$form-check-inline-margin-x: .75rem !default;
+
+$form-group-margin-bottom:       $spacer-y !default;
+
+$input-group-addon-bg:           $gray-lighter !default;
+$input-group-addon-border-color: $input-border-color !default;
+
+$cursor-disabled:                not-allowed !default;
+
+$custom-control-gutter:   1.5rem !default;
+$custom-control-spacer-x: 1rem !default;
+$custom-control-spacer-y: .25rem !default;
+
+$custom-control-indicator-size:       1rem !default;
+$custom-control-indicator-margin-y:   (($line-height-base * 1rem) - $custom-control-indicator-size) / -2 !default;
+$custom-control-indicator-bg:         #ddd !default;
+$custom-control-indicator-bg-size:    50% 50% !default;
+$custom-control-indicator-box-shadow: inset 0 .25rem .25rem rgba($black,.1) !default;
+
+$custom-control-disabled-cursor:             $cursor-disabled !default;
+$custom-control-disabled-indicator-bg:       $gray-lighter !default;
+$custom-control-disabled-description-color:  $gray-light !default;
+
+$custom-control-checked-indicator-color:      $white !default;
+$custom-control-checked-indicator-bg:         $brand-primary !default;
+$custom-control-checked-indicator-box-shadow: none !default;
+
+$custom-control-focus-indicator-box-shadow: 0 0 0 1px $body-bg, 0 0 0 3px $brand-primary !default;
+
+$custom-control-active-indicator-color:      $white !default;
+$custom-control-active-indicator-bg:         lighten($brand-primary, 35%) !default;
+$custom-control-active-indicator-box-shadow: none !default;
+
+$custom-checkbox-radius: $border-radius !default;
+$custom-checkbox-checked-icon: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-checked-indicator-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$custom-checkbox-indeterminate-bg: $brand-primary !default;
+$custom-checkbox-indeterminate-indicator-color: $custom-control-checked-indicator-color !default;
+$custom-checkbox-indeterminate-icon: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indeterminate-indicator-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-checkbox-indeterminate-box-shadow: none !default;
+
+$custom-radio-radius: 50% !default;
+$custom-radio-checked-icon: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-checked-indicator-color}'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$custom-select-padding-x:          .75rem  !default;
+$custom-select-padding-y:          .375rem !default;
+$custom-select-indicator-padding:   1rem !default; // Extra padding to account for the presence of the background-image based indicator
+$custom-select-line-height:         $input-line-height !default;
+$custom-select-color:               $input-color !default;
+$custom-select-disabled-color:      $gray-light !default;
+$custom-select-bg:            $white !default;
+$custom-select-disabled-bg:   $gray-lighter !default;
+$custom-select-bg-size:       8px 10px !default; // In pixels because image dimensions
+$custom-select-indicator-color: #333 !default;
+$custom-select-indicator:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-select-border-width:  $input-btn-border-width !default;
+$custom-select-border-color:  $input-border-color !default;
+$custom-select-border-radius: $border-radius !default;
+
+$custom-select-focus-border-color: lighten($brand-primary, 25%) !default;
+$custom-select-focus-box-shadow:   inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5) !default;
+
+$custom-select-sm-padding-y:  .2rem !default;
+$custom-select-sm-font-size:  75% !default;
+
+$custom-file-height:           2.5rem !default;
+$custom-file-width:            14rem !default;
+$custom-file-focus-box-shadow: 0 0 0 .075rem $white, 0 0 0 .2rem $brand-primary !default;
+
+$custom-file-padding-x:     .5rem !default;
+$custom-file-padding-y:     1rem !default;
+$custom-file-line-height:   1.5 !default;
+$custom-file-color:         $gray !default;
+$custom-file-bg:            $white !default;
+$custom-file-border-width:  $border-width !default;
+$custom-file-border-color:  $input-border-color !default;
+$custom-file-border-radius: $border-radius !default;
+$custom-file-box-shadow:    inset 0 .2rem .4rem rgba($black,.05) !default;
+$custom-file-button-color:  $custom-file-color !default;
+$custom-file-button-bg:     $gray-lighter !default;
+$custom-file-text: (
+  placeholder: (
+    en: "Choose file..."
+  ),
+  button-label: (
+    en: "Browse"
+  )
+) !default;
+
+
+// Form validation icons
+$form-icon-success-color: $brand-success !default;
+$form-icon-success: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$form-icon-success-color}' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$form-icon-warning-color: $brand-warning !default;
+$form-icon-warning: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$form-icon-warning-color}' d='M4.4 5.324h-.8v-2.46h.8zm0 1.42h-.8V5.89h.8zM3.76.63L.04 7.075c-.115.2.016.425.26.426h7.397c.242 0 .372-.226.258-.426C6.726 4.924 5.47 2.79 4.253.63c-.113-.174-.39-.174-.494 0z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$form-icon-danger-color: $brand-danger !default;
+$form-icon-danger: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$form-icon-danger-color}' viewBox='-2 -2 7 7'%3E%3Cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3E%3Ccircle r='.5'/%3E%3Ccircle cx='3' r='.5'/%3E%3Ccircle cy='3' r='.5'/%3E%3Ccircle cx='3' cy='3' r='.5'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+$dropdown-min-width:             10rem !default;
+$dropdown-padding-y:             .5rem !default;
+$dropdown-margin-top:            .125rem !default;
+$dropdown-bg:                    $white !default;
+$dropdown-border-color:          rgba($black,.15) !default;
+$dropdown-border-width:          $border-width !default;
+$dropdown-divider-bg:            $gray-lighter !default;
+$dropdown-box-shadow:            0 .5rem 1rem rgba($black,.175) !default;
+
+$dropdown-link-color:            $gray-dark !default;
+$dropdown-link-hover-color:      darken($gray-dark, 5%) !default;
+$dropdown-link-hover-bg:         $gray-lightest !default;
+
+$dropdown-link-active-color:     $component-active-color !default;
+$dropdown-link-active-bg:        $component-active-bg !default;
+
+$dropdown-link-disabled-color:   $gray-light !default;
+
+$dropdown-item-padding-x:        1.5rem !default;
+
+$dropdown-header-color:          $gray-light !default;
+
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+$zindex-dropdown-backdrop:  990 !default;
+$zindex-navbar:             1000 !default;
+$zindex-dropdown:           1000 !default;
+$zindex-fixed:              1030 !default;
+$zindex-sticky:             1030 !default;
+$zindex-modal-backdrop:     1040 !default;
+$zindex-modal:              1050 !default;
+$zindex-popover:            1060 !default;
+$zindex-tooltip:            1070 !default;
+
+
+// Navbar
+
+$navbar-border-radius:              $border-radius !default;
+$navbar-padding-x:                  $spacer !default;
+$navbar-padding-y:                  ($spacer / 2) !default;
+
+$navbar-brand-padding-y:            .25rem !default;
+
+$navbar-toggler-padding-x:           .75rem !default;
+$navbar-toggler-padding-y:           .25rem !default;
+$navbar-toggler-font-size:           $font-size-lg !default;
+$navbar-toggler-border-radius:       $btn-border-radius !default;
+
+$navbar-inverse-color:                 rgba($white,.5) !default;
+$navbar-inverse-hover-color:           rgba($white,.75) !default;
+$navbar-inverse-active-color:          rgba($white,1) !default;
+$navbar-inverse-disabled-color:        rgba($white,.25) !default;
+$navbar-inverse-toggler-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-inverse-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-inverse-toggler-border:        rgba($white,.1) !default;
+
+$navbar-light-color:                rgba($black,.5) !default;
+$navbar-light-hover-color:          rgba($black,.7) !default;
+$navbar-light-active-color:         rgba($black,.9) !default;
+$navbar-light-disabled-color:       rgba($black,.3) !default;
+$navbar-light-toggler-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-light-toggler-border:       rgba($black,.1) !default;
+
+// Navs
+
+$nav-item-margin:               .2rem !default;
+$nav-item-inline-spacer:        1rem !default;
+$nav-link-padding:              .5em 1em !default;
+$nav-link-hover-bg:             $gray-lighter !default;
+$nav-disabled-link-color:       $gray-light !default;
+
+$nav-tabs-border-color:                       #ddd !default;
+$nav-tabs-border-width:                       $border-width !default;
+$nav-tabs-border-radius:                      $border-radius !default;
+$nav-tabs-link-hover-border-color:            $gray-lighter !default;
+$nav-tabs-active-link-hover-color:            $gray !default;
+$nav-tabs-active-link-hover-bg:               $body-bg !default;
+$nav-tabs-active-link-hover-border-color:     #ddd !default;
+$nav-tabs-justified-link-border-color:        #ddd !default;
+$nav-tabs-justified-active-link-border-color: $body-bg !default;
+
+$nav-pills-border-radius:     $border-radius !default;
+$nav-pills-active-link-color: $component-active-color !default;
+$nav-pills-active-link-bg:    $component-active-bg !default;
+
+
+// Pagination
+
+$pagination-padding-x:                .75rem !default;
+$pagination-padding-y:                .5rem !default;
+$pagination-padding-x-sm:             .5rem !default;
+$pagination-padding-y-sm:             .25rem !default;
+$pagination-padding-x-lg:             1.5rem !default;
+$pagination-padding-y-lg:             .75rem !default;
+$pagination-line-height:              1.25 !default;
+
+$pagination-color:                     $link-color !default;
+$pagination-bg:                        $white !default;
+$pagination-border-width:              $border-width !default;
+$pagination-border-color:              #ddd !default;
+
+$pagination-hover-color:               $link-hover-color !default;
+$pagination-hover-bg:                  $gray-lighter !default;
+$pagination-hover-border:              #ddd !default;
+
+$pagination-active-color:              $white !default;
+$pagination-active-bg:                 $brand-primary !default;
+$pagination-active-border:             $brand-primary !default;
+
+$pagination-disabled-color:            $gray-light !default;
+$pagination-disabled-bg:               $white !default;
+$pagination-disabled-border:           #ddd !default;
+
+
+// Jumbotron
+
+$jumbotron-padding:              2rem !default;
+$jumbotron-bg:                   $gray-lighter !default;
+
+
+// Form states and alerts
+//
+// Define colors for form feedback states and, by default, alerts.
+
+$state-success-text:             #FFFFFF !default;
+$state-success-bg:               $brand-success !default;
+$state-success-border:           darken($state-success-bg, 5%) !default;
+
+$state-info-text:                #FFFFFF !default;
+$state-info-bg:                  $brand-info !default;
+$state-info-border:              darken($state-info-bg, 7%) !default;
+
+$state-warning-text:             #FFFFFF !default;
+$state-warning-bg:               $brand-warning !default;
+$mark-bg:                        $state-warning-bg !default;
+$state-warning-border:           darken($state-warning-bg, 5%) !default;
+
+$state-danger-text:              #FFFFFF !default;
+$state-danger-bg:                $brand-danger !default;
+$state-danger-border:            darken($state-danger-bg, 5%) !default;
+
+
+// Cards
+
+$card-spacer-x:            1.25rem !default;
+$card-spacer-y:            .75rem !default;
+$card-border-width:        1px !default;
+$card-border-radius:       $border-radius !default;
+$card-border-color:        rgba($black,.125) !default;
+$card-border-radius-inner: calc(#{$card-border-radius} - #{$card-border-width}) !default;
+$card-cap-bg:              $gray-lightest !default;
+$card-bg:                  $white !default;
+
+$card-link-hover-color:    $white !default;
+
+$card-img-overlay-padding: 1.25rem !default;
+
+$card-deck-margin:          ($grid-gutter-width-base / 2) !default;
+
+$card-columns-count:        3 !default;
+$card-columns-gap:          1.25rem !default;
+$card-columns-margin:       $card-spacer-y !default;
+
+
+// Tooltips
+
+$tooltip-max-width:           200px !default;
+$tooltip-color:               $white !default;
+$tooltip-bg:                  $black !default;
+$tooltip-opacity:             .9 !default;
+$tooltip-padding-y:           3px !default;
+$tooltip-padding-x:           8px !default;
+$tooltip-margin:              3px !default;
+
+$tooltip-arrow-width:         5px !default;
+$tooltip-arrow-color:         $tooltip-bg !default;
+
+
+// Popovers
+
+$popover-inner-padding:               1px !default;
+$popover-bg:                          $white !default;
+$popover-max-width:                   276px !default;
+$popover-border-width:                $border-width !default;
+$popover-border-color:                rgba($black,.2) !default;
+$popover-box-shadow:                  0 5px 10px rgba($black,.2) !default;
+
+$popover-title-bg:                    darken($popover-bg, 3%) !default;
+$popover-title-padding-x:             14px !default;
+$popover-title-padding-y:             8px !default;
+
+$popover-content-padding-x:           14px !default;
+$popover-content-padding-y:           9px !default;
+
+$popover-arrow-width:                 10px !default;
+$popover-arrow-color:                 $popover-bg !default;
+
+$popover-arrow-outer-width:           ($popover-arrow-width + 1px) !default;
+$popover-arrow-outer-color:           fade-in($popover-border-color, .05) !default;
+
+
+// Badges
+
+$badge-default-bg:            $gray-light !default;
+$badge-primary-bg:            $brand-primary !default;
+$badge-success-bg:            $brand-success !default;
+$badge-info-bg:               $brand-info !default;
+$badge-warning-bg:            $brand-warning !default;
+$badge-danger-bg:             $brand-danger !default;
+
+$badge-color:                 $white !default;
+$badge-link-hover-color:      $white !default;
+$badge-font-size:             75% !default;
+$badge-font-weight:           $font-weight-bold !default;
+$badge-padding-x:             .4em !default;
+$badge-padding-y:             .25em !default;
+
+$badge-pill-padding-x:        .6em !default;
+// Use a higher than normal value to ensure completely rounded edges when
+// customizing padding or font-size on labels.
+$badge-pill-border-radius:    10rem !default;
+
+
+// Modals
+
+// Padding applied to the modal body
+$modal-inner-padding:         15px !default;
+
+$modal-dialog-margin:         10px !default;
+$modal-dialog-sm-up-margin-y: 30px !default;
+
+$modal-title-line-height:     $line-height-base !default;
+
+$modal-content-bg:               $white !default;
+$modal-content-border-color:     rgba($black,.2) !default;
+$modal-content-border-width:     $border-width !default;
+$modal-content-xs-box-shadow:    0 3px 9px rgba($black,.5) !default;
+$modal-content-sm-up-box-shadow: 0 5px 15px rgba($black,.5) !default;
+
+$modal-backdrop-bg:           $black !default;
+$modal-backdrop-opacity:      .5 !default;
+$modal-header-border-color:   $gray-lighter !default;
+$modal-footer-border-color:   $modal-header-border-color !default;
+$modal-header-border-width:   $modal-content-border-width !default;
+$modal-footer-border-width:   $modal-header-border-width !default;
+$modal-header-padding:        15px !default;
+
+$modal-lg:                    800px !default;
+$modal-md:                    500px !default;
+$modal-sm:                    300px !default;
+
+$modal-transition:            transform .3s ease-out !default;
+
+
+// Alerts
+//
+// Define alert colors, border radius, and padding.
+
+$alert-padding-x:             1.25rem !default;
+$alert-padding-y:             .75rem !default;
+$alert-margin-bottom:         $spacer-y !default;
+$alert-border-radius:         $border-radius !default;
+$alert-link-font-weight:      $font-weight-bold !default;
+$alert-border-width:          $border-width !default;
+
+$alert-success-bg:            $state-success-bg !default;
+$alert-success-text:          $state-success-text !default;
+$alert-success-border:        $state-success-border !default;
+
+$alert-info-bg:               $state-info-bg !default;
+$alert-info-text:             $state-info-text !default;
+$alert-info-border:           $state-info-border !default;
+
+$alert-warning-bg:            $state-warning-bg !default;
+$alert-warning-text:          $state-warning-text !default;
+$alert-warning-border:        $state-warning-border !default;
+
+$alert-danger-bg:             $state-danger-bg !default;
+$alert-danger-text:           $state-danger-text !default;
+$alert-danger-border:         $state-danger-border !default;
+
+
+// Progress bars
+
+$progress-height:               1rem !default;
+$progress-font-size:            .75rem !default;
+$progress-bg:                   $gray-lighter !default;
+$progress-border-radius:        $border-radius !default;
+$progress-box-shadow:           inset 0 .1rem .1rem rgba($black,.1) !default;
+$progress-bar-color:            $white !default;
+$progress-bar-bg:               $brand-primary !default;
+$progress-bar-animation-timing: 1s linear infinite !default;
+
+// List group
+
+$list-group-color:               $body-color !default;
+$list-group-bg:                  $white !default;
+$list-group-border-color:        rgba($black,.125) !default;
+$list-group-border-width:        $border-width !default;
+$list-group-border-radius:       $border-radius !default;
+
+$list-group-item-padding-x:      1.25rem !default;
+$list-group-item-padding-y:      .75rem !default;
+
+$list-group-hover-bg:            $gray-lightest !default;
+$list-group-active-color:        $component-active-color !default;
+$list-group-active-bg:           $component-active-bg !default;
+$list-group-active-border:       $list-group-active-bg !default;
+$list-group-active-text-color:   lighten($list-group-active-bg, 50%) !default;
+
+$list-group-disabled-color:      $gray-light !default;
+$list-group-disabled-bg:         $list-group-bg !default;
+$list-group-disabled-text-color: $list-group-disabled-color !default;
+
+$list-group-link-color:          $gray !default;
+$list-group-link-heading-color:  $gray-dark !default;
+$list-group-link-hover-color:    $list-group-link-color !default;
+
+$list-group-link-active-color:   $list-group-color !default;
+$list-group-link-active-bg:      $gray-lighter !default;
+
+
+// Image thumbnails
+
+$thumbnail-padding:           .25rem !default;
+$thumbnail-bg:                $body-bg !default;
+$thumbnail-border-width:      $border-width !default;
+$thumbnail-border-color:      #ddd !default;
+$thumbnail-border-radius:     $border-radius !default;
+$thumbnail-box-shadow:        0 1px 2px rgba($black,.075) !default;
+$thumbnail-transition:        all .2s ease-in-out !default;
+
+
+// Figures
+
+$figure-caption-font-size: 90% !default;
+$figure-caption-color:     $gray-light !default;
+
+
+// Breadcrumbs
+
+$breadcrumb-padding-y:          .75rem !default;
+$breadcrumb-padding-x:          1rem !default;
+$breadcrumb-item-padding:       .5rem !default;
+
+$breadcrumb-bg:                 $gray-lighter !default;
+$breadcrumb-divider-color:      $gray-light !default;
+$breadcrumb-active-color:       $gray-light !default;
+$breadcrumb-divider:            "/" !default;
+
+
+// Carousel
+
+$carousel-control-color:                      $white !default;
+$carousel-control-width:                      15% !default;
+$carousel-control-opacity:                    .5 !default;
+
+$carousel-indicator-width:                    30px !default;
+$carousel-indicator-height:                   3px !default;
+$carousel-indicator-spacer:                   3px !default;
+$carousel-indicator-active-bg:                $white !default;
+
+$carousel-caption-width:                      70% !default;
+$carousel-caption-color:                      $white !default;
+
+$carousel-control-icon-width:                 20px !default;
+
+$carousel-control-prev-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M4 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$carousel-control-next-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M1.5 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$carousel-transition:           transform .6s ease-in-out !default;
+
+
+// Close
+
+$close-font-size:             $font-size-base * 1.5 !default;
+$close-font-weight:           $font-weight-bold !default;
+$close-color:                 $black !default;
+$close-text-shadow:           0 1px 0 $white !default;
+
+
+// Code
+
+$code-font-size:              90% !default;
+$code-padding-x:              .4rem !default;
+$code-padding-y:              .2rem !default;
+$code-color:                  #bd4147 !default;
+$code-bg:                     $gray-lightest !default;
+
+$kbd-color:                   $white !default;
+$kbd-bg:                      $gray-dark !default;
+
+$pre-bg:                      $gray-lightest !default;
+$pre-color:                   $gray-dark !default;
+$pre-border-color:            #ccc !default;
+$pre-scrollable-max-height:   340px !default;
diff --git a/src/styles/bootstrap/bootstrap-grid.scss b/src/styles/bootstrap/bootstrap-grid.scss
new file mode 100755
index 0000000000000000000000000000000000000000..182b9626b60d4224ba190800e53eb1fe8d12bea9
--- /dev/null
+++ b/src/styles/bootstrap/bootstrap-grid.scss
@@ -0,0 +1,43 @@
+// Bootstrap Grid only
+//
+// Includes relevant variables and mixins for the flexbox grid
+// system, as well as the generated predefined classes (e.g., `.col-sm-4`).
+
+//
+// Box sizing, responsive, and more
+//
+
+@at-root {
+  @-ms-viewport { width: device-width; }
+}
+
+html {
+  box-sizing: border-box;
+  -ms-overflow-style: scrollbar;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: inherit;
+}
+
+
+//
+// Variables
+//
+
+@import "variables";
+
+//
+// Grid mixins
+//
+
+@import "mixins/clearfix";
+@import "mixins/breakpoints";
+@import "mixins/grid-framework";
+@import "mixins/grid";
+
+@import "custom";
+
+@import "grid";
diff --git a/src/styles/bootstrap/bootstrap-reboot.scss b/src/styles/bootstrap/bootstrap-reboot.scss
new file mode 100755
index 0000000000000000000000000000000000000000..978b086a1a28d9ed2a6241e57349f3ba5a9dde45
--- /dev/null
+++ b/src/styles/bootstrap/bootstrap-reboot.scss
@@ -0,0 +1,10 @@
+// Bootstrap Reboot only
+//
+// Includes only Normalize and our custom Reboot reset.
+
+@import "variables";
+@import "mixins";
+@import "custom";
+
+@import "normalize";
+@import "reboot";
diff --git a/src/styles/bootstrap/bootstrap.scss b/src/styles/bootstrap/bootstrap.scss
new file mode 100755
index 0000000000000000000000000000000000000000..88a60cafa0b9cf25580bee8bc74dadb0e9d6b236
--- /dev/null
+++ b/src/styles/bootstrap/bootstrap.scss
@@ -0,0 +1,54 @@
+/*!
+ * Bootstrap v4.0.0-alpha.6 (https://getbootstrap.com)
+ * Copyright 2011-2017 The Bootstrap Authors
+ * Copyright 2011-2017 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+// Core variables and mixins
+@import "variables";
+@import "mixins";
+@import "custom";
+
+// Reset and dependencies
+@import "normalize";
+@import "print";
+
+// Core CSS
+@import "reboot";
+@import "type";
+@import "images";
+@import "code";
+@import "grid";
+@import "tables";
+@import "forms";
+@import "buttons";
+
+// Components
+@import "transitions";
+@import "dropdown";
+@import "button-group";
+@import "input-group";
+@import "custom-forms";
+@import "nav";
+@import "navbar";
+@import "card";
+@import "breadcrumb";
+@import "pagination";
+@import "badge";
+@import "jumbotron";
+@import "alert";
+@import "progress";
+@import "media";
+@import "list-group";
+@import "responsive-embed";
+@import "close";
+
+// Components w/ JavaScript
+@import "modal";
+@import "tooltip";
+@import "popover";
+@import "carousel";
+
+// Utility classes
+@import "utilities";
diff --git a/src/styles/bootstrap/mixins/_alert.scss b/src/styles/bootstrap/mixins/_alert.scss
new file mode 100755
index 0000000000000000000000000000000000000000..6ed3a81ab1d015aaaf2074fe6d028fdb88b105a4
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_alert.scss
@@ -0,0 +1,14 @@
+// Alerts
+
+@mixin alert-variant($background, $border, $body-color) {
+  background-color: $background;
+  border-color: $border;
+  color: $body-color;
+
+  hr {
+    border-top-color: darken($border, 5%);
+  }
+  .alert-link {
+    color: darken($body-color, 10%);
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_background-variant.scss b/src/styles/bootstrap/mixins/_background-variant.scss
new file mode 100755
index 0000000000000000000000000000000000000000..54a734dcc82f5a0226df8c09bd03c4a9381d245e
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_background-variant.scss
@@ -0,0 +1,12 @@
+// Contextual backgrounds
+
+@mixin bg-variant($parent, $color) {
+  #{$parent} {
+    background-color: $color !important;
+  }
+  a#{$parent} {
+    @include hover-focus {
+      background-color: darken($color, 10%) !important;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_badge.scss b/src/styles/bootstrap/mixins/_badge.scss
new file mode 100755
index 0000000000000000000000000000000000000000..9fa44b647813b390ac3c71ac0e7e41bb135f7544
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_badge.scss
@@ -0,0 +1,11 @@
+// Badges
+
+@mixin badge-variant($color) {
+  background-color: $color;
+
+  &[href] {
+    @include hover-focus {
+      background-color: darken($color, 10%);
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_border-radius.scss b/src/styles/bootstrap/mixins/_border-radius.scss
new file mode 100755
index 0000000000000000000000000000000000000000..54f29f41da40170873dca54b089a60a41e123f98
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_border-radius.scss
@@ -0,0 +1,35 @@
+// Single side border-radius
+
+@mixin border-radius($radius: $border-radius) {
+  @if $enable-rounded {
+    border-radius: $radius;
+  }
+}
+
+@mixin border-top-radius($radius) {
+  @if $enable-rounded {
+    border-top-right-radius: $radius;
+    border-top-left-radius: $radius;
+  }
+}
+
+@mixin border-right-radius($radius) {
+  @if $enable-rounded {
+    border-bottom-right-radius: $radius;
+    border-top-right-radius: $radius;
+  }
+}
+
+@mixin border-bottom-radius($radius) {
+  @if $enable-rounded {
+    border-bottom-right-radius: $radius;
+    border-bottom-left-radius: $radius;
+  }
+}
+
+@mixin border-left-radius($radius) {
+  @if $enable-rounded {
+    border-bottom-left-radius: $radius;
+    border-top-left-radius: $radius;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_breakpoints.scss b/src/styles/bootstrap/mixins/_breakpoints.scss
new file mode 100755
index 0000000000000000000000000000000000000000..6fd2e8e1e86f1cdf3aed7c381af68a072a679f94
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_breakpoints.scss
@@ -0,0 +1,95 @@
+// Breakpoint viewport sizes and media queries.
+//
+// Breakpoints are defined as a map of (name: minimum width), order from small to large:
+//
+//    (xs: 0, sm: 576px, md: 768px)
+//
+// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
+
+// Name of the next breakpoint, or null for the last breakpoint.
+//
+//    >> breakpoint-next(sm)
+//    md
+//    >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px))
+//    md
+//    >> breakpoint-next(sm, $breakpoint-names: (xs sm md))
+//    md
+@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
+  $n: index($breakpoint-names, $name);
+  @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
+}
+
+// Minimum breakpoint width. Null for the smallest (first) breakpoint.
+//
+//    >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px))
+//    576px
+@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
+  $min: map-get($breakpoints, $name);
+  @return if($min != 0, $min, null);
+}
+
+// Maximum breakpoint width. Null for the largest (last) breakpoint.
+// The maximum value is calculated as the minimum of the next one less 0.1.
+//
+//    >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px))
+//    767px
+@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
+  $next: breakpoint-next($name, $breakpoints);
+  @return if($next, breakpoint-min($next, $breakpoints) - 1px, null);
+}
+
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
+// Useful for making responsive utilities.
+//
+//    >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px))
+//    ""  (Returns a blank string)
+//    >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px))
+//    "-sm"
+@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
+  @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
+}
+
+// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
+// Makes the @content apply to the given breakpoint and wider.
+@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($name, $breakpoints);
+  @if $min {
+    @media (min-width: $min) {
+      @content;
+    }
+  } @else {
+    @content;
+  }
+}
+
+// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
+// Makes the @content apply to the given breakpoint and narrower.
+@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
+  $max: breakpoint-max($name, $breakpoints);
+  @if $max {
+    @media (max-width: $max) {
+      @content;
+    }
+  } @else {
+    @content;
+  }
+}
+
+// Media that spans multiple breakpoint widths.
+// Makes the @content apply between the min and max breakpoints
+@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
+  @include media-breakpoint-up($lower, $breakpoints) {
+    @include media-breakpoint-down($upper, $breakpoints) {
+      @content;
+    }
+  }
+}
+
+// Media between the breakpoint's minimum and maximum widths.
+// No minimum for the smallest breakpoint, and no maximum for the largest one.
+// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
+@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
+  @include media-breakpoint-between($name, $name, $breakpoints) {
+    @content;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_buttons.scss b/src/styles/bootstrap/mixins/_buttons.scss
new file mode 100755
index 0000000000000000000000000000000000000000..f9981e326ae932142f3d1ec614ec1272349957b3
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_buttons.scss
@@ -0,0 +1,86 @@
+// Button variants
+//
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+
+@mixin button-variant($color, $background, $border) {
+  $active-background: darken($background, 10%);
+  $active-border: darken($border, 12%);
+
+  color: $color;
+  background-color: $background;
+  border-color: $border;
+  @include box-shadow($btn-box-shadow);
+
+  // Hover and focus styles are shared
+  @include hover {
+    color: $color;
+    background-color: $active-background;
+    border-color: $active-border;
+  }
+  &:focus,
+  &.focus {
+    // Avoid using mixin so we can pass custom focus shadow properly
+    @if $enable-shadows {
+      box-shadow: $btn-box-shadow, 0 0 0 2px rgba($border, .5);
+    } @else {
+      box-shadow: 0 0 0 2px rgba($border, .5);
+    }
+  }
+
+  // Disabled comes first so active can properly restyle
+  &.disabled,
+  &:disabled {
+    background-color: $background;
+    border-color: $border;
+  }
+
+  &:active,
+  &.active,
+  .show > &.dropdown-toggle {
+    color: $color;
+    background-color: $active-background;
+    background-image: none; // Remove the gradient for the pressed/active state
+    border-color: $active-border;
+    @include box-shadow($btn-active-box-shadow);
+  }
+}
+
+@mixin button-outline-variant($color, $color-hover: #fff) {
+  color: $color;
+  background-image: none;
+  background-color: transparent;
+  border-color: $color;
+
+  @include hover {
+    color: $color-hover;
+    background-color: $color;
+    border-color: $color;
+  }
+
+  &:focus,
+  &.focus {
+    box-shadow: 0 0 0 2px rgba($color, .5);
+  }
+
+  &.disabled,
+  &:disabled {
+    color: $color;
+    background-color: transparent;
+  }
+
+  &:active,
+  &.active,
+  .show > &.dropdown-toggle {
+    color: $color-hover;
+    background-color: $color;
+    border-color: $color;
+  }
+}
+
+// Button sizes
+@mixin button-size($padding-y, $padding-x, $font-size, $border-radius) {
+  padding: $padding-y $padding-x;
+  font-size: $font-size;
+  @include border-radius($border-radius);
+}
diff --git a/src/styles/bootstrap/mixins/_cards.scss b/src/styles/bootstrap/mixins/_cards.scss
new file mode 100755
index 0000000000000000000000000000000000000000..4b1232d8b20094db228d7da58353f4bf2b9cb639
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_cards.scss
@@ -0,0 +1,47 @@
+// Card variants
+
+@mixin card-variant($background, $border) {
+  background-color: $background;
+  border-color: $border;
+
+  .card-header,
+  .card-footer {
+    background-color: transparent;
+  }
+}
+
+@mixin card-outline-variant($color) {
+  background-color: transparent;
+  border-color: $color;
+}
+
+//
+// Inverse text within a card for use with dark backgrounds
+//
+
+@mixin card-inverse {
+  color: rgba(255,255,255,.65);
+
+  .card-header,
+  .card-footer {
+    background-color: transparent;
+    border-color: rgba(255,255,255,.2);
+  }
+  .card-header,
+  .card-footer,
+  .card-title,
+  .card-blockquote {
+    color: #fff;
+  }
+  .card-link,
+  .card-text,
+  .card-subtitle,
+  .card-blockquote .blockquote-footer {
+    color: rgba(255,255,255,.65);
+  }
+  .card-link {
+    @include hover-focus {
+      color: $card-link-hover-color;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_clearfix.scss b/src/styles/bootstrap/mixins/_clearfix.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b72cf27128aaf88939ffdc18d1a8ab643b3c8f3f
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_clearfix.scss
@@ -0,0 +1,7 @@
+@mixin clearfix() {
+  &::after {
+    display: block;
+    content: "";
+    clear: both;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_float.scss b/src/styles/bootstrap/mixins/_float.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b43116fa6cb6693cfe093c3b8e5ded326d3a318c
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_float.scss
@@ -0,0 +1,9 @@
+@mixin float-left {
+  float: left !important;
+}
+@mixin float-right {
+  float: right !important;
+}
+@mixin float-none {
+  float: none !important;
+}
diff --git a/src/styles/bootstrap/mixins/_forms.scss b/src/styles/bootstrap/mixins/_forms.scss
new file mode 100755
index 0000000000000000000000000000000000000000..c8aea9669d821edec49e233bc9d4fb97dbb0440a
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_forms.scss
@@ -0,0 +1,79 @@
+// Form validation states
+//
+// Used in _forms.scss to generate the form validation CSS for warnings, errors,
+// and successes.
+
+@mixin form-control-validation($color) {
+  // Color the label and help text
+  .form-control-feedback,
+  .form-control-label,
+  .col-form-label,
+  .form-check-label,
+  .custom-control {
+    color: $color;
+  }
+
+  // Set the border and box shadow on specific inputs to match
+  .form-control {
+    border-color: $color;
+
+    &:focus {
+      @include box-shadow($input-box-shadow, 0 0 6px lighten($color, 20%));
+    }
+  }
+
+  // Set validation states also for addons
+  .input-group-addon {
+    color: $color;
+    border-color: $color;
+    background-color: lighten($color, 40%);
+  }
+}
+
+// Form control focus state
+//
+// Generate a customized focus state and for any input with the specified color,
+// which defaults to the `@input-border-focus` variable.
+//
+// We highly encourage you to not customize the default value, but instead use
+// this to tweak colors on an as-needed basis. This aesthetic change is based on
+// WebKit's default styles, but applicable to a wider range of browsers. Its
+// usability and accessibility should be taken into account with any change.
+//
+// Example usage: change the default blue border and shadow to white for better
+// contrast against a dark gray background.
+@mixin form-control-focus() {
+  &:focus {
+    color: $input-color-focus;
+    background-color: $input-bg-focus;
+    border-color: $input-border-focus;
+    outline: none;
+    @include box-shadow($input-box-shadow-focus);
+  }
+}
+
+// Form control sizing
+//
+// Relative text size, padding, and border-radii changes for form controls. For
+// horizontal sizing, wrap controls in the predefined grid classes. `<select>`
+// element gets special love because it's special, and that's a fact!
+
+@mixin input-size($parent, $input-height, $padding-y, $padding-x, $font-size, $line-height, $border-radius) {
+  #{$parent} {
+    height: $input-height;
+    padding: $padding-y $padding-x;
+    font-size: $font-size;
+    line-height: $line-height;
+    @include border-radius($border-radius);
+  }
+
+  select#{$parent} {
+    height: $input-height;
+    line-height: $input-height;
+  }
+
+  textarea#{$parent},
+  select[multiple]#{$parent} {
+    height: auto;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_gradients.scss b/src/styles/bootstrap/mixins/_gradients.scss
new file mode 100755
index 0000000000000000000000000000000000000000..8bfd97c4d8de8a8b19c24a727335177275689cd5
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_gradients.scss
@@ -0,0 +1,37 @@
+// Gradients
+
+// Horizontal gradient, from left to right
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+@mixin gradient-x($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
+  background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent);
+  background-repeat: repeat-x;
+}
+
+// Vertical gradient, from top to bottom
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+@mixin gradient-y($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
+  background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent);
+  background-repeat: repeat-x;
+}
+
+@mixin gradient-directional($start-color: #555, $end-color: #333, $deg: 45deg) {
+  background-repeat: repeat-x;
+  background-image: linear-gradient($deg, $start-color, $end-color);
+}
+@mixin gradient-x-three-colors($start-color: #00b3ee, $mid-color: #7a43b6, $color-stop: 50%, $end-color: #c3325f) {
+  background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color);
+  background-repeat: no-repeat;
+}
+@mixin gradient-y-three-colors($start-color: #00b3ee, $mid-color: #7a43b6, $color-stop: 50%, $end-color: #c3325f) {
+  background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color);
+  background-repeat: no-repeat;
+}
+@mixin gradient-radial($inner-color: #555, $outer-color: #333) {
+  background-image: radial-gradient(circle, $inner-color, $outer-color);
+  background-repeat: no-repeat;
+}
+@mixin gradient-striped($color: rgba(255,255,255,.15), $angle: 45deg) {
+  background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent);
+}
diff --git a/src/styles/bootstrap/mixins/_grid-framework.scss b/src/styles/bootstrap/mixins/_grid-framework.scss
new file mode 100755
index 0000000000000000000000000000000000000000..0aa814ab2d89a95166b367db58eaca0968bb5bcf
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_grid-framework.scss
@@ -0,0 +1,65 @@
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `$grid-columns`.
+
+@mixin make-grid-columns($columns: $grid-columns, $gutters: $grid-gutter-widths, $breakpoints: $grid-breakpoints) {
+  // Common properties for all breakpoints
+  %grid-column {
+    position: relative;
+    width: 100%;
+    min-height: 1px; // Prevent columns from collapsing when empty
+
+    @include make-gutters($gutters);
+  }
+
+  @each $breakpoint in map-keys($breakpoints) {
+    $infix: breakpoint-infix($breakpoint, $breakpoints);
+
+    // Allow columns to stretch full width below their breakpoints
+    @for $i from 1 through $columns {
+      .col#{$infix}-#{$i} {
+        @extend %grid-column;
+      }
+    }
+    .col#{$infix} {
+      @extend %grid-column;
+    }
+
+    @include media-breakpoint-up($breakpoint, $breakpoints) {
+      // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+      .col#{$infix} {
+        flex-basis: 0;
+        flex-grow: 1;
+        max-width: 100%;
+      }
+      .col#{$infix}-auto {
+        flex: 0 0 auto;
+        width: auto;
+      }
+
+      @for $i from 1 through $columns {
+        .col#{$infix}-#{$i} {
+          @include make-col($i, $columns);
+        }
+      }
+
+      @each $modifier in (pull, push) {
+        @for $i from 0 through $columns {
+          .#{$modifier}#{$infix}-#{$i} {
+            @include make-col-modifier($modifier, $i, $columns)
+          }
+        }
+      }
+
+      // `$columns - 1` because offsetting by the width of an entire row isn't possible
+      @for $i from 0 through ($columns - 1) {
+        @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-xs-0
+          .offset#{$infix}-#{$i} {
+            @include make-col-modifier(offset, $i, $columns)
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_grid.scss b/src/styles/bootstrap/mixins/_grid.scss
new file mode 100755
index 0000000000000000000000000000000000000000..9cd8c7bbbbd2df3307ee7925dc463d3e798661db
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_grid.scss
@@ -0,0 +1,100 @@
+/// Grid system
+//
+// Generate semantic grid columns with these mixins.
+
+@mixin make-container($gutters: $grid-gutter-widths) {
+  position: relative;
+  margin-left: auto;
+  margin-right: auto;
+
+  @each $breakpoint in map-keys($gutters) {
+    @include media-breakpoint-up($breakpoint) {
+      $gutter: map-get($gutters, $breakpoint);
+      padding-right: ($gutter / 2);
+      padding-left:  ($gutter / 2);
+    }
+  }
+}
+
+
+// For each breakpoint, define the maximum width of the container in a media query
+@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
+  @each $breakpoint, $container-max-width in $max-widths {
+    @include media-breakpoint-up($breakpoint, $breakpoints) {
+      width: $container-max-width;
+      max-width: 100%;
+    }
+  }
+}
+
+@mixin make-gutters($gutters: $grid-gutter-widths) {
+  @each $breakpoint in map-keys($gutters) {
+    @include media-breakpoint-up($breakpoint) {
+      $gutter: map-get($gutters, $breakpoint);
+      padding-right: ($gutter / 2);
+      padding-left:  ($gutter / 2);
+    }
+  }
+}
+
+@mixin make-row($gutters: $grid-gutter-widths) {
+  display: flex;
+  flex-wrap: wrap;
+
+  @each $breakpoint in map-keys($gutters) {
+    @include media-breakpoint-up($breakpoint) {
+      $gutter: map-get($gutters, $breakpoint);
+      margin-right: ($gutter / -2);
+      margin-left:  ($gutter / -2);
+    }
+  }
+}
+
+@mixin make-col-ready($gutters: $grid-gutter-widths) {
+  position: relative;
+  // Prevent columns from becoming too narrow when at smaller grid tiers by
+  // always setting `width: 100%;`. This works because we use `flex` values
+  // later on to override this initial width.
+  width: 100%;
+  min-height: 1px; // Prevent collapsing
+
+  @each $breakpoint in map-keys($gutters) {
+    @include media-breakpoint-up($breakpoint) {
+      $gutter: map-get($gutters, $breakpoint);
+      padding-right: ($gutter / 2);
+      padding-left:  ($gutter / 2);
+    }
+  }
+}
+
+@mixin make-col($size, $columns: $grid-columns) {
+  flex: 0 0 percentage($size / $columns);
+  // width: percentage($size / $columns);
+  // Add a `max-width` to ensure content within each column does not blow out
+  // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
+  // do not appear to require this.
+  max-width: percentage($size / $columns);
+}
+
+@mixin make-col-offset($size, $columns: $grid-columns) {
+  margin-left: percentage($size / $columns);
+}
+
+@mixin make-col-push($size, $columns: $grid-columns) {
+  left: if($size > 0, percentage($size / $columns), auto);
+}
+
+@mixin make-col-pull($size, $columns: $grid-columns) {
+  right: if($size > 0, percentage($size / $columns), auto);
+}
+
+@mixin make-col-modifier($type, $size, $columns) {
+  // Work around the lack of dynamic mixin @include support (https://github.com/sass/sass/issues/626)
+  @if $type == push {
+    @include make-col-push($size, $columns);
+  } @else if $type == pull {
+    @include make-col-pull($size, $columns);
+  } @else if $type == offset {
+    @include make-col-offset($size, $columns);
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_hover.scss b/src/styles/bootstrap/mixins/_hover.scss
new file mode 100755
index 0000000000000000000000000000000000000000..6dd55e705a6e177d1c62c4dcdf02d2d61f33d54a
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_hover.scss
@@ -0,0 +1,60 @@
+@mixin hover {
+  // TODO: re-enable along with mq4-hover-shim
+//  @if $enable-hover-media-query {
+//    // See Media Queries Level 4: https://drafts.csswg.org/mediaqueries/#hover
+//    // Currently shimmed by https://github.com/twbs/mq4-hover-shim
+//    @media (hover: hover) {
+//      &:hover { @content }
+//    }
+//  }
+//  @else {
+    &:hover { @content }
+//  }
+}
+
+@mixin hover-focus {
+  @if $enable-hover-media-query {
+    &:focus { @content }
+    @include hover { @content }
+  }
+  @else {
+    &:focus,
+    &:hover {
+      @content
+    }
+  }
+}
+
+@mixin plain-hover-focus {
+  @if $enable-hover-media-query {
+    &,
+    &:focus {
+      @content
+    }
+    @include hover { @content }
+  }
+  @else {
+    &,
+    &:focus,
+    &:hover {
+      @content
+    }
+  }
+}
+
+@mixin hover-focus-active {
+  @if $enable-hover-media-query {
+    &:focus,
+    &:active {
+      @content
+    }
+    @include hover { @content }
+  }
+  @else {
+    &:focus,
+    &:active,
+    &:hover {
+      @content
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_image.scss b/src/styles/bootstrap/mixins/_image.scss
new file mode 100755
index 0000000000000000000000000000000000000000..c2b45f2ceadf974db39e4b4b6d3e592785bffb05
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_image.scss
@@ -0,0 +1,36 @@
+// Image Mixins
+// - Responsive image
+// - Retina image
+
+
+// Responsive image
+//
+// Keep images from scaling beyond the width of their parents.
+
+@mixin img-fluid {
+  // Part 1: Set a maximum relative to the parent
+  max-width: 100%;
+  // Part 2: Override the height to auto, otherwise images will be stretched
+  // when setting a width and height attribute on the img element.
+  height: auto;
+}
+
+
+// Retina image
+//
+// Short retina mixin for setting background-image and -size.
+
+@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {
+  background-image: url($file-1x);
+
+  // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,
+  // but doesn't convert dppx=>dpi.
+  // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.
+  // Compatibility info: http://caniuse.com/#feat=css-media-resolution
+  @media
+  only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx
+  only screen and (min-resolution: 2dppx) { // Standardized
+    background-image: url($file-2x);
+    background-size: $width-1x $height-1x;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_list-group.scss b/src/styles/bootstrap/mixins/_list-group.scss
new file mode 100755
index 0000000000000000000000000000000000000000..3db5b096a421072587ca56dd21108ae37a415145
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_list-group.scss
@@ -0,0 +1,28 @@
+// List Groups
+
+@mixin list-group-item-variant($state, $background, $color) {
+  .list-group-item-#{$state} {
+    color: $color;
+    background-color: $background;
+  }
+
+  a.list-group-item-#{$state},
+  button.list-group-item-#{$state} {
+    color: $color;
+
+    .list-group-item-heading {
+      color: inherit;
+    }
+
+    @include hover-focus {
+      color: $color;
+      background-color: darken($background, 5%);
+    }
+
+    &.active {
+      color: #fff;
+      background-color: $color;
+      border-color: $color;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_lists.scss b/src/styles/bootstrap/mixins/_lists.scss
new file mode 100755
index 0000000000000000000000000000000000000000..25185626698393b1365199f93aadd8d3350dc9d5
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_lists.scss
@@ -0,0 +1,7 @@
+// Lists
+
+// Unstyled keeps list items block level, just removes default browser padding and list-style
+@mixin list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
diff --git a/src/styles/bootstrap/mixins/_nav-divider.scss b/src/styles/bootstrap/mixins/_nav-divider.scss
new file mode 100755
index 0000000000000000000000000000000000000000..fb3d12e9f69253bb53e35d69688c6cbf6cc153ac
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_nav-divider.scss
@@ -0,0 +1,10 @@
+// Horizontal dividers
+//
+// Dividers (basically an hr) within dropdowns and nav lists
+
+@mixin nav-divider($color: #e5e5e5) {
+  height: 1px;
+  margin: ($spacer-y / 2) 0;
+  overflow: hidden;
+  background-color: $color;
+}
diff --git a/src/styles/bootstrap/mixins/_navbar-align.scss b/src/styles/bootstrap/mixins/_navbar-align.scss
new file mode 100755
index 0000000000000000000000000000000000000000..c454a4ffe9a477bcee78b107eb83ce9c375a8e75
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_navbar-align.scss
@@ -0,0 +1,9 @@
+// Navbar vertical align
+//
+// Vertically center elements in the navbar.
+// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.
+
+// @mixin navbar-vertical-align($element-height) {
+//   margin-top: (($navbar-height - $element-height) / 2);
+//   margin-bottom: (($navbar-height - $element-height) / 2);
+// }
diff --git a/src/styles/bootstrap/mixins/_pagination.scss b/src/styles/bootstrap/mixins/_pagination.scss
new file mode 100755
index 0000000000000000000000000000000000000000..8cd9317cf5226e2f35d19cefa637feb73cfe4a8b
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_pagination.scss
@@ -0,0 +1,21 @@
+// Pagination
+
+@mixin pagination-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) {
+  .page-link {
+    padding: $padding-y $padding-x;
+    font-size: $font-size;
+  }
+
+  .page-item {
+    &:first-child {
+      .page-link {
+        @include border-left-radius($border-radius);
+      }
+    }
+    &:last-child {
+      .page-link {
+        @include border-right-radius($border-radius);
+      }
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_reset-text.scss b/src/styles/bootstrap/mixins/_reset-text.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b952730977bde7abbfc2b33eb07abf63d6456d50
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_reset-text.scss
@@ -0,0 +1,17 @@
+@mixin reset-text {
+  font-family: $font-family-base;
+  // We deliberately do NOT reset font-size or word-wrap.
+  font-style: normal;
+  font-weight: $font-weight-normal;
+  letter-spacing: normal;
+  line-break: auto;
+  line-height: $line-height-base;
+  text-align: left; // Fallback for where `start` is not supported
+  text-align: start;
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  white-space: normal;
+  word-break: normal;
+  word-spacing: normal;
+}
diff --git a/src/styles/bootstrap/mixins/_resize.scss b/src/styles/bootstrap/mixins/_resize.scss
new file mode 100755
index 0000000000000000000000000000000000000000..83fa6379179cba67dbd3b3fb1b1d167380f361d4
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_resize.scss
@@ -0,0 +1,6 @@
+// Resize anything
+
+@mixin resizable($direction) {
+  resize: $direction; // Options: horizontal, vertical, both
+  overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
+}
diff --git a/src/styles/bootstrap/mixins/_screen-reader.scss b/src/styles/bootstrap/mixins/_screen-reader.scss
new file mode 100755
index 0000000000000000000000000000000000000000..c208583249e27836ace927941cd07db5216497f6
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_screen-reader.scss
@@ -0,0 +1,32 @@
+// Only display content to screen readers
+//
+// See: http://a11yproject.com/posts/how-to-hide-content
+
+@mixin sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0,0,0,0);
+  border: 0;
+}
+
+// Use in conjunction with .sr-only to only display content when it's focused.
+//
+// Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+//
+// Credit: HTML5 Boilerplate
+
+@mixin sr-only-focusable {
+  &:active,
+  &:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_size.scss b/src/styles/bootstrap/mixins/_size.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b9dd48e8dfdacc6104c2cf360ea30bb65970a32b
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_size.scss
@@ -0,0 +1,6 @@
+// Sizing shortcuts
+
+@mixin size($width, $height: $width) {
+  width: $width;
+  height: $height;
+}
diff --git a/src/styles/bootstrap/mixins/_table-row.scss b/src/styles/bootstrap/mixins/_table-row.scss
new file mode 100755
index 0000000000000000000000000000000000000000..84f1d305aaf5287dbdf5baea2ff561ff57e425ec
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_table-row.scss
@@ -0,0 +1,30 @@
+// Tables
+
+@mixin table-row-variant($state, $background) {
+  // Exact selectors below required to override `.table-striped` and prevent
+  // inheritance to nested tables.
+  .table-#{$state} {
+    &,
+    > th,
+    > td {
+      background-color: $background;
+    }
+  }
+
+  // Hover states for `.table-hover`
+  // Note: this is not available for cells or rows within `thead` or `tfoot`.
+  .table-hover {
+    $hover-background: darken($background, 5%);
+
+    .table-#{$state} {
+      @include hover {
+        background-color: $hover-background;
+
+        > td,
+        > th {
+          background-color: $hover-background;
+        }
+      }
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_text-emphasis.scss b/src/styles/bootstrap/mixins/_text-emphasis.scss
new file mode 100755
index 0000000000000000000000000000000000000000..9cd4b6a4f008acb58dcfd415893df420edf6efd2
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_text-emphasis.scss
@@ -0,0 +1,12 @@
+// Typography
+
+@mixin text-emphasis-variant($parent, $color) {
+  #{$parent} {
+    color: $color !important;
+  }
+  a#{$parent} {
+    @include hover-focus {
+      color: darken($color, 10%) !important;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_text-hide.scss b/src/styles/bootstrap/mixins/_text-hide.scss
new file mode 100755
index 0000000000000000000000000000000000000000..52a38a906989ed0e4e7b19933c36a73209956056
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_text-hide.scss
@@ -0,0 +1,8 @@
+// CSS image replacement
+@mixin text-hide() {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
diff --git a/src/styles/bootstrap/mixins/_text-truncate.scss b/src/styles/bootstrap/mixins/_text-truncate.scss
new file mode 100755
index 0000000000000000000000000000000000000000..5a40bf533a9281f5ef015fdadc4a98ce169d9c2e
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_text-truncate.scss
@@ -0,0 +1,8 @@
+// Text truncate
+// Requires inline-block or block for proper styling
+
+@mixin text-truncate() {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
\ No newline at end of file
diff --git a/src/styles/bootstrap/mixins/_transforms.scss b/src/styles/bootstrap/mixins/_transforms.scss
new file mode 100755
index 0000000000000000000000000000000000000000..4005c9d028d3d8f3fcc94d5fa3d1cc6192bb2829
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_transforms.scss
@@ -0,0 +1,14 @@
+// Applies the given styles only when the browser support CSS3 3D transforms.
+@mixin if-supports-3d-transforms() {
+  @media (-webkit-transform-3d) {
+    // Old Safari, Old Android
+    // http://caniuse.com/#feat=css-featurequeries
+    // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/-webkit-transform-3d
+    @content;
+  }
+
+  @supports (transform: translate3d(0,0,0)) {
+    // The Proper Way: Using a CSS feature query
+    @content;
+  }
+}
diff --git a/src/styles/bootstrap/mixins/_visibility.scss b/src/styles/bootstrap/mixins/_visibility.scss
new file mode 100755
index 0000000000000000000000000000000000000000..88c50b05d5ced1df322e324996e2d30928dba85b
--- /dev/null
+++ b/src/styles/bootstrap/mixins/_visibility.scss
@@ -0,0 +1,5 @@
+// Visibility
+
+@mixin invisible {
+  visibility: hidden !important;
+}
diff --git a/src/styles/bootstrap/utilities/_align.scss b/src/styles/bootstrap/utilities/_align.scss
new file mode 100755
index 0000000000000000000000000000000000000000..4dbbbc2dbc47ed53d177ad1bc78d0c06605663e9
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_align.scss
@@ -0,0 +1,6 @@
+.align-baseline    { vertical-align: baseline !important; } // Browser default
+.align-top         { vertical-align: top !important; }
+.align-middle      { vertical-align: middle !important; }
+.align-bottom      { vertical-align: bottom !important; }
+.align-text-bottom { vertical-align: text-bottom !important; }
+.align-text-top    { vertical-align: text-top !important; }
diff --git a/src/styles/bootstrap/utilities/_background.scss b/src/styles/bootstrap/utilities/_background.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b9ac295231cfa4cffb6a8ce7d30cce429c829c69
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_background.scss
@@ -0,0 +1,19 @@
+//
+// Contextual backgrounds
+//
+
+.bg-faded {
+  background-color: darken($body-bg, 3%);
+}
+
+@include bg-variant('.bg-primary', $brand-primary);
+
+@include bg-variant('.bg-success', $brand-success);
+
+@include bg-variant('.bg-info', $brand-info);
+
+@include bg-variant('.bg-warning', $brand-warning);
+
+@include bg-variant('.bg-danger', $brand-danger);
+
+@include bg-variant('.bg-inverse', $brand-inverse);
diff --git a/src/styles/bootstrap/utilities/_borders.scss b/src/styles/bootstrap/utilities/_borders.scss
new file mode 100755
index 0000000000000000000000000000000000000000..b256881e5ece38fcd323bc67665b9221b254e069
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_borders.scss
@@ -0,0 +1,37 @@
+//
+// Border
+//
+
+.border-0        { border: 0 !important; }
+.border-top-0    { border-top: 0 !important; }
+.border-right-0  { border-right: 0 !important; }
+.border-bottom-0 { border-bottom: 0 !important; }
+.border-left-0   { border-left: 0 !important; }
+
+//
+// Border-radius
+//
+
+.rounded {
+  @include border-radius($border-radius);
+}
+.rounded-top {
+  @include border-top-radius($border-radius);
+}
+.rounded-right {
+  @include border-right-radius($border-radius);
+}
+.rounded-bottom {
+  @include border-bottom-radius($border-radius);
+}
+.rounded-left {
+  @include border-left-radius($border-radius);
+}
+
+.rounded-circle {
+  border-radius: 50%;
+}
+
+.rounded-0 {
+  border-radius: 0;
+}
diff --git a/src/styles/bootstrap/utilities/_clearfix.scss b/src/styles/bootstrap/utilities/_clearfix.scss
new file mode 100755
index 0000000000000000000000000000000000000000..e92522a94d82a571b84ac1de470bcb70b176023c
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_clearfix.scss
@@ -0,0 +1,3 @@
+.clearfix {
+  @include clearfix();
+}
diff --git a/src/styles/bootstrap/utilities/_display.scss b/src/styles/bootstrap/utilities/_display.scss
new file mode 100755
index 0000000000000000000000000000000000000000..ae942a6fb97d71d4e274ecf3b7f79966a03d3eba
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_display.scss
@@ -0,0 +1,18 @@
+//
+// Display utilities
+//
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .d#{$infix}-none         { display: none !important; }
+    .d#{$infix}-inline       { display: inline !important; }
+    .d#{$infix}-inline-block { display: inline-block !important; }
+    .d#{$infix}-block        { display: block !important; }
+    .d#{$infix}-table        { display: table !important; }
+    .d#{$infix}-table-cell   { display: table-cell !important; }
+    .d#{$infix}-flex         { display: flex !important; }
+    .d#{$infix}-inline-flex  { display: inline-flex !important; }
+  }
+}
diff --git a/src/styles/bootstrap/utilities/_flex.scss b/src/styles/bootstrap/utilities/_flex.scss
new file mode 100755
index 0000000000000000000000000000000000000000..1b98aaa3fa8bcbd15dd458276d0c2015b5ac833e
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_flex.scss
@@ -0,0 +1,48 @@
+// Flex variation
+//
+// Custom styles for additional flex alignment options.
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .flex#{$infix}-first     { order: -1; }
+    .flex#{$infix}-last      { order: 1; }
+    .flex#{$infix}-unordered { order: 0; }
+
+    .flex#{$infix}-row            { flex-direction: row !important; }
+    .flex#{$infix}-column         { flex-direction: column !important; }
+    .flex#{$infix}-row-reverse    { flex-direction: row-reverse !important; }
+    .flex#{$infix}-column-reverse { flex-direction: column-reverse !important; }
+
+    .flex#{$infix}-wrap         { flex-wrap: wrap !important; }
+    .flex#{$infix}-nowrap       { flex-wrap: nowrap !important; }
+    .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }
+
+    .justify-content#{$infix}-start   { justify-content: flex-start !important; }
+    .justify-content#{$infix}-end     { justify-content: flex-end !important; }
+    .justify-content#{$infix}-center  { justify-content: center !important; }
+    .justify-content#{$infix}-between { justify-content: space-between !important; }
+    .justify-content#{$infix}-around  { justify-content: space-around !important; }
+
+    .align-items#{$infix}-start    { align-items: flex-start !important; }
+    .align-items#{$infix}-end      { align-items: flex-end !important; }
+    .align-items#{$infix}-center   { align-items: center !important; }
+    .align-items#{$infix}-baseline { align-items: baseline !important; }
+    .align-items#{$infix}-stretch  { align-items: stretch !important; }
+
+    .align-content#{$infix}-start   { align-content: flex-start !important; }
+    .align-content#{$infix}-end     { align-content: flex-end !important; }
+    .align-content#{$infix}-center  { align-content: center !important; }
+    .align-content#{$infix}-between { align-content: space-between !important; }
+    .align-content#{$infix}-around  { align-content: space-around !important; }
+    .align-content#{$infix}-stretch { align-content: stretch !important; }
+
+    .align-self#{$infix}-auto     { align-self: auto !important; }
+    .align-self#{$infix}-start    { align-self: flex-start !important; }
+    .align-self#{$infix}-end      { align-self: flex-end !important; }
+    .align-self#{$infix}-center   { align-self: center !important; }
+    .align-self#{$infix}-baseline { align-self: baseline !important; }
+    .align-self#{$infix}-stretch  { align-self: stretch !important; }
+  }
+}
diff --git a/src/styles/bootstrap/utilities/_float.scss b/src/styles/bootstrap/utilities/_float.scss
new file mode 100755
index 0000000000000000000000000000000000000000..01655e9a5212731038b8a99633a8a7ed73ecf6a1
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_float.scss
@@ -0,0 +1,9 @@
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .float#{$infix}-left  { @include float-left; }
+    .float#{$infix}-right { @include float-right; }
+    .float#{$infix}-none  { @include float-none; }
+  }
+}
diff --git a/src/styles/bootstrap/utilities/_position.scss b/src/styles/bootstrap/utilities/_position.scss
new file mode 100755
index 0000000000000000000000000000000000000000..2cf08bfa013d9a9bdd4c0a852ba2f380e68ccd78
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_position.scss
@@ -0,0 +1,23 @@
+// Positioning
+
+.fixed-top {
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  z-index: $zindex-fixed;
+}
+
+.fixed-bottom {
+  position: fixed;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: $zindex-fixed;
+}
+
+.sticky-top {
+  position: sticky;
+  top: 0;
+  z-index: $zindex-sticky;
+}
diff --git a/src/styles/bootstrap/utilities/_screenreaders.scss b/src/styles/bootstrap/utilities/_screenreaders.scss
new file mode 100755
index 0000000000000000000000000000000000000000..9f26fde03538350e750a76cf2415a62f960976af
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_screenreaders.scss
@@ -0,0 +1,11 @@
+//
+// Screenreaders
+//
+
+.sr-only {
+  @include sr-only();
+}
+
+.sr-only-focusable {
+  @include sr-only-focusable();
+}
diff --git a/src/styles/bootstrap/utilities/_sizing.scss b/src/styles/bootstrap/utilities/_sizing.scss
new file mode 100755
index 0000000000000000000000000000000000000000..a7dc3e49b8e1c9f280ae7815c4ae02ef63dbacf7
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_sizing.scss
@@ -0,0 +1,10 @@
+// Width and height
+
+@each $prop, $abbrev in (width: w, height: h) {
+  @each $size, $length in $sizes {
+    .#{$abbrev}-#{$size} { #{$prop}: $length !important; }
+  }
+}
+
+.mw-100 { max-width: 100% !important; }
+.mh-100 { max-height: 100% !important; }
diff --git a/src/styles/bootstrap/utilities/_spacing.scss b/src/styles/bootstrap/utilities/_spacing.scss
new file mode 100755
index 0000000000000000000000000000000000000000..6056e2b7e27fbdb401a61832b32499dc352c62b3
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_spacing.scss
@@ -0,0 +1,43 @@
+// Margin and Padding
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    @each $prop, $abbrev in (margin: m, padding: p) {
+      @each $size, $lengths in $spacers {
+        $length-x: map-get($lengths, x);
+        $length-y: map-get($lengths, y);
+
+        .#{$abbrev}#{$infix}-#{$size}  { #{$prop}:        $length-y $length-x !important; }
+        .#{$abbrev}t#{$infix}-#{$size} { #{$prop}-top:    $length-y !important; }
+        .#{$abbrev}r#{$infix}-#{$size} { #{$prop}-right:  $length-x !important; }
+        .#{$abbrev}b#{$infix}-#{$size} { #{$prop}-bottom: $length-y !important; }
+        .#{$abbrev}l#{$infix}-#{$size} { #{$prop}-left:   $length-x !important; }
+        .#{$abbrev}x#{$infix}-#{$size} {
+          #{$prop}-right: $length-x !important;
+          #{$prop}-left:  $length-x !important;
+        }
+        .#{$abbrev}y#{$infix}-#{$size} {
+          #{$prop}-top:    $length-y !important;
+          #{$prop}-bottom: $length-y !important;
+        }
+      }
+    }
+
+    // Some special margin utils
+    .m#{$infix}-auto  { margin:        auto !important; }
+    .mt#{$infix}-auto { margin-top:    auto !important; }
+    .mr#{$infix}-auto { margin-right:  auto !important; }
+    .mb#{$infix}-auto { margin-bottom: auto !important; }
+    .ml#{$infix}-auto { margin-left:   auto !important; }
+    .mx#{$infix}-auto {
+      margin-right: auto !important;
+      margin-left:  auto !important;
+    }
+    .my#{$infix}-auto {
+      margin-top:    auto !important;
+      margin-bottom: auto !important;
+    }
+  }
+}
diff --git a/src/styles/bootstrap/utilities/_text.scss b/src/styles/bootstrap/utilities/_text.scss
new file mode 100755
index 0000000000000000000000000000000000000000..4ac90533acb9376b3a31016fb70c56803f7ef199
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_text.scss
@@ -0,0 +1,61 @@
+//
+// Text
+//
+
+// Alignment
+
+.text-justify  { text-align: justify !important; }
+.text-nowrap   { white-space: nowrap !important; }
+.text-truncate { @include text-truncate; }
+
+// Responsive alignment
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .text#{$infix}-left   { text-align: left !important; }
+    .text#{$infix}-right  { text-align: right !important; }
+    .text#{$infix}-center { text-align: center !important; }
+  }
+}
+
+// Transformation
+
+.text-lowercase  { text-transform: lowercase !important; }
+.text-uppercase  { text-transform: uppercase !important; }
+.text-capitalize { text-transform: capitalize !important; }
+
+// Weight and italics
+
+.font-weight-normal { font-weight: $font-weight-normal; }
+.font-weight-bold   { font-weight: $font-weight-bold; }
+.font-italic        { font-style: italic; }
+
+// Contextual colors
+
+.text-white {
+  color: #fff !important;
+}
+
+@include text-emphasis-variant('.text-muted', $text-muted);
+
+@include text-emphasis-variant('.text-primary', $brand-primary);
+
+@include text-emphasis-variant('.text-success', $brand-success);
+
+@include text-emphasis-variant('.text-info', $brand-info);
+
+@include text-emphasis-variant('.text-warning', $brand-warning);
+
+@include text-emphasis-variant('.text-danger', $brand-danger);
+
+// Font color
+
+@include text-emphasis-variant('.text-gray-dark', $gray-dark);
+
+// Misc
+
+.text-hide {
+  @include text-hide();
+}
diff --git a/src/styles/bootstrap/utilities/_visibility.scss b/src/styles/bootstrap/utilities/_visibility.scss
new file mode 100755
index 0000000000000000000000000000000000000000..fcedc9cb91233651d010415efd14a23afab67306
--- /dev/null
+++ b/src/styles/bootstrap/utilities/_visibility.scss
@@ -0,0 +1,55 @@
+//
+// Visibility utilities
+//
+
+.invisible {
+  @include invisible();
+}
+
+// Responsive visibility utilities
+
+@each $bp in map-keys($grid-breakpoints) {
+  .hidden-#{$bp}-up {
+    @include media-breakpoint-up($bp) {
+      display: none !important;
+    }
+  }
+  .hidden-#{$bp}-down {
+    @include media-breakpoint-down($bp) {
+      display: none !important;
+    }
+  }
+}
+
+
+// Print utilities
+//
+// Media queries are placed on the inside to be mixin-friendly.
+
+.visible-print-block {
+  display: none !important;
+
+  @media print {
+    display: block !important;
+  }
+}
+.visible-print-inline {
+  display: none !important;
+
+  @media print {
+    display: inline !important;
+  }
+}
+.visible-print-inline-block {
+  display: none !important;
+
+  @media print {
+    display: inline-block !important;
+  }
+}
+
+.hidden-print {
+  @media print {
+    display: none !important;
+  }
+}
diff --git a/src/styles/console.css b/src/styles/console.css
new file mode 100644
index 0000000000000000000000000000000000000000..3020e3eb8d0ca61b41e3cd16cf4dd532235af85f
--- /dev/null
+++ b/src/styles/console.css
@@ -0,0 +1,176 @@
+    #console {
+        height: 352px;
+        width: 100%;
+        /*margin-left: -10px !important;*/
+        position: relative;
+        background-color: black;
+        border: 2px solid #CCC;
+        margin: 0 auto;
+    }
+    /* The inner console element. */
+    
+    .jqconsole {
+        padding: 10px;        
+    }
+    /* The cursor. */
+    
+    .jqconsole-cursor {
+        background-color: gray;
+    }
+    /* The cursor color when the console looses focus. */
+    
+    .jqconsole-blurred .jqconsole-cursor {
+        background-color: #666;
+    }
+    /* The current prompt text color */
+    
+    .jqconsole-prompt {
+        color: rgb(11, 204, 50);
+           font-style: italic;
+    }
+    /* The command history */
+    
+    .jqconsole-old-prompt {
+        color: rgb(11, 204, 50);
+       /* color: rgb(11, 204, 2); */
+        /*color: rgba(140, 136, 187, 0.84);*/
+        /* font-weight: normal;*/
+    }
+    
+    .jqconsole-prompt, .jqconsole-old-prompt, .jqconsole-output {
+        word-wrap: break-word;
+        white-space: pre-wrap;
+
+    }
+
+    /* The text color when in input mode. */
+
+    .jqconsole-input {
+        
+    }
+    /* Previously entered input. */
+    
+    .jqconsole-old-input {
+        color: #bb0;
+        font-weight: normal;
+    }
+    /* The text color of the output. */
+
+    
+    .jqconsole-header{
+        color: #d0d0d0;
+    }
+
+    .nomArchivoInp {
+        /*width: 55% !important;*/
+        width: calc(100% - 250px);
+        float: left;
+    }
+    
+    /*.CodeMirror {
+        height: 450px !important;
+    }*/
+    
+     /* custom clases */
+
+    /* normal output */
+    .jqconsole-output{
+        color:white;
+    }
+
+    /* logs */
+    .jqconsole-logs{
+        color:brown;
+    }
+
+/* CSS Necesario para los paneles del editor */
+ .border {
+    border: 1px solid #f7f7f7;
+  }
+  .add-panel {
+    background: orange;
+    padding: 3px 6px;
+    color: white !important; 
+    border-radius: 3px;
+  }
+  .add-panel, .remove-panel {
+    cursor: pointer;
+  }
+  .remove-panel {
+    float: right;
+  }
+  .panel {
+    background: #f7f7f7;
+    padding: 3px 7px;
+    font-size: 0.85em;
+  }
+  .panel.top, .panel.after-top {
+    border-bottom: 1px solid #ddd;
+  }
+  .panel.bottom, .panel.before-bottom {
+    border-top: 1px solid #ddd;
+  }
+/* FIN CSS Paneles **************************/  
+
+
+.alertPosition{
+  position: absolute;
+  left: 50%;
+  z-index: 1500;
+  transform: translateX(-50%);
+}
+
+
+.breakpoints {width: .8em;}
+      .breakpoint { color: white !important; }
+      .CodeMirror {border: 1px solid #aaa;}
+.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
+  background-position: left bottom;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-lint-mark-error {
+  background-image:
+  url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
+  ;
+}
+
+.CodeMirror-lint-mark-warning {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
+  background-position: center center;
+  background-repeat: no-repeat;
+  cursor: pointer;
+  display: inline-block;
+  height: 16px;
+  width: 16px;
+  vertical-align: middle;
+  position: relative;
+}
+
+.tooltip {
+    position: relative;
+    display: inline-block;
+    border-bottom: 1px dotted black;
+    cursor:pointer;
+}
+
+.tooltiptext {
+    visibility: hidden;
+    width: 120px;
+    background-color: white;
+    color: brown;
+    text-align: center;
+    border-radius: 6px;
+    padding: 5px 0;
+
+    /* Position the tooltip */
+    position: absolute;
+    z-index: 10000;
+}
+
+.tooltip:hover .tooltiptext {
+    visibility: visible;
+}
\ No newline at end of file
diff --git a/src/test.ts b/src/test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9bf72267e9b1ada4f46ff6a7729ef5f272f7c947
--- /dev/null
+++ b/src/test.ts
@@ -0,0 +1,32 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare var __karma__: any;
+declare var require: any;
+
+// Prevent Karma from running prematurely.
+__karma__.loaded = function () {};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.start();
diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json
new file mode 100644
index 0000000000000000000000000000000000000000..5e2507db58c96c20ac2015842994a5af0e39734b
--- /dev/null
+++ b/src/tsconfig.app.json
@@ -0,0 +1,13 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/app",
+    "module": "es2015",
+    "baseUrl": "",
+    "types": []
+  },
+  "exclude": [
+    "test.ts",
+    "**/*.spec.ts"
+  ]
+}
diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json
new file mode 100644
index 0000000000000000000000000000000000000000..510e3f1fdae16c04db35a215260837dedf236a43
--- /dev/null
+++ b/src/tsconfig.spec.json
@@ -0,0 +1,20 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/spec",
+    "module": "commonjs",
+    "target": "es5",
+    "baseUrl": "",
+    "types": [
+      "jasmine",
+      "node"
+    ]
+  },
+  "files": [
+    "test.ts"
+  ],
+  "include": [
+    "**/*.spec.ts",
+    "**/*.d.ts"
+  ]
+}
diff --git a/src/typings.d.ts b/src/typings.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..403b22fee09b241bf3a56ec1c6ed0fe2ccc7959c
--- /dev/null
+++ b/src/typings.d.ts
@@ -0,0 +1,5 @@
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+    id: string;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..a35a8ee3a40d4326972a6d31edf57a9bce437520
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,20 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "outDir": "./dist/out-tsc",
+    "baseUrl": "src",
+    "sourceMap": true,
+    "declaration": false,
+    "moduleResolution": "node",
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "target": "es5",
+    "typeRoots": [
+      "node_modules/@types"
+    ],
+    "lib": [
+      "es2016",
+      "dom"
+    ]
+  }
+}
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000000000000000000000000000000000000..9113f1368b224fe758fc39b529c6e9089fd06280
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,116 @@
+{
+  "rulesDirectory": [
+    "node_modules/codelyzer"
+  ],
+  "rules": {
+    "callable-types": true,
+    "class-name": true,
+    "comment-format": [
+      true,
+      "check-space"
+    ],
+    "curly": true,
+    "eofline": true,
+    "forin": true,
+    "import-blacklist": [true, "rxjs"],
+    "import-spacing": true,
+    "indent": [
+      true,
+      "spaces"
+    ],
+    "interface-over-type-literal": true,
+    "label-position": true,
+    "max-line-length": [
+      true,
+      140
+    ],
+    "member-access": false,
+    "member-ordering": [
+      true,
+      "static-before-instance",
+      "variables-before-functions"
+    ],
+    "no-arg": true,
+    "no-bitwise": true,
+    "no-console": [
+      true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace"
+    ],
+    "no-construct": true,
+    "no-debugger": true,
+    "no-duplicate-variable": true,
+    "no-empty": false,
+    "no-empty-interface": true,
+    "no-eval": true,
+    "no-inferrable-types": [true, "ignore-params"],
+    "no-shadowed-variable": true,
+    "no-string-literal": false,
+    "no-string-throw": true,
+    "no-switch-case-fall-through": true,
+    "no-trailing-whitespace": true,
+    "no-unused-expression": true,
+    "no-use-before-declare": true,
+    "no-var-keyword": true,
+    "object-literal-sort-keys": false,
+    "one-line": [
+      true,
+      "check-open-brace",
+      "check-catch",
+      "check-else",
+      "check-whitespace"
+    ],
+    "prefer-const": true,
+    "quotemark": [
+      true,
+      "single"
+    ],
+    "radix": true,
+    "semicolon": [
+      "always"
+    ],
+    "triple-equals": [
+      true,
+      "allow-null-check"
+    ],
+    "typedef-whitespace": [
+      true,
+      {
+        "call-signature": "nospace",
+        "index-signature": "nospace",
+        "parameter": "nospace",
+        "property-declaration": "nospace",
+        "variable-declaration": "nospace"
+      }
+    ],
+    "typeof-compare": true,
+    "unified-signatures": true,
+    "variable-name": false,
+    "whitespace": [
+      true,
+      "check-branch",
+      "check-decl",
+      "check-operator",
+      "check-separator",
+      "check-type"
+    ],
+
+    "directive-selector": [true, "attribute", "app", "camelCase"],
+    "component-selector": [true, "element", "app", "kebab-case"],
+    "use-input-property-decorator": true,
+    "use-output-property-decorator": true,
+    "use-host-property-decorator": true,
+    "no-input-rename": true,
+    "no-output-rename": true,
+    "use-life-cycle-interface": true,
+    "use-pipe-transform-interface": true,
+    "component-class-suffix": true,
+    "directive-class-suffix": true,
+    "no-access-missing-member": true,
+    "templates-use-public": true,
+    "invoke-injectable": true
+  }
+}