diff --git a/kfet/forms.py b/kfet/forms.py
index b8d670c1..2a7f111a 100644
--- a/kfet/forms.py
+++ b/kfet/forms.py
@@ -37,8 +37,8 @@ class DateTimeWidget(forms.DateTimeInput):
self.attrs["format"] = "%Y-%m-%d %H:%M"
class Media:
- css = {"all": ("kfet/css/bootstrap-datetimepicker.min.css",)}
- js = ("kfet/js/bootstrap-datetimepicker.min.js",)
+ css = {"all": ("kfet/vendor/bootstrap/bootstrap-datetimepicker.min.css",)}
+ js = ("kfet/vendor/bootstrap/bootstrap-datetimepicker.min.js",)
# -----
diff --git a/kfet/static/kfet/js/reconnecting-websocket.js b/kfet/static/kfet/js/reconnecting-websocket.js
deleted file mode 100644
index 0cd4332d..00000000
--- a/kfet/static/kfet/js/reconnecting-websocket.js
+++ /dev/null
@@ -1,365 +0,0 @@
-// MIT License:
-//
-// Copyright (c) 2010-2012, Joe Walnes
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-/**
- * This behaves like a WebSocket in every way, except if it fails to connect,
- * or it gets disconnected, it will repeatedly poll until it successfully connects
- * again.
- *
- * It is API compatible, so when you have:
- * ws = new WebSocket('ws://....');
- * you can replace with:
- * ws = new ReconnectingWebSocket('ws://....');
- *
- * The event stream will typically look like:
- * onconnecting
- * onopen
- * onmessage
- * onmessage
- * onclose // lost connection
- * onconnecting
- * onopen // sometime later...
- * onmessage
- * onmessage
- * etc...
- *
- * It is API compatible with the standard WebSocket API, apart from the following members:
- *
- * - `bufferedAmount`
- * - `extensions`
- * - `binaryType`
- *
- * Latest version: https://github.com/joewalnes/reconnecting-websocket/
- * - Joe Walnes
- *
- * Syntax
- * ======
- * var socket = new ReconnectingWebSocket(url, protocols, options);
- *
- * Parameters
- * ==========
- * url - The url you are connecting to.
- * protocols - Optional string or array of protocols.
- * options - See below
- *
- * Options
- * =======
- * Options can either be passed upon instantiation or set after instantiation:
- *
- * var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
- *
- * or
- *
- * var socket = new ReconnectingWebSocket(url);
- * socket.debug = true;
- * socket.reconnectInterval = 4000;
- *
- * debug
- * - Whether this instance should log debug messages. Accepts true or false. Default: false.
- *
- * automaticOpen
- * - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
- *
- * reconnectInterval
- * - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000.
- *
- * maxReconnectInterval
- * - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000.
- *
- * reconnectDecay
- * - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5.
- *
- * timeoutInterval
- * - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000.
- *
- */
-(function (global, factory) {
- if (typeof define === 'function' && define.amd) {
- define([], factory);
- } else if (typeof module !== 'undefined' && module.exports){
- module.exports = factory();
- } else {
- global.ReconnectingWebSocket = factory();
- }
-})(this, function () {
-
- if (!('WebSocket' in window)) {
- return;
- }
-
- function ReconnectingWebSocket(url, protocols, options) {
-
- // Default settings
- var settings = {
-
- /** Whether this instance should log debug messages. */
- debug: false,
-
- /** Whether or not the websocket should attempt to connect immediately upon instantiation. */
- automaticOpen: true,
-
- /** The number of milliseconds to delay before attempting to reconnect. */
- reconnectInterval: 1000,
- /** The maximum number of milliseconds to delay a reconnection attempt. */
- maxReconnectInterval: 30000,
- /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */
- reconnectDecay: 1.5,
-
- /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */
- timeoutInterval: 2000,
-
- /** The maximum number of reconnection attempts to make. Unlimited if null. */
- maxReconnectAttempts: null,
-
- /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
- binaryType: 'blob'
- }
- if (!options) { options = {}; }
-
- // Overwrite and define settings with options if they exist.
- for (var key in settings) {
- if (typeof options[key] !== 'undefined') {
- this[key] = options[key];
- } else {
- this[key] = settings[key];
- }
- }
-
- // These should be treated as read-only properties
-
- /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */
- this.url = url;
-
- /** The number of attempted reconnects since starting, or the last successful connection. Read only. */
- this.reconnectAttempts = 0;
-
- /**
- * The current state of the connection.
- * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
- * Read only.
- */
- this.readyState = WebSocket.CONNECTING;
-
- /**
- * A string indicating the name of the sub-protocol the server selected; this will be one of
- * the strings specified in the protocols parameter when creating the WebSocket object.
- * Read only.
- */
- this.protocol = null;
-
- // Private state variables
-
- var self = this;
- var ws;
- var forcedClose = false;
- var timedOut = false;
- var eventTarget = document.createElement('div');
-
- // Wire up "on*" properties as event handlers
-
- eventTarget.addEventListener('open', function(event) { self.onopen(event); });
- eventTarget.addEventListener('close', function(event) { self.onclose(event); });
- eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
- eventTarget.addEventListener('message', function(event) { self.onmessage(event); });
- eventTarget.addEventListener('error', function(event) { self.onerror(event); });
-
- // Expose the API required by EventTarget
-
- this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
- this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
- this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
-
- /**
- * This function generates an event that is compatible with standard
- * compliant browsers and IE9 - IE11
- *
- * This will prevent the error:
- * Object doesn't support this action
- *
- * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
- * @param s String The name that the event should use
- * @param args Object an optional object that the event will use
- */
- function generateEvent(s, args) {
- var evt = document.createEvent("CustomEvent");
- evt.initCustomEvent(s, false, false, args);
- return evt;
- };
-
- this.open = function (reconnectAttempt) {
- ws = new WebSocket(self.url, protocols || []);
- ws.binaryType = this.binaryType;
-
- if (reconnectAttempt) {
- if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {
- return;
- }
- } else {
- eventTarget.dispatchEvent(generateEvent('connecting'));
- this.reconnectAttempts = 0;
- }
-
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
- }
-
- var localWs = ws;
- var timeout = setTimeout(function() {
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
- }
- timedOut = true;
- localWs.close();
- timedOut = false;
- }, self.timeoutInterval);
-
- ws.onopen = function(event) {
- clearTimeout(timeout);
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'onopen', self.url);
- }
- self.protocol = ws.protocol;
- self.readyState = WebSocket.OPEN;
- self.reconnectAttempts = 0;
- var e = generateEvent('open');
- e.isReconnect = reconnectAttempt;
- reconnectAttempt = false;
- eventTarget.dispatchEvent(e);
- };
-
- ws.onclose = function(event) {
- clearTimeout(timeout);
- ws = null;
- if (forcedClose) {
- self.readyState = WebSocket.CLOSED;
- eventTarget.dispatchEvent(generateEvent('close'));
- } else {
- self.readyState = WebSocket.CONNECTING;
- var e = generateEvent('connecting');
- e.code = event.code;
- e.reason = event.reason;
- e.wasClean = event.wasClean;
- eventTarget.dispatchEvent(e);
- if (!reconnectAttempt && !timedOut) {
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'onclose', self.url);
- }
- eventTarget.dispatchEvent(generateEvent('close'));
- }
-
- var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
- setTimeout(function() {
- self.reconnectAttempts++;
- self.open(true);
- }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
- }
- };
- ws.onmessage = function(event) {
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
- }
- var e = generateEvent('message');
- e.data = event.data;
- eventTarget.dispatchEvent(e);
- };
- ws.onerror = function(event) {
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
- }
- eventTarget.dispatchEvent(generateEvent('error'));
- };
- }
-
- // Whether or not to create a websocket upon instantiation
- if (this.automaticOpen == true) {
- this.open(false);
- }
-
- /**
- * Transmits data to the server over the WebSocket connection.
- *
- * @param data a text string, ArrayBuffer or Blob to send to the server.
- */
- this.send = function(data) {
- if (ws) {
- if (self.debug || ReconnectingWebSocket.debugAll) {
- console.debug('ReconnectingWebSocket', 'send', self.url, data);
- }
- return ws.send(data);
- } else {
- throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
- }
- };
-
- /**
- * Closes the WebSocket connection or connection attempt, if any.
- * If the connection is already CLOSED, this method does nothing.
- */
- this.close = function(code, reason) {
- // Default CLOSE_NORMAL code
- if (typeof code == 'undefined') {
- code = 1000;
- }
- forcedClose = true;
- if (ws) {
- ws.close(code, reason);
- }
- };
-
- /**
- * Additional public API method to refresh the connection if still open (close, re-open).
- * For example, if the app suspects bad data / missed heart beats, it can try to refresh.
- */
- this.refresh = function() {
- if (ws) {
- ws.close();
- }
- };
- }
-
- /**
- * An event listener to be called when the WebSocket connection's readyState changes to OPEN;
- * this indicates that the connection is ready to send and receive data.
- */
- ReconnectingWebSocket.prototype.onopen = function(event) {};
- /** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */
- ReconnectingWebSocket.prototype.onclose = function(event) {};
- /** An event listener to be called when a connection begins being attempted. */
- ReconnectingWebSocket.prototype.onconnecting = function(event) {};
- /** An event listener to be called when a message is received from the server. */
- ReconnectingWebSocket.prototype.onmessage = function(event) {};
- /** An event listener to be called when an error occurs. */
- ReconnectingWebSocket.prototype.onerror = function(event) {};
-
- /**
- * Whether all instances of ReconnectingWebSocket should log debug messages.
- * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true.
- */
- ReconnectingWebSocket.debugAll = false;
-
- ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;
- ReconnectingWebSocket.OPEN = WebSocket.OPEN;
- ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;
- ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;
-
- return ReconnectingWebSocket;
-});
diff --git a/kfet/static/kfet/css/bootstrap-datetimepicker.min.css b/kfet/static/kfet/vendor/bootstrap/bootstrap-datetimepicker.min.css
similarity index 100%
rename from kfet/static/kfet/css/bootstrap-datetimepicker.min.css
rename to kfet/static/kfet/vendor/bootstrap/bootstrap-datetimepicker.min.css
diff --git a/kfet/static/kfet/js/bootstrap-datetimepicker.min.js b/kfet/static/kfet/vendor/bootstrap/bootstrap-datetimepicker.min.js
similarity index 100%
rename from kfet/static/kfet/js/bootstrap-datetimepicker.min.js
rename to kfet/static/kfet/vendor/bootstrap/bootstrap-datetimepicker.min.js
diff --git a/kfet/static/kfet/css/jquery-confirm.css b/kfet/static/kfet/vendor/jquery/jquery-confirm.css
similarity index 100%
rename from kfet/static/kfet/css/jquery-confirm.css
rename to kfet/static/kfet/vendor/jquery/jquery-confirm.css
diff --git a/kfet/static/kfet/js/jquery-confirm.js b/kfet/static/kfet/vendor/jquery/jquery-confirm.js
similarity index 100%
rename from kfet/static/kfet/js/jquery-confirm.js
rename to kfet/static/kfet/vendor/jquery/jquery-confirm.js
diff --git a/kfet/static/kfet/vendor/jquery/jquery-confirm.min.css b/kfet/static/kfet/vendor/jquery/jquery-confirm.min.css
new file mode 100644
index 00000000..cb8c3245
--- /dev/null
+++ b/kfet/static/kfet/vendor/jquery/jquery-confirm.min.css
@@ -0,0 +1,9 @@
+/*!
+ * jquery-confirm v2.5.1 (http://craftpip.github.io/jquery-confirm/)
+ * Author: boniface pereira
+ * Website: www.craftpip.com
+ * Contact: hey@craftpip.com
+ *
+ * Copyright 2013-2016 jquery-confirm
+ * Licensed under MIT (https://github.com/craftpip/jquery-confirm/blob/master/LICENSE)
+ */@-webkit-keyframes jconfirm-rotate{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes jconfirm-rotate{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.jconfirm{position:fixed;top:0;left:0;right:0;bottom:0;z-index:99999999;font-family:inherit;overflow:hidden}.jconfirm .jconfirm-bg{position:fixed;top:0;left:0;right:0;bottom:0;opacity:0;-webkit-transition:all .4s;transition:all .4s}.jconfirm .jconfirm-bg.seen{opacity:1}.jconfirm .jconfirm-scrollpane{position:fixed;top:0;left:0;right:0;bottom:0;overflow-y:auto;-webkit-perspective:500px;perspective:500px;-webkit-perspective-origin:center;perspective-origin:center}.jconfirm .jconfirm-box{background:#fff;border-radius:4px;position:relative;outline:none;padding:15px 15px 0}.jconfirm .jconfirm-box div.closeIcon{height:20px;width:20px;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.6;text-align:center;-webkit-transition:opacity .1s ease-in;transition:opacity .1s ease-in;display:none;font-size:27px;line-height:14px}.jconfirm .jconfirm-box div.closeIcon .fa{font-size:16px}.jconfirm .jconfirm-box div.closeIcon .glyphicon{font-size:16px}.jconfirm .jconfirm-box div.closeIcon .zmdi{font-size:16px}.jconfirm .jconfirm-box div.closeIcon:hover{opacity:1}.jconfirm .jconfirm-box div.title-c{display:block;font-size:22px;line-height:20px}.jconfirm .jconfirm-box div.title-c .icon-c{font-size:inherit;padding-bottom:15px;display:inline-block;margin-right:8px;vertical-align:middle}.jconfirm .jconfirm-box div.title-c .icon-c i{vertical-align:middle}.jconfirm .jconfirm-box div.title-c .icon-c:empty{display:none}.jconfirm .jconfirm-box div.title-c .title{font-size:inherit;font-family:inherit;display:inline-block;vertical-align:middle;padding-bottom:15px}.jconfirm .jconfirm-box div.title-c .title:empty{display:none}.jconfirm .jconfirm-box div.content-pane{margin-bottom:15px;height:auto;-webkit-transition:height .4s ease-in;transition:height .4s ease-in;display:inline-block;width:100%;position:relative}.jconfirm .jconfirm-box div.content-pane .content{position:absolute;top:0;left:0;-webkit-transition:all .2s ease-in;transition:all .2s ease-in;right:0}.jconfirm .jconfirm-box div.content-pane .content img{width:100%;height:auto}.jconfirm .jconfirm-box div.content-pane .content:empty{display:none}.jconfirm .jconfirm-box div.content-pane .content:empty.loading{height:40px;position:relative;opacity:.6;display:block}.jconfirm .jconfirm-box div.content-pane .content:empty.loading:before{content:'';height:20px;width:20px;border:solid 2px transparent;position:absolute;left:50%;margin-left:-10px;border-radius:50%;-webkit-animation:jconfirm-rotate 1s infinite linear;animation:jconfirm-rotate 1s infinite linear;border-bottom-color:#aaa;top:50%;margin-top:-10px}.jconfirm .jconfirm-box div.content-pane .content:empty.loading:after{content:'';position:absolute;left:50%;margin-left:-15px}.jconfirm .jconfirm-box .buttons{padding-bottom:15px}.jconfirm .jconfirm-box .buttons button+button{margin-left:5px}.jconfirm .jquery-clear{clear:both}.jconfirm.rtl{direction:rtl}.jconfirm.rtl div.closeIcon{left:12px;right:auto}.jconfirm.jconfirm-white .jconfirm-bg{background-color:rgba(0,0,0,0.2)}.jconfirm.jconfirm-white .jconfirm-box{box-shadow:0 2px 6px rgba(0,0,0,0.2);border-radius:5px}.jconfirm.jconfirm-white .jconfirm-box .buttons{float:right}.jconfirm.jconfirm-white .jconfirm-box .buttons button{border:none;background-image:none;text-transform:uppercase;font-size:14px;font-weight:bold;text-shadow:none;-webkit-transition:background .1s;transition:background .1s;color:#fff}.jconfirm.jconfirm-white .jconfirm-box .buttons button.btn-default{box-shadow:none;color:#333}.jconfirm.jconfirm-white .jconfirm-box .buttons button.btn-default:hover{background:#ddd}.jconfirm.jconfirm-black .jconfirm-bg{background-color:rgba(0,0,0,0.5)}.jconfirm.jconfirm-black .jconfirm-box{box-shadow:0 2px 6px rgba(0,0,0,0.2);background:#444;border-radius:5px;color:#fff}.jconfirm.jconfirm-black .jconfirm-box .buttons{float:right}.jconfirm.jconfirm-black .jconfirm-box .buttons button{border:none;background-image:none;text-transform:uppercase;font-size:14px;font-weight:bold;text-shadow:none;-webkit-transition:background .1s;transition:background .1s;color:#fff}.jconfirm.jconfirm-black .jconfirm-box .buttons button.btn-default{box-shadow:none;color:#fff;background:none}.jconfirm.jconfirm-black .jconfirm-box .buttons button.btn-default:hover{background:#666}.jconfirm .jconfirm-box.hilight{-webkit-animation:hilight .82s cubic-bezier(.36, .07, .19, .97) both;animation:hilight .82s cubic-bezier(.36, .07, .19, .97) both;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}@-webkit-keyframes hilight{10%,90%{-webkit-transform:translate3d(-2px, 0, 0);transform:translate3d(-2px, 0, 0)}20%,80%{-webkit-transform:translate3d(4px, 0, 0);transform:translate3d(4px, 0, 0)}30%,50%,70%{-webkit-transform:translate3d(-8px, 0, 0);transform:translate3d(-8px, 0, 0)}40%,60%{-webkit-transform:translate3d(8px, 0, 0);transform:translate3d(8px, 0, 0)}}@keyframes hilight{10%,90%{-webkit-transform:translate3d(-2px, 0, 0);transform:translate3d(-2px, 0, 0)}20%,80%{-webkit-transform:translate3d(4px, 0, 0);transform:translate3d(4px, 0, 0)}30%,50%,70%{-webkit-transform:translate3d(-8px, 0, 0);transform:translate3d(-8px, 0, 0)}40%,60%{-webkit-transform:translate3d(8px, 0, 0);transform:translate3d(8px, 0, 0)}}.jconfirm{-webkit-perspective:400px;perspective:400px}.jconfirm .jconfirm-box{opacity:1;-webkit-transition-property:-webkit-transform,opacity,box-shadow;transition-property:transform,opacity,box-shadow}.jconfirm .jconfirm-box.anim-top,.jconfirm .jconfirm-box.anim-left,.jconfirm .jconfirm-box.anim-right,.jconfirm .jconfirm-box.anim-bottom,.jconfirm .jconfirm-box.anim-opacity,.jconfirm .jconfirm-box.anim-zoom,.jconfirm .jconfirm-box.anim-scale,.jconfirm .jconfirm-box.anim-none,.jconfirm .jconfirm-box.anim-rotate,.jconfirm .jconfirm-box.anim-rotatex,.jconfirm .jconfirm-box.anim-rotatey,.jconfirm .jconfirm-box.anim-scaley,.jconfirm .jconfirm-box.anim-scalex{opacity:0}.jconfirm .jconfirm-box.anim-rotate{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.jconfirm .jconfirm-box.anim-rotatex{-webkit-transform:rotateX(90deg);transform:rotateX(90deg);-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.anim-rotatexr{-webkit-transform:rotateX(-90deg);transform:rotateX(-90deg);-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.anim-rotatey{-webkit-transform:rotatey(90deg);-ms-transform:rotatey(90deg);transform:rotatey(90deg);-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.anim-rotateyr{-webkit-transform:rotatey(-90deg);-ms-transform:rotatey(-90deg);transform:rotatey(-90deg);-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.anim-scaley{-webkit-transform:scaley(1.5);-ms-transform:scaley(1.5);transform:scaley(1.5);-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.anim-scalex{-webkit-transform:scalex(1.5);-ms-transform:scalex(1.5);transform:scalex(1.5);-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.anim-top{-webkit-transform:translate(0, -100px);-ms-transform:translate(0, -100px);transform:translate(0, -100px)}.jconfirm .jconfirm-box.anim-left{-webkit-transform:translate(-100px, 0);-ms-transform:translate(-100px, 0);transform:translate(-100px, 0)}.jconfirm .jconfirm-box.anim-right{-webkit-transform:translate(100px, 0);-ms-transform:translate(100px, 0);transform:translate(100px, 0)}.jconfirm .jconfirm-box.anim-bottom{-webkit-transform:translate(0, 100px);-ms-transform:translate(0, 100px);transform:translate(0, 100px)}.jconfirm .jconfirm-box.anim-zoom{-webkit-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}.jconfirm .jconfirm-box.anim-scale{-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}.jconfirm .jconfirm-box.anim-none{display:none}.jconfirm.jconfirm-supervan .jconfirm-bg{background-color:rgba(54,70,93,0.95)}.jconfirm.jconfirm-supervan .jconfirm-box{background-color:transparent}.jconfirm.jconfirm-supervan .jconfirm-box div.closeIcon{color:#fff}.jconfirm.jconfirm-supervan .jconfirm-box div.title-c{text-align:center;color:#fff;font-size:28px;font-weight:normal}.jconfirm.jconfirm-supervan .jconfirm-box div.title-c>*{padding-bottom:25px}.jconfirm.jconfirm-supervan .jconfirm-box div.content-pane{margin-bottom:25px}.jconfirm.jconfirm-supervan .jconfirm-box div.content{text-align:center;color:#fff}.jconfirm.jconfirm-supervan .jconfirm-box .buttons{text-align:center}.jconfirm.jconfirm-supervan .jconfirm-box .buttons button{font-size:16px;border-radius:2px;background:#303f53;text-shadow:none;border:none;color:#fff;padding:10px;min-width:100px}.jconfirm.jconfirm-material .jconfirm-bg{background-color:rgba(0,0,0,0.67)}.jconfirm.jconfirm-material .jconfirm-box{background-color:#fff;box-shadow:0 7px 8px -4px rgba(0,0,0,0.2),0 13px 19px 2px rgba(0,0,0,0.14),0 5px 24px 4px rgba(0,0,0,0.12);padding:30px 25px 10px 25px}.jconfirm.jconfirm-material .jconfirm-box div.closeIcon{color:rgba(0,0,0,0.87)}.jconfirm.jconfirm-material .jconfirm-box div.title-c{color:rgba(0,0,0,0.87);font-size:22px;font-weight:bold}.jconfirm.jconfirm-material .jconfirm-box div.content{text-align:left;color:rgba(0,0,0,0.87)}.jconfirm.jconfirm-material .jconfirm-box .buttons{text-align:right}.jconfirm.jconfirm-material .jconfirm-box .buttons button{text-transform:uppercase;font-weight:500}.jconfirm.jconfirm-bootstrap .jconfirm-bg{background-color:rgba(0,0,0,0.21)}.jconfirm.jconfirm-bootstrap .jconfirm-box{background-color:#fff;box-shadow:0 3px 8px 0 rgba(0,0,0,0.2);border:solid 1px rgba(0,0,0,0.4);padding:15px 0 0}.jconfirm.jconfirm-bootstrap .jconfirm-box div.closeIcon{color:rgba(0,0,0,0.87)}.jconfirm.jconfirm-bootstrap .jconfirm-box div.title-c{color:rgba(0,0,0,0.87);font-size:22px;font-weight:bold;padding-left:15px;padding-right:15px}.jconfirm.jconfirm-bootstrap .jconfirm-box div.content{text-align:left;color:rgba(0,0,0,0.87);padding:0 15px}.jconfirm.jconfirm-bootstrap .jconfirm-box .buttons{text-align:right;padding:0 0 0;margin:-5px 0 0;border-top:solid 1px #ddd;overflow:hidden;border-radius:0 0 4px 4px}.jconfirm.jconfirm-bootstrap .jconfirm-box .buttons button{font-weight:500;border-radius:0;margin:0;border-left:solid 1px #ddd}
\ No newline at end of file
diff --git a/kfet/static/kfet/vendor/jquery/jquery-confirm.min.js b/kfet/static/kfet/vendor/jquery/jquery-confirm.min.js
new file mode 100644
index 00000000..f766c1fc
--- /dev/null
+++ b/kfet/static/kfet/vendor/jquery/jquery-confirm.min.js
@@ -0,0 +1 @@
+if("undefined"==typeof jQuery)throw new Error("jquery-confirm requires jQuery");var jconfirm,Jconfirm;!function(t){"use strict";t.fn.confirm=function(n,i){return void 0===n&&(n={}),"string"==typeof n&&(n={content:n,title:i||!1}),t(this).each(function(){var i=t(this);i.on("click",function(e){e.preventDefault();var o=t.extend({},n);i.attr("data-title")&&(o.title=i.attr("data-title")),i.attr("data-content")&&(o.content=i.attr("data-content")),o.$target=i,i.attr("href")&&!n.confirm&&(o.confirm=function(){location.href=i.attr("href")}),t.confirm(o)})}),t(this)},t.confirm=function(t,n){return void 0===t&&(t={}),"string"==typeof t&&(t={content:t,title:n||!1}),jconfirm(t)},t.alert=function(t,n){return void 0===t&&(t={}),"string"==typeof t&&(t={content:t,title:n||!1}),t.cancelButton=!1,jconfirm(t)},t.dialog=function(t,n){return void 0===t&&(t={}),"string"==typeof t&&(t={content:t,title:n||!1}),t.cancelButton=!1,t.confirmButton=!1,t.confirmKeys=[13],jconfirm(t)},jconfirm=function(n){void 0===n&&(n={}),jconfirm.defaults&&t.extend(jconfirm.pluginDefaults,jconfirm.defaults);n=t.extend({},jconfirm.pluginDefaults,n);return new Jconfirm(n)},(Jconfirm=function(n){t.extend(this,n),this._init()}).prototype={_init:function(){var t=this;this._rand=Math.round(99999*Math.random()),this._buildHTML(),this._bindEvents(),setTimeout(function(){t.open(),t._watchContent()},0)},_buildHTML:function(){var n=this;this.animation="anim-"+this.animation.toLowerCase(),this.closeAnimation="anim-"+this.closeAnimation.toLowerCase(),this.theme="jconfirm-"+this.theme.toLowerCase(),"anim-none"==this.animation&&(this.animationSpeed=0),this._lastFocused=t("body").find(":focus"),this.$el=t(this.template).appendTo(this.container).addClass(this.theme),this.$el.find(".jconfirm-box-container").addClass(this.columnClass),this.$el.find(".jconfirm-bg").css(this._getCSS(this.animationSpeed,1)),this.$el.find(".jconfirm-bg").css("opacity",this.opacity),this.$b=this.$el.find(".jconfirm-box").css(this._getCSS(this.animationSpeed,this.animationBounce)).addClass(this.animation),this.$body=this.$b,this.rtl&&this.$el.addClass("rtl"),this._contentReady=t.Deferred(),this._modalReady=t.Deferred(),this.$title=this.$el.find(".title"),this.contentDiv=this.$el.find("div.content"),this.$content=this.contentDiv,this.$contentPane=this.$el.find(".content-pane"),this.$icon=this.$el.find(".icon-c"),this.$closeIcon=this.$el.find(".closeIcon"),this.$contentPane.css(this._getCSS(this.animationSpeed,1)),this.setTitle(),this.setIcon(),this._setButtons(),this.closeIconClass&&this.$closeIcon.html(''),n._contentHash=this._hash(n.$content.html()),t.when(this._contentReady,this._modalReady).then(function(){n.setContent(),n.setTitle(),n.setIcon()}),this._getContent(),this._imagesLoaded(),this.autoClose&&this._startCountDown()},_unwatchContent:function(){clearInterval(this._timer)},_hash:function(){return btoa(encodeURIComponent(this.$content.html()))},_watchContent:function(){var t=this;this._timer=setInterval(function(){var n=t._hash(t.$content.html());t._contentHash!=n&&(t._contentHash=n,t.setDialogCenter(),t._imagesLoaded())},this.watchInterval)},_bindEvents:function(){var n=this,i=!1;this.$el.find(".jconfirm-scrollpane").click(function(t){i||(n.backgroundDismiss?(n.cancel(),n.close()):(n.$b.addClass("hilight"),setTimeout(function(){n.$b.removeClass("hilight")},800))),i=!1}),this.$el.find(".jconfirm-box").click(function(t){i=!0}),this.$confirmButton&&this.$confirmButton.click(function(t){t.preventDefault();var i=n.confirm(n.$b);n._stopCountDown(),n.onAction("confirm"),(void 0===i||i)&&n.close()}),this.$cancelButton&&this.$cancelButton.click(function(t){t.preventDefault();var i=n.cancel(n.$b);n._stopCountDown(),n.onAction("cancel"),(void 0===i||i)&&n.close()}),this.$closeButton&&this.$closeButton.click(function(t){t.preventDefault(),n._stopCountDown(),n.cancel(),n.onAction("close"),n.close()}),this.keyboardEnabled&&setTimeout(function(){t(window).on("keyup."+this._rand,function(t){n.reactOnKey(t)})},500),t(window).on("resize."+this._rand,function(){n.setDialogCenter(!0)})},_getCSS:function(t,n){return{"-webkit-transition-duration":t/1e3+"s","transition-duration":t/1e3+"s","-webkit-transition-timing-function":"cubic-bezier(.36,1.1,.2, "+n+")","transition-timing-function":"cubic-bezier(.36,1.1,.2, "+n+")"}},_imagesLoaded:function(){var n=this;t.each(this.$content.find("img:not(.loaded)"),function(i,e){var o=setInterval(function(){"0px"!==t(e).css("height")&&(t(e).addClass("loaded"),n.setDialogCenter(),clearInterval(o))},40)})},_setButtons:function(){this.$btnc=this.$el.find(".buttons"),this.confirmButton&&""!==t.trim(this.confirmButton)&&(this.$confirmButton=t('").appendTo(this.$btnc).addClass(this.confirmButtonClass)),this.cancelButton&&""!==t.trim(this.cancelButton)&&(this.$cancelButton=t('").appendTo(this.$btnc).addClass(this.cancelButtonClass)),this.confirmButton||this.cancelButton||this.$btnc.hide(),this.confirmButton||this.cancelButton||null!==this.closeIcon||(this.$closeButton=this.$b.find(".closeIcon").show()),!0===this.closeIcon&&(this.$closeButton=this.$b.find(".closeIcon").show())},setTitle:function(t){this.title=void 0!==t?t:this.title,this.$title.html(this.title||"")},setIcon:function(t){this.title="undefined"!=typeof string?t:this.title,this.$icon.html(this.icon?'':"")},setContent:function(t){this.content=void 0===t?this.content:t,""==this.content?(this.$content.html(this.content),this.$contentPane.hide()):(this.$content.html(this.content),this.$contentPane.show()),this.$content.hasClass("loading")&&(this.$content.removeClass("loading"),this.$btnc.find("button").prop("disabled",!1))},_getContent:function(n){var i=this;if(n=n||this.content,this._isAjax=!1,this.content)if("string"==typeof this.content)if("url:"===this.content.substr(0,4).toLowerCase()){this._isAjax=!0,this.$content.addClass("loading"),this.$btnc.find("button").prop("disabled",!0);var e=this.content.substring(4,this.content.length);t.get(e).done(function(t){i.content=t,i._contentReady.resolve()}).always(function(t,n,e){"function"==typeof i.contentLoaded&&i.contentLoaded(t,n,e)})}else this.setContent(this.content),this._contentReady.reject();else if("function"==typeof this.content){this.$content.addClass("loading"),this.$btnc.find("button").attr("disabled","disabled");var o=this.content(this);"object"!=typeof o?console.error("The content function must return jquery promise."):"function"!=typeof o.always?console.error("The object returned is not a jquery promise."):(this._isAjax=!0,o.always(function(t,n){i._contentReady.resolve()}))}else console.error("Invalid option for property content, passed: "+typeof this.content);else this.content="",this.setContent(this.content),this._contentReady.reject();this.setDialogCenter()},_stopCountDown:function(){clearInterval(this.timerInterval),this.$cd&&this.$cd.remove()},_startCountDown:function(){var n=this.autoClose.split("|");if(/cancel/.test(n[0])&&"alert"===this.type)return!1;if(/confirm|cancel/.test(n[0])){this.$cd=t('').appendTo(this["$"+n[0]+"Button"]);var i=this;i.$cd.parent().click();var e=n[1]/1e3;this.timerInterval=setInterval(function(){i.$cd.html(" ("+(e-=1)+")"),0===e&&(i.$cd.html(""),i.$cd.parent().trigger("click"),clearInterval(i.timerInterval))},1e3)}else console.error("Invalid option "+n[0]+", must be confirm/cancel")},reactOnKey:function(n){var i=t(".jconfirm");if(i.eq(i.length-1)[0]!==this.$el[0])return!1;var e=n.which;if(this.contentDiv.find(":input").is(":focus")&&/13|32/.test(e))return!1;-1!==t.inArray(e,this.cancelKeys)&&(this.$cancelButton?this.$cancelButton.click():this.close())},setDialogCenter:function(){if("none"==this.$contentPane.css("display"))var n=0,i=0;else{n=this.$content.outerHeight();0==(i=this.$contentPane.height())&&(i=n)}var e=this.$content.outerWidth();this.$content.css({clip:"rect(0px "+(100+e)+"px "+n+"px -100px)"}),this.$contentPane.css({height:n});var o=t(window).height(),s=this.$b.outerHeight()-i+n,c=(o-s)/2;if(s>o-100){var a={"margin-top":50,"margin-bottom":50};t("body").addClass("jconfirm-noscroll")}else{a={"margin-top":c};t("body").removeClass("jconfirm-noscroll")}this.$b.css(a)},close:function(){var n=this;if(this.isClosed())return!1;"function"==typeof this.onClose&&this.onClose(),this._unwatchContent(),n._lastFocused.focus(),t(window).unbind("resize."+this._rand),this.keyboardEnabled&&t(window).unbind("keyup."+this._rand),n.$el.find(".jconfirm-bg").removeClass("seen"),t("body").removeClass("jconfirm-noscroll"),this.$b.addClass(this.closeAnimation);var i="anim-none"==this.closeAnimation?0:this.animationSpeed;return setTimeout(function(){n.$el.remove()},25*i/100),jconfirm.record.closed+=1,jconfirm.record.currentlyOpen-=1,!0},open:function(){var t=this;if(this.isClosed())return!1;t.$el.find(".jconfirm-bg").addClass("seen"),this.$b.removeClass(this.animation),jconfirm.record.opened+=1,jconfirm.record.currentlyOpen+=1,"function"==typeof this.onOpen&&this.onOpen();var n="jconfirm-box"+this._rand;return this.$b.attr("aria-labelledby",n).attr("tabindex",-1).focus(),this.$b.find("input[autofocus]:visible:first").focus(),this.$title?this.$title.attr("id",n):this.$content&&this.$content.attr("id",n),setTimeout(function(){t.$b.css({"transition-property":t.$b.css("transition-property")+", margin"}),t._modalReady.resolve()},this.animationSpeed),!0},isClosed:function(){return""===this.$el.css("display")}},jconfirm.pluginDefaults={template:'
×
',title:"Hello",content:"Are you sure to continue?",contentLoaded:function(){},icon:"",opacity:.2,confirmButton:"Okay",cancelButton:"Close",confirmButtonClass:"btn-default",cancelButtonClass:"btn-default",theme:"white",animation:"zoom",closeAnimation:"scale",animationSpeed:500,animationBounce:1.2,keyboardEnabled:!1,rtl:!1,confirmKeys:[13],cancelKeys:[27],container:"body",confirm:function(){},cancel:function(){},backgroundDismiss:!1,autoClose:!1,closeIcon:null,closeIconClass:!1,watchInterval:100,columnClass:"col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3 col-xs-10 col-xs-offset-1",onOpen:function(){},onClose:function(){},onAction:function(){}},jconfirm.record={opened:0,closed:0,currentlyOpen:0}}(jQuery);
\ No newline at end of file
diff --git a/kfet/static/kfet/vendor/jquery-tablesorter/jquery.tablesorter.combined.js b/kfet/static/kfet/vendor/jquery/jquery.tablesorter.combined.js
similarity index 100%
rename from kfet/static/kfet/vendor/jquery-tablesorter/jquery.tablesorter.combined.js
rename to kfet/static/kfet/vendor/jquery/jquery.tablesorter.combined.js
diff --git a/kfet/static/kfet/vendor/jquery/jquery.tablesorter.combined.min.js b/kfet/static/kfet/vendor/jquery/jquery.tablesorter.combined.min.js
new file mode 100644
index 00000000..0efd971c
--- /dev/null
+++ b/kfet/static/kfet/vendor/jquery/jquery.tablesorter.combined.min.js
@@ -0,0 +1,4 @@
+(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery){
+
+/*! tablesorter (FORK) - updated 2018-11-20 (v2.31.1)*/
+!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof module&&"object"==typeof module.exports?module.exports=e(require("jquery")):e(jQuery)}(function(e){return function(R){"use strict";var T=R.tablesorter={version:"2.31.1",parsers:[],widgets:[],defaults:{theme:"default",widthFixed:!1,showProcessing:!1,headerTemplate:"{content}",onRenderTemplate:null,onRenderHeader:null,cancelSelection:!0,tabIndex:!0,dateFormat:"mmddyyyy",sortMultiSortKey:"shiftKey",sortResetKey:"ctrlKey",usNumberFormat:!0,delayInit:!1,serverSideSorting:!1,resort:!0,headers:{},ignoreCase:!0,sortForce:null,sortList:[],sortAppend:null,sortStable:!1,sortInitialOrder:"asc",sortLocaleCompare:!1,sortReset:!1,sortRestart:!1,emptyTo:"bottom",stringTo:"max",duplicateSpan:!0,textExtraction:"basic",textAttribute:"data-text",textSorter:null,numberSorter:null,initWidgets:!0,widgetClass:"widget-{name}",widgets:[],widgetOptions:{zebra:["even","odd"]},initialized:null,tableClass:"",cssAsc:"",cssDesc:"",cssNone:"",cssHeader:"",cssHeaderRow:"",cssProcessing:"",cssChildRow:"tablesorter-childRow",cssInfoBlock:"tablesorter-infoOnly",cssNoSort:"tablesorter-noSort",cssIgnoreRow:"tablesorter-ignoreRow",cssIcon:"tablesorter-icon",cssIconNone:"",cssIconAsc:"",cssIconDesc:"",cssIconDisabled:"",pointerClick:"click",pointerDown:"mousedown",pointerUp:"mouseup",selectorHeaders:"> thead th, > thead td",selectorSort:"th, td",selectorRemove:".remove-me",debug:!1,headerList:[],empties:{},strings:{},parsers:[],globalize:0,imgAttr:0},css:{table:"tablesorter",cssHasChild:"tablesorter-hasChildRow",childRow:"tablesorter-childRow",colgroup:"tablesorter-colgroup",header:"tablesorter-header",headerRow:"tablesorter-headerRow",headerIn:"tablesorter-header-inner",icon:"tablesorter-icon",processing:"tablesorter-processing",sortAsc:"tablesorter-headerAsc",sortDesc:"tablesorter-headerDesc",sortNone:"tablesorter-headerUnSorted"},language:{sortAsc:"Ascending sort applied, ",sortDesc:"Descending sort applied, ",sortNone:"No sort applied, ",sortDisabled:"sorting is disabled",nextAsc:"activate to apply an ascending sort",nextDesc:"activate to apply a descending sort",nextNone:"activate to remove the sort"},regex:{templateContent:/\{content\}/g,templateIcon:/\{icon\}/g,templateName:/\{name\}/i,spaces:/\s+/g,nonWord:/\W/g,formElements:/(input|select|button|textarea)/i,chunk:/(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi,chunks:/(^\\0|\\0$)/,hex:/^0x[0-9a-f]+$/i,comma:/,/g,digitNonUS:/[\s|\.]/g,digitNegativeTest:/^\s*\([.\d]+\)/,digitNegativeReplace:/^\s*\(([.\d]+)\)/,digitTest:/^[\-+(]?\d+[)]?$/,digitReplace:/[,.'"\s]/g},string:{max:1,min:-1,emptymin:1,emptymax:-1,zero:0,none:0,"null":0,top:!0,bottom:!1},keyCodes:{enter:13},dates:{},instanceMethods:{},setup:function(t,r){if(t&&t.tHead&&0!==t.tBodies.length&&!0!==t.hasInitialized){var e,a="",s=R(t),i=R.metadata;t.hasInitialized=!1,t.isProcessing=!0,t.config=r,R.data(t,"tablesorter",r),T.debug(r,"core")&&(console[console.group?"group":"log"]("Initializing tablesorter v"+T.version),R.data(t,"startoveralltimer",new Date)),r.supportsDataObject=((e=R.fn.jquery.split("."))[0]=parseInt(e[0],10),1':"",l.$headers=R(R.map(l.$table.find(l.selectorHeaders),function(e,t){var r,a,s,i,o,n=R(e);if(!T.getClosest(n,"tr").hasClass(l.cssIgnoreRow))return/(th|td)/i.test(e.nodeName)||(o=T.getClosest(n,"th, td"),n.attr("data-column",o.attr("data-column"))),r=T.getColumnData(l.table,l.headers,t,!0),l.headerContent[t]=n.html(),""===l.headerTemplate||n.find("."+T.css.headerIn).length||(i=l.headerTemplate.replace(T.regex.templateContent,n.html()).replace(T.regex.templateIcon,n.find("."+T.css.icon).length?"":c),l.onRenderTemplate&&(a=l.onRenderTemplate.apply(n,[t,i]))&&"string"==typeof a&&(i=a),n.html('