custom mentions
This commit is contained in:
parent
b8e2136cb7
commit
de1d41da05
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -1,9 +1,3 @@
|
|||||||
public/static/css/animate.min.css linguist-vendored
|
public/static/css/animate.min.css linguist-vendored
|
||||||
public/static/css/bootstrap.min.css linguist-vendored
|
public/static/css/bootstrap.min.css linguist-vendored
|
||||||
public/static/css/light-bootstrap-dashboard.css linguist-vendored
|
public/static/css/light-bootstrap-dashboard.css linguist-vendored
|
||||||
public/static/js/bootstrap-notify.js linguist-vendored
|
|
||||||
public/static/js/bootstrap-select.js linguist-vendored
|
|
||||||
public/static/js/bootstrap.min.js linguist-vendored
|
|
||||||
public/static/js/chartist.min.js linguist-vendored
|
|
||||||
public/static/js/jquery.3.2.1.min.js linguist-vendored
|
|
||||||
public/static/js/light-bootstrap-dashboard.js linguist-vendored
|
|
||||||
|
@ -125,6 +125,40 @@ func CreatePanel(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert role mention data
|
||||||
|
// string is role ID or "user" to mention the ticket opener
|
||||||
|
for _, mention := range data.Mentions {
|
||||||
|
if mention == "user" {
|
||||||
|
if err = dbclient.Client.PanelUserMention.Set(msgId, true); err != nil {
|
||||||
|
ctx.AbortWithStatusJSON(500, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
roleId, err := strconv.ParseUint(mention, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.AbortWithStatusJSON(500, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// should we check the role is a valid role in the guild?
|
||||||
|
// not too much of an issue if it isnt
|
||||||
|
|
||||||
|
if err = dbclient.Client.PanelRoleMentions.Add(msgId, roleId); err != nil {
|
||||||
|
ctx.AbortWithStatusJSON(500, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.JSON(200, gin.H{
|
ctx.JSON(200, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message_id": strconv.FormatUint(msgId, 10),
|
"message_id": strconv.FormatUint(msgId, 10),
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/TicketsBot/GoPanel/database"
|
"github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type panel struct {
|
type panel struct {
|
||||||
@ -14,6 +17,7 @@ type panel struct {
|
|||||||
CategoryId uint64 `json:"category_id,string"`
|
CategoryId uint64 `json:"category_id,string"`
|
||||||
Emote string `json:"emote"`
|
Emote string `json:"emote"`
|
||||||
WelcomeMessage *string `json:"welcome_message"`
|
WelcomeMessage *string `json:"welcome_message"`
|
||||||
|
Mentions []string `json:"mentions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListPanels(ctx *gin.Context) {
|
func ListPanels(ctx *gin.Context) {
|
||||||
@ -30,17 +34,56 @@ func ListPanels(ctx *gin.Context) {
|
|||||||
|
|
||||||
wrapped := make([]panel, len(panels))
|
wrapped := make([]panel, len(panels))
|
||||||
|
|
||||||
|
// we will need to lookup role mentions
|
||||||
|
group, _ := errgroup.WithContext(context.Background())
|
||||||
|
|
||||||
for i, p := range panels {
|
for i, p := range panels {
|
||||||
|
group.Go(func() (err error) {
|
||||||
|
var mentions []string
|
||||||
|
|
||||||
|
// get role mentions
|
||||||
|
roles, err := database.Client.PanelRoleMentions.GetRoles(p.MessageId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to strings
|
||||||
|
for _, roleId := range roles {
|
||||||
|
mentions = append(mentions, strconv.FormatUint(roleId, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get if we should mention the ticket opener
|
||||||
|
shouldMention, err := database.Client.PanelUserMention.ShouldMentionUser(p.MessageId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldMention {
|
||||||
|
mentions = append(mentions, "user")
|
||||||
|
}
|
||||||
|
|
||||||
wrapped[i] = panel{
|
wrapped[i] = panel{
|
||||||
ChannelId: p.ChannelId,
|
|
||||||
MessageId: p.MessageId,
|
MessageId: p.MessageId,
|
||||||
|
ChannelId: p.ChannelId,
|
||||||
Title: p.Title,
|
Title: p.Title,
|
||||||
Content: p.Content,
|
Content: p.Content,
|
||||||
Colour: uint32(p.Colour),
|
Colour: uint32(p.Colour),
|
||||||
CategoryId: p.TargetCategory,
|
CategoryId: p.TargetCategory,
|
||||||
Emote: p.ReactionEmote,
|
Emote: p.ReactionEmote,
|
||||||
WelcomeMessage: p.WelcomeMessage,
|
WelcomeMessage: p.WelcomeMessage,
|
||||||
|
Mentions: mentions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := group.Wait(); err != nil {
|
||||||
|
ctx.JSON(500, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, wrapped)
|
ctx.JSON(200, wrapped)
|
||||||
|
17
app/http/endpoints/api/roles.go
Normal file
17
app/http/endpoints/api/roles.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TicketsBot/GoPanel/rpc/cache"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RolesHandler(ctx *gin.Context) {
|
||||||
|
guildId := ctx.Keys["guildid"].(uint64)
|
||||||
|
|
||||||
|
roles := cache.Instance.GetGuildRoles(guildId)
|
||||||
|
|
||||||
|
ctx.JSON(200, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"roles": roles,
|
||||||
|
})
|
||||||
|
}
|
@ -78,6 +78,7 @@ func StartServer() {
|
|||||||
guildAuthApi.GET("/channels", api.ChannelsHandler)
|
guildAuthApi.GET("/channels", api.ChannelsHandler)
|
||||||
guildAuthApi.GET("/premium", api.PremiumHandler)
|
guildAuthApi.GET("/premium", api.PremiumHandler)
|
||||||
guildAuthApi.GET("/user/:user", api.UserHandler)
|
guildAuthApi.GET("/user/:user", api.UserHandler)
|
||||||
|
guildAuthApi.GET("/roles", api.RolesHandler)
|
||||||
|
|
||||||
guildAuthApi.GET("/settings", api.GetSettingsHandler)
|
guildAuthApi.GET("/settings", api.GetSettingsHandler)
|
||||||
guildAuthApi.POST("/settings", api.UpdateSettingsHandler)
|
guildAuthApi.POST("/settings", api.UpdateSettingsHandler)
|
||||||
|
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ require (
|
|||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306
|
github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306
|
||||||
github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4
|
github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4
|
||||||
github.com/TicketsBot/database v0.0.0-20200614182426-d117a0e3f769
|
github.com/TicketsBot/database v0.0.0-20200619194554-a6db672a94cf
|
||||||
github.com/apex/log v1.1.2
|
github.com/apex/log v1.1.2
|
||||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
|
6
public/static/css/animate.min.css
vendored
6
public/static/css/animate.min.css
vendored
File diff suppressed because one or more lines are too long
5
public/static/css/bootstrap.min.css
vendored
5
public/static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -2058,7 +2058,7 @@ fieldset[disabled] .btn-neutral.active {
|
|||||||
margin: 10px 10px 10px 0px;
|
margin: 10px 10px 10px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
/*.dropdown-menu {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -2167,7 +2167,7 @@ fieldset[disabled] .btn-neutral.active {
|
|||||||
.dropdown-menu.dropdown-red > li > a:hover,
|
.dropdown-menu.dropdown-red > li > a:hover,
|
||||||
.dropdown-menu.dropdown-red > li > a:focus {
|
.dropdown-menu.dropdown-red > li > a:focus {
|
||||||
background-color: rgba(255, 74, 85, 0.2);
|
background-color: rgba(255, 74, 85, 0.2);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
.dropdown-with-icons > li > a {
|
.dropdown-with-icons > li > a {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
@ -3211,11 +3211,6 @@ fieldset[disabled] .btn-neutral.active {
|
|||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
.navbar-nav > li > .dropdown-menu, .dropdown .dropdown-menu {
|
.navbar-nav > li > .dropdown-menu, .dropdown .dropdown-menu {
|
||||||
-webkit-transform: scale(0);
|
|
||||||
-moz-transform: scale(0);
|
|
||||||
-o-transform: scale(0);
|
|
||||||
-ms-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
-webkit-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
|
-webkit-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
|
||||||
-moz-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
|
-moz-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
|
||||||
-o-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
|
-o-transition: all 370ms cubic-bezier(0.34, 1.61, 0.7, 1);
|
||||||
|
@ -15,7 +15,7 @@ body {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
html > ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,3 +161,37 @@ body {
|
|||||||
.content {
|
.content {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* drop down menu */
|
||||||
|
.dropdown-menu {
|
||||||
|
background-color: #2e3136 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-light:not(:disabled):not(.disabled).active, .btn-light:not(:disabled):not(.disabled):active, .show>.btn-light.dropdown-toggle {
|
||||||
|
background-color: #2e3136 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bs-searchbox input {
|
||||||
|
background-color: white !important;
|
||||||
|
color: #2e3136 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item > .text {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:hover {
|
||||||
|
background-color: #272727 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-option-inner-inner {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
border-color: #2e3136 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bootstrap-select .bs-ok-default:after {
|
||||||
|
color: #2ECC71 !important;
|
||||||
|
}
|
||||||
|
27
public/static/js/auth.js
Normal file
27
public/static/js/auth.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
async function getToken() {
|
||||||
|
let token = window.localStorage.getItem('token');
|
||||||
|
if (token == null) {
|
||||||
|
let res = await axios.post('/token', {
|
||||||
|
withCredentials: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status !== 200 || !res.data.success) {
|
||||||
|
console.log("An error occurred whilst retrieving an authentication token. Please contact the developer");
|
||||||
|
console.log(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = res.data.token;
|
||||||
|
localStorage.setItem('token', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setDefaultHeader() {
|
||||||
|
const token = await getToken();
|
||||||
|
axios.defaults.headers.common['Authorization'] = token;
|
||||||
|
axios.defaults.validateStatus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultHeader();
|
404
public/static/js/bootstrap-notify.js
vendored
404
public/static/js/bootstrap-notify.js
vendored
@ -1,404 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Creative Tim Modifications
|
|
||||||
|
|
||||||
Lines: 239, 240 was changed from top: 5px to top: 50% and we added margin-top: -13px. In this way the close button will be aligned vertically
|
|
||||||
Line:242 - modified when the icon is set, we add the class "alert-with-icon", so there will be enough space for the icon.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Project: Bootstrap Notify = v3.1.5
|
|
||||||
* Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
|
|
||||||
* Author: Mouse0270 aka Robert McIntosh
|
|
||||||
* License: MIT License
|
|
||||||
* Website: https://github.com/mouse0270/bootstrap-growl
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global define:false, require: false, jQuery:false */
|
|
||||||
|
|
||||||
(function (factory) {
|
|
||||||
if (typeof define === 'function' && define.amd) {
|
|
||||||
// AMD. Register as an anonymous module.
|
|
||||||
define(['jquery'], factory);
|
|
||||||
} else if (typeof exports === 'object') {
|
|
||||||
// Node/CommonJS
|
|
||||||
factory(require('jquery'));
|
|
||||||
} else {
|
|
||||||
// Browser globals
|
|
||||||
factory(jQuery);
|
|
||||||
}
|
|
||||||
}(function ($) {
|
|
||||||
// Create the defaults once
|
|
||||||
var defaults = {
|
|
||||||
element: 'body',
|
|
||||||
position: null,
|
|
||||||
type: "info",
|
|
||||||
allow_dismiss: true,
|
|
||||||
allow_duplicates: true,
|
|
||||||
newest_on_top: false,
|
|
||||||
showProgressbar: false,
|
|
||||||
placement: {
|
|
||||||
from: "top",
|
|
||||||
align: "right"
|
|
||||||
},
|
|
||||||
offset: 20,
|
|
||||||
spacing: 10,
|
|
||||||
z_index: 1031,
|
|
||||||
delay: 5000,
|
|
||||||
timer: 1000,
|
|
||||||
url_target: '_blank',
|
|
||||||
mouse_over: null,
|
|
||||||
animate: {
|
|
||||||
enter: 'animated fadeInDown',
|
|
||||||
exit: 'animated fadeOutUp'
|
|
||||||
},
|
|
||||||
onShow: null,
|
|
||||||
onShown: null,
|
|
||||||
onClose: null,
|
|
||||||
onClosed: null,
|
|
||||||
icon_type: 'class',
|
|
||||||
template: '<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'
|
|
||||||
};
|
|
||||||
|
|
||||||
String.format = function () {
|
|
||||||
var str = arguments[0];
|
|
||||||
for (var i = 1; i < arguments.length; i++) {
|
|
||||||
str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]);
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
};
|
|
||||||
|
|
||||||
function isDuplicateNotification(notification) {
|
|
||||||
var isDupe = false;
|
|
||||||
|
|
||||||
$('[data-notify="container"]').each(function (i, el) {
|
|
||||||
var $el = $(el);
|
|
||||||
var title = $el.find('[data-notify="title"]').text().trim();
|
|
||||||
var message = $el.find('[data-notify="message"]').html().trim();
|
|
||||||
|
|
||||||
// The input string might be different than the actual parsed HTML string!
|
|
||||||
// (<br> vs <br /> for example)
|
|
||||||
// So we have to force-parse this as HTML here!
|
|
||||||
var isSameTitle = title === $("<div>" + notification.settings.content.title + "</div>").html().trim();
|
|
||||||
var isSameMsg = message === $("<div>" + notification.settings.content.message + "</div>").html().trim();
|
|
||||||
var isSameType = $el.hasClass('alert-' + notification.settings.type);
|
|
||||||
|
|
||||||
if (isSameTitle && isSameMsg && isSameType) {
|
|
||||||
//we found the dupe. Set the var and stop checking.
|
|
||||||
isDupe = true;
|
|
||||||
}
|
|
||||||
return !isDupe;
|
|
||||||
});
|
|
||||||
|
|
||||||
return isDupe;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Notify(element, content, options) {
|
|
||||||
// Setup Content of Notify
|
|
||||||
var contentObj = {
|
|
||||||
content: {
|
|
||||||
message: typeof content === 'object' ? content.message : content,
|
|
||||||
title: content.title ? content.title : '',
|
|
||||||
icon: content.icon ? content.icon : '',
|
|
||||||
url: content.url ? content.url : '#',
|
|
||||||
target: content.target ? content.target : '-'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
options = $.extend(true, {}, contentObj, options);
|
|
||||||
this.settings = $.extend(true, {}, defaults, options);
|
|
||||||
this._defaults = defaults;
|
|
||||||
if (this.settings.content.target === "-") {
|
|
||||||
this.settings.content.target = this.settings.url_target;
|
|
||||||
}
|
|
||||||
this.animations = {
|
|
||||||
start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
|
|
||||||
end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof this.settings.offset === 'number') {
|
|
||||||
this.settings.offset = {
|
|
||||||
x: this.settings.offset,
|
|
||||||
y: this.settings.offset
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//if duplicate messages are not allowed, then only continue if this new message is not a duplicate of one that it already showing
|
|
||||||
if (this.settings.allow_duplicates || (!this.settings.allow_duplicates && !isDuplicateNotification(this))) {
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$.extend(Notify.prototype, {
|
|
||||||
init: function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.buildNotify();
|
|
||||||
if (this.settings.content.icon) {
|
|
||||||
this.setIcon();
|
|
||||||
}
|
|
||||||
if (this.settings.content.url != "#") {
|
|
||||||
this.styleURL();
|
|
||||||
}
|
|
||||||
this.styleDismiss();
|
|
||||||
this.placement();
|
|
||||||
this.bind();
|
|
||||||
|
|
||||||
this.notify = {
|
|
||||||
$ele: this.$ele,
|
|
||||||
update: function (command, update) {
|
|
||||||
var commands = {};
|
|
||||||
if (typeof command === "string") {
|
|
||||||
commands[command] = update;
|
|
||||||
} else {
|
|
||||||
commands = command;
|
|
||||||
}
|
|
||||||
for (var cmd in commands) {
|
|
||||||
switch (cmd) {
|
|
||||||
case "type":
|
|
||||||
this.$ele.removeClass('alert-' + self.settings.type);
|
|
||||||
this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
|
|
||||||
self.settings.type = commands[cmd];
|
|
||||||
this.$ele.addClass('alert-' + commands[cmd]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[cmd]);
|
|
||||||
break;
|
|
||||||
case "icon":
|
|
||||||
var $icon = this.$ele.find('[data-notify="icon"]');
|
|
||||||
if (self.settings.icon_type.toLowerCase() === 'class') {
|
|
||||||
$icon.removeClass(self.settings.content.icon).addClass(commands[cmd]);
|
|
||||||
} else {
|
|
||||||
if (!$icon.is('img')) {
|
|
||||||
$icon.find('img');
|
|
||||||
}
|
|
||||||
$icon.attr('src', commands[cmd]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "progress":
|
|
||||||
var newDelay = self.settings.delay - (self.settings.delay * (commands[cmd] / 100));
|
|
||||||
this.$ele.data('notify-delay', newDelay);
|
|
||||||
this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[cmd]).css('width', commands[cmd] + '%');
|
|
||||||
break;
|
|
||||||
case "url":
|
|
||||||
this.$ele.find('[data-notify="url"]').attr('href', commands[cmd]);
|
|
||||||
break;
|
|
||||||
case "target":
|
|
||||||
this.$ele.find('[data-notify="url"]').attr('target', commands[cmd]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.$ele.find('[data-notify="' + cmd + '"]').html(commands[cmd]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
|
|
||||||
self.reposition(posX);
|
|
||||||
},
|
|
||||||
close: function () {
|
|
||||||
self.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
},
|
|
||||||
buildNotify: function () {
|
|
||||||
var content = this.settings.content;
|
|
||||||
this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
|
|
||||||
this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
|
|
||||||
if (!this.settings.allow_dismiss) {
|
|
||||||
this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
|
|
||||||
}
|
|
||||||
if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
|
|
||||||
this.$ele.find('[data-notify="progressbar"]').remove();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setIcon: function () {
|
|
||||||
|
|
||||||
this.$ele.addClass('alert-with-icon');
|
|
||||||
|
|
||||||
if (this.settings.icon_type.toLowerCase() === 'class') {
|
|
||||||
this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
|
|
||||||
} else {
|
|
||||||
if (this.$ele.find('[data-notify="icon"]').is('img')) {
|
|
||||||
this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
|
|
||||||
} else {
|
|
||||||
this.$ele.find('[data-notify="icon"]').append('<img src="' + this.settings.content.icon + '" alt="Notify Icon" />');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
styleDismiss: function () {
|
|
||||||
this.$ele.find('[data-notify="dismiss"]').css({
|
|
||||||
position: 'absolute',
|
|
||||||
right: '10px',
|
|
||||||
top: '50%',
|
|
||||||
marginTop: '-13px',
|
|
||||||
zIndex: this.settings.z_index + 2
|
|
||||||
});
|
|
||||||
},
|
|
||||||
styleURL: function () {
|
|
||||||
this.$ele.find('[data-notify="url"]').css({
|
|
||||||
backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
|
|
||||||
height: '100%',
|
|
||||||
left: 0,
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
width: '100%',
|
|
||||||
zIndex: this.settings.z_index + 1
|
|
||||||
});
|
|
||||||
},
|
|
||||||
placement: function () {
|
|
||||||
var self = this,
|
|
||||||
offsetAmt = this.settings.offset.y,
|
|
||||||
css = {
|
|
||||||
display: 'inline-block',
|
|
||||||
margin: '0px auto',
|
|
||||||
position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
|
|
||||||
transition: 'all .5s ease-in-out',
|
|
||||||
zIndex: this.settings.z_index
|
|
||||||
},
|
|
||||||
hasAnimation = false,
|
|
||||||
settings = this.settings;
|
|
||||||
|
|
||||||
$('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function () {
|
|
||||||
offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
|
|
||||||
});
|
|
||||||
if (this.settings.newest_on_top === true) {
|
|
||||||
offsetAmt = this.settings.offset.y;
|
|
||||||
}
|
|
||||||
css[this.settings.placement.from] = offsetAmt + 'px';
|
|
||||||
|
|
||||||
switch (this.settings.placement.align) {
|
|
||||||
case "left":
|
|
||||||
case "right":
|
|
||||||
css[this.settings.placement.align] = this.settings.offset.x + 'px';
|
|
||||||
break;
|
|
||||||
case "center":
|
|
||||||
css.left = 0;
|
|
||||||
css.right = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.$ele.css(css).addClass(this.settings.animate.enter);
|
|
||||||
$.each(Array('webkit-', 'moz-', 'o-', 'ms-', ''), function (index, prefix) {
|
|
||||||
self.$ele[0].style[prefix + 'AnimationIterationCount'] = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.settings.element).append(this.$ele);
|
|
||||||
|
|
||||||
if (this.settings.newest_on_top === true) {
|
|
||||||
offsetAmt = (parseInt(offsetAmt) + parseInt(this.settings.spacing)) + this.$ele.outerHeight();
|
|
||||||
this.reposition(offsetAmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($.isFunction(self.settings.onShow)) {
|
|
||||||
self.settings.onShow.call(this.$ele);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$ele.one(this.animations.start, function () {
|
|
||||||
hasAnimation = true;
|
|
||||||
}).one(this.animations.end, function () {
|
|
||||||
if ($.isFunction(self.settings.onShown)) {
|
|
||||||
self.settings.onShown.call(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
if (!hasAnimation) {
|
|
||||||
if ($.isFunction(self.settings.onShown)) {
|
|
||||||
self.settings.onShown.call(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 600);
|
|
||||||
},
|
|
||||||
bind: function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.$ele.find('[data-notify="dismiss"]').on('click', function () {
|
|
||||||
self.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$ele.mouseover(function () {
|
|
||||||
$(this).data('data-hover', "true");
|
|
||||||
}).mouseout(function () {
|
|
||||||
$(this).data('data-hover', "false");
|
|
||||||
});
|
|
||||||
this.$ele.data('data-hover', "false");
|
|
||||||
|
|
||||||
if (this.settings.delay > 0) {
|
|
||||||
self.$ele.data('notify-delay', self.settings.delay);
|
|
||||||
var timer = setInterval(function () {
|
|
||||||
var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
|
|
||||||
if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over === "pause") || self.settings.mouse_over != "pause") {
|
|
||||||
var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
|
|
||||||
self.$ele.data('notify-delay', delay);
|
|
||||||
self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
|
|
||||||
}
|
|
||||||
if (delay <= -(self.settings.timer)) {
|
|
||||||
clearInterval(timer);
|
|
||||||
self.close();
|
|
||||||
}
|
|
||||||
}, self.settings.timer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close: function () {
|
|
||||||
var self = this,
|
|
||||||
posX = parseInt(this.$ele.css(this.settings.placement.from)),
|
|
||||||
hasAnimation = false;
|
|
||||||
|
|
||||||
this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
|
|
||||||
self.reposition(posX);
|
|
||||||
|
|
||||||
if ($.isFunction(self.settings.onClose)) {
|
|
||||||
self.settings.onClose.call(this.$ele);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$ele.one(this.animations.start, function () {
|
|
||||||
hasAnimation = true;
|
|
||||||
}).one(this.animations.end, function () {
|
|
||||||
$(this).remove();
|
|
||||||
if ($.isFunction(self.settings.onClosed)) {
|
|
||||||
self.settings.onClosed.call(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
if (!hasAnimation) {
|
|
||||||
self.$ele.remove();
|
|
||||||
if (self.settings.onClosed) {
|
|
||||||
self.settings.onClosed(self.$ele);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 600);
|
|
||||||
},
|
|
||||||
reposition: function (posX) {
|
|
||||||
var self = this,
|
|
||||||
notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
|
|
||||||
$elements = this.$ele.nextAll(notifies);
|
|
||||||
if (this.settings.newest_on_top === true) {
|
|
||||||
$elements = this.$ele.prevAll(notifies);
|
|
||||||
}
|
|
||||||
$elements.each(function () {
|
|
||||||
$(this).css(self.settings.placement.from, posX);
|
|
||||||
posX = (parseInt(posX) + parseInt(self.settings.spacing)) + $(this).outerHeight();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$.notify = function (content, options) {
|
|
||||||
var plugin = new Notify(this, content, options);
|
|
||||||
return plugin.notify;
|
|
||||||
};
|
|
||||||
$.notifyDefaults = function (options) {
|
|
||||||
defaults = $.extend(true, {}, defaults, options);
|
|
||||||
return defaults;
|
|
||||||
};
|
|
||||||
$.notifyClose = function (command) {
|
|
||||||
if (typeof command === "undefined" || command === "all") {
|
|
||||||
$('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
|
|
||||||
} else {
|
|
||||||
$('[data-notify-position="' + command + '"]').find('[data-notify="dismiss"]').trigger('click');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}));
|
|
438
public/static/js/bootstrap-select.js
vendored
438
public/static/js/bootstrap-select.js
vendored
@ -1,438 +0,0 @@
|
|||||||
!function($) {
|
|
||||||
var Selectpicker = function(element, options, e) {
|
|
||||||
if (e ) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
this.$element = $(element);
|
|
||||||
this.$newElement = null;
|
|
||||||
this.button = null;
|
|
||||||
|
|
||||||
//Merge defaults, options and data-attributes to make our options
|
|
||||||
this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options);
|
|
||||||
|
|
||||||
//If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute
|
|
||||||
if(this.options.title==null)
|
|
||||||
this.options.title = this.$element.attr('title');
|
|
||||||
|
|
||||||
//Expose public methods
|
|
||||||
this.val = Selectpicker.prototype.val;
|
|
||||||
this.render = Selectpicker.prototype.render;
|
|
||||||
this.init();
|
|
||||||
};
|
|
||||||
|
|
||||||
Selectpicker.prototype = {
|
|
||||||
|
|
||||||
constructor: Selectpicker,
|
|
||||||
|
|
||||||
init: function (e) {
|
|
||||||
var _this = this;
|
|
||||||
this.$element.hide();
|
|
||||||
this.multiple = this.$element.prop('multiple');
|
|
||||||
|
|
||||||
|
|
||||||
var classList = this.$element.attr('class') !== undefined ? this.$element.attr('class').split(/\s+/) : '';
|
|
||||||
var id = this.$element.attr('id');
|
|
||||||
this.$element.after( this.createView() );
|
|
||||||
this.$newElement = this.$element.next('.select');
|
|
||||||
var select = this.$newElement;
|
|
||||||
var menu = this.$newElement.find('.dropdown-menu');
|
|
||||||
var menuArrow = this.$newElement.find('.dropdown-arrow');
|
|
||||||
var menuA = menu.find('li > a');
|
|
||||||
var liHeight = select.addClass('open').find('.dropdown-menu li > a').outerHeight();
|
|
||||||
select.removeClass('open');
|
|
||||||
var divHeight = menu.find('li .divider').outerHeight(true);
|
|
||||||
var selectOffset_top = this.$newElement.offset().top;
|
|
||||||
var size = 0;
|
|
||||||
var menuHeight = 0;
|
|
||||||
var selectHeight = this.$newElement.outerHeight();
|
|
||||||
this.button = this.$newElement.find('> button');
|
|
||||||
if (id !== undefined) {
|
|
||||||
this.button.attr('id', id);
|
|
||||||
$('label[for="' + id + '"]').click(function(){ select.find('button#'+id).focus(); })
|
|
||||||
}
|
|
||||||
for (var i = 0; i < classList.length; i++) {
|
|
||||||
if(classList[i] != 'selectpicker') {
|
|
||||||
this.$newElement.addClass(classList[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//If we are multiple, then add the show-tick class by default
|
|
||||||
if(this.multiple) {
|
|
||||||
this.$newElement.addClass('select-multiple');
|
|
||||||
}
|
|
||||||
this.button.addClass(this.options.style);
|
|
||||||
menu.addClass(this.options.menuStyle);
|
|
||||||
menuArrow.addClass(function() {
|
|
||||||
if (_this.options.menuStyle) {
|
|
||||||
return _this.options.menuStyle.replace('dropdown-', 'dropdown-arrow-');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.checkDisabled();
|
|
||||||
this.checkTabIndex();
|
|
||||||
this.clickListener();
|
|
||||||
var menuPadding = parseInt(menu.css('padding-top')) + parseInt(menu.css('padding-bottom')) + parseInt(menu.css('border-top-width')) + parseInt(menu.css('border-bottom-width'));
|
|
||||||
if (this.options.size == 'auto') {
|
|
||||||
|
|
||||||
// Creative Tim Changes: We changed the regular function made in bootstrap-select with this function so the getSize() will not be triggered one million times per second while you scroll.
|
|
||||||
|
|
||||||
var getSize = debounce(function() {
|
|
||||||
var selectOffset_top_scroll = selectOffset_top - $(window).scrollTop();
|
|
||||||
var windowHeight = $(window).innerHeight();
|
|
||||||
var menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2;
|
|
||||||
var selectOffset_bot = windowHeight - selectOffset_top_scroll - selectHeight - menuExtras;
|
|
||||||
menuHeight = selectOffset_bot;
|
|
||||||
if (select.hasClass('dropup')) {
|
|
||||||
menuHeight = selectOffset_top_scroll - menuExtras;
|
|
||||||
}
|
|
||||||
//limit menuHeight to 300px to have a smooth transition with cubic bezier on dropdown
|
|
||||||
if(menuHeight >= 300){
|
|
||||||
menuHeight = 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'auto', 'min-height' : liHeight * 3 + 'px'});
|
|
||||||
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
getSize;
|
|
||||||
$(window).on('scroll', getSize);
|
|
||||||
$(window).on('resize', getSize);
|
|
||||||
|
|
||||||
if (window.MutationObserver) {
|
|
||||||
new MutationObserver(getSize).observe(this.$element.get(0), {
|
|
||||||
childList: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$element.bind('DOMNodeInserted', getSize);
|
|
||||||
}
|
|
||||||
} else if (this.options.size && this.options.size != 'auto' && menu.find('li').length > this.options.size) {
|
|
||||||
var optIndex = menu.find("li > *").filter(':not(.divider)').slice(0,this.options.size).last().parent().index();
|
|
||||||
var divLength = menu.find("li").slice(0,optIndex + 1).find('.divider').length;
|
|
||||||
menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding;
|
|
||||||
menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'scroll'});
|
|
||||||
//console.log('sunt in if');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for updates to the DOM and re render... (Use Mutation Observer when availiable)
|
|
||||||
if (window.MutationObserver) {
|
|
||||||
new MutationObserver($.proxy(this.reloadLi, this)).observe(this.$element.get(0), {
|
|
||||||
childList: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$element.bind('DOMNodeInserted', $.proxy(this.reloadLi, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
createDropdown: function() {
|
|
||||||
var drop =
|
|
||||||
"<div class='btn-group select'>" +
|
|
||||||
"<button class='btn dropdown-toggle clearfix' data-toggle='dropdown'>" +
|
|
||||||
"<span class='filter-option'></span> " +
|
|
||||||
"<span class='caret'></span>" +
|
|
||||||
"</button>" +
|
|
||||||
"<span class='dropdown-arrow'></span>" +
|
|
||||||
"<ul class='dropdown-menu' role='menu'>" +
|
|
||||||
"</ul>" +
|
|
||||||
"</div>";
|
|
||||||
|
|
||||||
return $(drop);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
createView: function() {
|
|
||||||
var $drop = this.createDropdown();
|
|
||||||
var $li = this.createLi();
|
|
||||||
$drop.find('ul').append($li);
|
|
||||||
return $drop;
|
|
||||||
},
|
|
||||||
|
|
||||||
reloadLi: function() {
|
|
||||||
//Remove all children.
|
|
||||||
this.destroyLi();
|
|
||||||
//Re build
|
|
||||||
$li = this.createLi();
|
|
||||||
this.$newElement.find('ul').append( $li );
|
|
||||||
//render view
|
|
||||||
this.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyLi:function() {
|
|
||||||
this.$newElement.find('li').remove();
|
|
||||||
},
|
|
||||||
|
|
||||||
createLi: function() {
|
|
||||||
|
|
||||||
var _this = this;
|
|
||||||
var _li = [];
|
|
||||||
var _liA = [];
|
|
||||||
var _liHtml = '';
|
|
||||||
|
|
||||||
this.$element.find('option').each(function(){
|
|
||||||
_li.push($(this).text());
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$element.find('option').each(function(index) {
|
|
||||||
//Get the class and text for the option
|
|
||||||
var optionClass = $(this).attr("class") !== undefined ? $(this).attr("class") : '';
|
|
||||||
var text = $(this).text();
|
|
||||||
var subtext = $(this).data('subtext') !== undefined ? '<small class="muted">'+$(this).data('subtext')+'</small>' : '';
|
|
||||||
|
|
||||||
//Append any subtext to the main text.
|
|
||||||
text+=subtext;
|
|
||||||
|
|
||||||
if ($(this).parent().is('optgroup') && $(this).data('divider') != true) {
|
|
||||||
if ($(this).index() == 0) {
|
|
||||||
//Get the opt group label
|
|
||||||
var label = $(this).parent().attr('label');
|
|
||||||
var labelSubtext = $(this).parent().data('subtext') !== undefined ? '<small class="muted">'+$(this).parent().data('subtext')+'</small>' : '';
|
|
||||||
label += labelSubtext;
|
|
||||||
|
|
||||||
if ($(this)[0].index != 0) {
|
|
||||||
_liA.push(
|
|
||||||
'<div class="divider"></div>'+
|
|
||||||
'<dt>'+label+'</dt>'+
|
|
||||||
_this.createA(text, "opt " + optionClass )
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_liA.push(
|
|
||||||
'<dt>'+label+'</dt>'+
|
|
||||||
_this.createA(text, "opt " + optionClass ));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_liA.push( _this.createA(text, "opt " + optionClass ) );
|
|
||||||
}
|
|
||||||
} else if ($(this).data('divider') == true) {
|
|
||||||
_liA.push('<div class="divider"></div>');
|
|
||||||
} else if ($(this).data('hidden') == true) {
|
|
||||||
_liA.push('');
|
|
||||||
} else {
|
|
||||||
_liA.push( _this.createA(text, optionClass ) );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_li.length > 0) {
|
|
||||||
for (var i = 0; i < _li.length; i++) {
|
|
||||||
var $option = this.$element.find('option').eq(i);
|
|
||||||
_liHtml += "<li rel=" + i + ">" + _liA[i] + "</li>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we dont have a selected item, and we dont have a title, select the first element so something is set in the button
|
|
||||||
if(this.$element.find('option:selected').length==0 && !_this.options.title) {
|
|
||||||
this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $(_liHtml);
|
|
||||||
},
|
|
||||||
|
|
||||||
createA:function(test, classes) {
|
|
||||||
return '<a tabindex="-1" href="#" class="'+classes+'">' +
|
|
||||||
'<span class="">' + test + '</span>' +
|
|
||||||
'</a>';
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
render:function() {
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
//Set width of select
|
|
||||||
if (this.options.width == 'auto') {
|
|
||||||
var ulWidth = this.$newElement.find('.dropdown-menu').css('width');
|
|
||||||
this.$newElement.css('width',ulWidth);
|
|
||||||
} else if (this.options.width && this.options.width != 'auto') {
|
|
||||||
this.$newElement.css('width',this.options.width);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update the LI to match the SELECT
|
|
||||||
this.$element.find('option').each(function(index) {
|
|
||||||
_this.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') );
|
|
||||||
_this.setSelected(index, $(this).is(':selected') );
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var selectedItems = this.$element.find('option:selected').map(function(index,value) {
|
|
||||||
if($(this).attr('title')!=undefined) {
|
|
||||||
return $(this).attr('title');
|
|
||||||
} else {
|
|
||||||
return $(this).text();
|
|
||||||
}
|
|
||||||
}).toArray();
|
|
||||||
|
|
||||||
//Convert all the values into a comma delimited string
|
|
||||||
var title = selectedItems.join(", ");
|
|
||||||
|
|
||||||
//If this is multi select, and the selectText type is count, the show 1 of 2 selected etc..
|
|
||||||
if(_this.multiple && _this.options.selectedTextFormat.indexOf('count') > -1) {
|
|
||||||
var max = _this.options.selectedTextFormat.split(">");
|
|
||||||
if( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) {
|
|
||||||
title = selectedItems.length +' of ' + this.$element.find('option').length + ' selected';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we dont have a title, then use the default, or if nothing is set at all, use the not selected text
|
|
||||||
if(!title) {
|
|
||||||
title = _this.options.title != undefined ? _this.options.title : _this.options.noneSelectedText;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$element.next('.select').find('.filter-option').html( title );
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setSelected:function(index, selected) {
|
|
||||||
if(selected) {
|
|
||||||
this.$newElement.find('li').eq(index).addClass('selected');
|
|
||||||
} else {
|
|
||||||
this.$newElement.find('li').eq(index).removeClass('selected');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setDisabled:function(index, disabled) {
|
|
||||||
if(disabled) {
|
|
||||||
this.$newElement.find('li').eq(index).addClass('disabled');
|
|
||||||
} else {
|
|
||||||
this.$newElement.find('li').eq(index).removeClass('disabled');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
checkDisabled: function() {
|
|
||||||
if (this.$element.is(':disabled')) {
|
|
||||||
this.button.addClass('disabled');
|
|
||||||
this.button.click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
checkTabIndex: function() {
|
|
||||||
if (this.$element.is('[tabindex]')) {
|
|
||||||
var tabindex = this.$element.attr("tabindex");
|
|
||||||
this.button.attr('tabindex', tabindex);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clickListener: function() {
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.$newElement.on('click', 'li a', function(e){
|
|
||||||
var clickedIndex = $(this).parent().index(),
|
|
||||||
$this = $(this).parent(),
|
|
||||||
$select = $this.parents('.select');
|
|
||||||
|
|
||||||
|
|
||||||
//Dont close on multi choice menu
|
|
||||||
if(_this.multiple) {
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
//Dont run if we have been disabled
|
|
||||||
if ($select.prev('select').not(':disabled') && !$(this).parent().hasClass('disabled')){
|
|
||||||
//Deselect all others if not multi select box
|
|
||||||
if (!_this.multiple) {
|
|
||||||
$select.prev('select').find('option').removeAttr('selected');
|
|
||||||
$select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected');
|
|
||||||
}
|
|
||||||
//Else toggle the one we have chosen if we are multi selet.
|
|
||||||
else {
|
|
||||||
var selected = $select.prev('select').find('option').eq(clickedIndex).prop('selected');
|
|
||||||
|
|
||||||
if(selected) {
|
|
||||||
$select.prev('select').find('option').eq(clickedIndex).removeAttr('selected');
|
|
||||||
} else {
|
|
||||||
$select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$select.find('.filter-option').html($this.text());
|
|
||||||
$select.find('button').focus();
|
|
||||||
|
|
||||||
// Trigger select 'change'
|
|
||||||
$select.prev('select').trigger('change');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$newElement.on('click', 'li.disabled a, li dt, li .divider', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
$select = $(this).parent().parents('.select');
|
|
||||||
$select.find('button').focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$element.on('change', function(e) {
|
|
||||||
_this.render();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
val:function(value) {
|
|
||||||
|
|
||||||
if(value!=undefined) {
|
|
||||||
this.$element.val( value );
|
|
||||||
|
|
||||||
this.$element.trigger('change');
|
|
||||||
return this.$element;
|
|
||||||
} else {
|
|
||||||
return this.$element.val();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn.selectpicker = function(option, event) {
|
|
||||||
//get the args of the outer function..
|
|
||||||
var args = arguments;
|
|
||||||
var value;
|
|
||||||
var chain = this.each(function () {
|
|
||||||
var $this = $(this),
|
|
||||||
data = $this.data('selectpicker'),
|
|
||||||
options = typeof option == 'object' && option;
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
$this.data('selectpicker', (data = new Selectpicker(this, options, event)));
|
|
||||||
} else {
|
|
||||||
for(var i in option) {
|
|
||||||
data[i]=option[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof option == 'string') {
|
|
||||||
//Copy the value of option, as once we shift the arguments
|
|
||||||
//it also shifts the value of option.
|
|
||||||
property = option;
|
|
||||||
if(data[property] instanceof Function) {
|
|
||||||
[].shift.apply(args);
|
|
||||||
value = data[property].apply(data, args);
|
|
||||||
} else {
|
|
||||||
value = data[property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(value!=undefined) {
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
return chain;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn.selectpicker.defaults = {
|
|
||||||
style: null,
|
|
||||||
size: 'auto',
|
|
||||||
title: null,
|
|
||||||
selectedTextFormat : 'values',
|
|
||||||
noneSelectedText : 'Nothing selected',
|
|
||||||
width: null,
|
|
||||||
menuStyle: null,
|
|
||||||
toggleSize: null
|
|
||||||
}
|
|
||||||
|
|
||||||
}(window.jQuery);
|
|
7
public/static/js/bootstrap.min.js
vendored
7
public/static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
9
public/static/js/chartist.min.js
vendored
9
public/static/js/chartist.min.js
vendored
File diff suppressed because one or more lines are too long
4
public/static/js/jquery.3.2.1.min.js
vendored
4
public/static/js/jquery.3.2.1.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,154 +0,0 @@
|
|||||||
/*!
|
|
||||||
|
|
||||||
=========================================================
|
|
||||||
* Light Bootstrap Dashboard - v1.4.0
|
|
||||||
=========================================================
|
|
||||||
|
|
||||||
* Product Page: http://www.creative-tim.com/product/light-bootstrap-dashboard
|
|
||||||
* Copyright 2017 Creative Tim (http://www.creative-tim.com)
|
|
||||||
* Licensed under MIT (https://github.com/creativetimofficial/light-bootstrap-dashboard/blob/master/LICENSE.md)
|
|
||||||
|
|
||||||
=========================================================
|
|
||||||
|
|
||||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
var searchVisible = 0;
|
|
||||||
var transparent = true;
|
|
||||||
|
|
||||||
var transparentDemo = true;
|
|
||||||
var fixedTop = false;
|
|
||||||
|
|
||||||
var navbar_initialized = false;
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
|
||||||
window_width = $(window).width();
|
|
||||||
|
|
||||||
// check if there is an image set for the sidebar's background
|
|
||||||
lbd.checkSidebarImage();
|
|
||||||
|
|
||||||
// Init navigation toggle for small screens
|
|
||||||
lbd.initRightMenu();
|
|
||||||
|
|
||||||
// Activate the tooltips
|
|
||||||
$('[rel="tooltip"]').tooltip();
|
|
||||||
|
|
||||||
$('.form-control').on("focus", function(){
|
|
||||||
$(this).parent('.input-group').addClass("input-group-focus");
|
|
||||||
}).on("blur", function(){
|
|
||||||
$(this).parent(".input-group").removeClass("input-group-focus");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fixes sub-nav not working as expected on IOS
|
|
||||||
$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.navbar-toggle', function(){
|
|
||||||
$toggle = $(this);
|
|
||||||
|
|
||||||
if(lbd.misc.navbar_menu_visible == 1) {
|
|
||||||
$('html').removeClass('nav-open');
|
|
||||||
lbd.misc.navbar_menu_visible = 0;
|
|
||||||
$('#bodyClick').remove();
|
|
||||||
setTimeout(function(){
|
|
||||||
$toggle.removeClass('toggled');
|
|
||||||
}, 550);
|
|
||||||
} else {
|
|
||||||
setTimeout(function(){
|
|
||||||
$toggle.addClass('toggled');
|
|
||||||
}, 580);
|
|
||||||
div = '<div id="bodyClick"></div>';
|
|
||||||
$(div).appendTo('body').click(function() {
|
|
||||||
$('html').removeClass('nav-open');
|
|
||||||
lbd.misc.navbar_menu_visible = 0;
|
|
||||||
setTimeout(function(){
|
|
||||||
$toggle.removeClass('toggled');
|
|
||||||
$('#bodyClick').remove();
|
|
||||||
}, 550);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('html').addClass('nav-open');
|
|
||||||
lbd.misc.navbar_menu_visible = 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).on('resize', function(){
|
|
||||||
if(navbar_initialized){
|
|
||||||
lbd.initRightMenu();
|
|
||||||
navbar_initialized = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lbd = {
|
|
||||||
misc:{
|
|
||||||
navbar_menu_visible: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
checkSidebarImage: function(){
|
|
||||||
$sidebar = $('.sidebar');
|
|
||||||
image_src = $sidebar.data('image');
|
|
||||||
|
|
||||||
if(image_src !== undefined){
|
|
||||||
sidebar_container = '<div class="sidebar-background" style="background-image: url(' + image_src + ') "/>'
|
|
||||||
$sidebar.append(sidebar_container);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initRightMenu: debounce(function(){
|
|
||||||
if(!navbar_initialized){
|
|
||||||
$sidebar_wrapper = $('.sidebar-wrapper');
|
|
||||||
$navbar = $('nav').find('.navbar-collapse').html();
|
|
||||||
|
|
||||||
mobile_menu_content = '';
|
|
||||||
|
|
||||||
nav_content = $navbar;
|
|
||||||
|
|
||||||
nav_content = '<ul class="nav nav-mobile-menu">' + nav_content + '</ul>';
|
|
||||||
|
|
||||||
// navbar_form = $('nav').find('.navbar-form').get(0).outerHTML;
|
|
||||||
|
|
||||||
$sidebar_nav = $sidebar_wrapper.find(' > .nav');
|
|
||||||
|
|
||||||
// insert the navbar form before the sidebar list
|
|
||||||
$nav_content = $(nav_content);
|
|
||||||
// $navbar_form = $(navbar_form);
|
|
||||||
$nav_content.insertBefore($sidebar_nav);
|
|
||||||
// $navbar_form.insertBefore($nav_content);
|
|
||||||
|
|
||||||
$(".sidebar-wrapper .dropdown .dropdown-menu > li > a").click(function(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
mobile_menu_initialized = true;
|
|
||||||
} else {
|
|
||||||
if($(window).width() > 991){
|
|
||||||
// reset all the additions that we made for the sidebar wrapper only if the screen is bigger than 991px
|
|
||||||
// $sidebar_wrapper.find('.navbar-form').remove();
|
|
||||||
$sidebar_wrapper.find('.nav-mobile-menu').remove();
|
|
||||||
|
|
||||||
mobile_menu_initialized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},200)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns a function, that, as long as it continues to be invoked, will not
|
|
||||||
// be triggered. The function will be called after it stops being called for
|
|
||||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
|
||||||
// leading edge, instead of the trailing.
|
|
||||||
|
|
||||||
function debounce(func, wait, immediate) {
|
|
||||||
var timeout;
|
|
||||||
return function() {
|
|
||||||
var context = this, args = arguments;
|
|
||||||
clearTimeout(timeout);
|
|
||||||
timeout = setTimeout(function() {
|
|
||||||
timeout = null;
|
|
||||||
if (!immediate) func.apply(context, args);
|
|
||||||
}, wait);
|
|
||||||
if (immediate && !timeout) func.apply(context, args);
|
|
||||||
};
|
|
||||||
};
|
|
23
public/static/js/loadingscreen.js
Normal file
23
public/static/js/loadingscreen.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
function showLoadingScreen() {
|
||||||
|
const content = document.getElementsByClassName('content')[0];
|
||||||
|
content.style.display = 'none';
|
||||||
|
document.getElementById('loading-container').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLoadingScreen() {
|
||||||
|
document.getElementById('loading-container').style.display = 'none';
|
||||||
|
|
||||||
|
const content = document.getElementsByClassName('content')[0];
|
||||||
|
content.style.display = 'block';
|
||||||
|
content.classList.add('fade-in');
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withLoadingScreen(func) {
|
||||||
|
showLoadingScreen();
|
||||||
|
await func();
|
||||||
|
hideLoadingScreen();
|
||||||
|
}
|
38
public/static/js/utils.js
Normal file
38
public/static/js/utils.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
function showToast(title, content) {
|
||||||
|
const container = document.getElementById('toast-container');
|
||||||
|
|
||||||
|
container.innerHTML += `
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">${title}</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
${content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
$('.toast').toast('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendTd(tr, content) {
|
||||||
|
const td = document.createElement('td');
|
||||||
|
td.appendChild(document.createTextNode(content));
|
||||||
|
td.classList.add('white');
|
||||||
|
tr.appendChild(td);
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendButton(tr, content, onclick) {
|
||||||
|
const tdRemove = document.createElement('td');
|
||||||
|
const removeButton = document.createElement('button');
|
||||||
|
removeButton.type = 'submit';
|
||||||
|
removeButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
|
||||||
|
removeButton.appendChild(document.createTextNode(content));
|
||||||
|
removeButton.onclick = onclick;
|
||||||
|
tdRemove.appendChild(removeButton);
|
||||||
|
tr.appendChild(tdRemove);
|
||||||
|
}
|
@ -22,87 +22,43 @@
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Vue
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
|
|
||||||
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>-->
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"
|
||||||
integrity="sha256-T/f7Sju1ZfNNfBh7skWn0idlCBcI3RwdLSS4/I7NQKQ=" crossorigin="anonymous"></script>
|
integrity="sha256-T/f7Sju1ZfNNfBh7skWn0idlCBcI3RwdLSS4/I7NQKQ=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script>
|
<script src="/assets/js/auth.js"></script>
|
||||||
async function getToken() {
|
<script src="/assets/js/loadingscreen.js"></script>
|
||||||
let token = window.localStorage.getItem('token');
|
<script src="/assets/js/utils.js"></script>
|
||||||
if (token == null) {
|
|
||||||
let res = await axios.post('/token', {
|
|
||||||
withCredentials: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status !== 200 || !res.data.success) {
|
<!-- Jquery -->
|
||||||
console.log("An error occurred whilst retrieving an authentication token. Please contact the developer");
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.slim.min.js"
|
||||||
console.log(res);
|
integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous"></script>
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = res.data.token;
|
|
||||||
localStorage.setItem('token', token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setDefaultHeader() {
|
|
||||||
const token = await getToken();
|
|
||||||
axios.defaults.headers.common['Authorization'] = token;
|
|
||||||
axios.defaults.validateStatus = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultHeader();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function showLoadingScreen() {
|
|
||||||
const content = document.getElementsByClassName('content')[0];
|
|
||||||
content.style.display = 'none';
|
|
||||||
document.getElementById('loading-container').style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideLoadingScreen() {
|
|
||||||
document.getElementById('loading-container').style.display = 'none';
|
|
||||||
|
|
||||||
const content = document.getElementsByClassName('content')[0];
|
|
||||||
content.style.display = 'block';
|
|
||||||
content.classList.add('fade-in');
|
|
||||||
}
|
|
||||||
|
|
||||||
function sleep(ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function withLoadingScreen(func) {
|
|
||||||
showLoadingScreen();
|
|
||||||
await func();
|
|
||||||
hideLoadingScreen();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<!-- Bootstrap -->
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css"
|
||||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
integrity="sha256-aAr2Zpq8MZ+YA/D6JtRD3xtrwpEz2IqOS+pWD/7XKIw=" crossorigin="anonymous"/>
|
||||||
<link href="/assets/css/light-bootstrap-dashboard.css?v=1.4.0" rel="stylesheet"/>
|
<link href="/assets/css/light-bootstrap-dashboard.css" rel="stylesheet"/>
|
||||||
<link href="/assets/css/animate.min.css" rel="stylesheet"/>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
|
||||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
integrity="sha256-6hqHMqXTVEds1R8HgKisLm3l/doneQs+rS1a5NLmwwo=" crossorigin="anonymous"/>
|
||||||
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
|
||||||
integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
|
integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"
|
||||||
integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
|
integrity="sha256-OFRAJNoaD8L3Br5lglV7VyLRf0itmoBzWUoM+Sji4/8=" crossorigin="anonymous"></script>
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
|
<!-- Bootstrap select -->
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.17/css/bootstrap-select.min.css"
|
||||||
|
integrity="sha256-VMPhaMmJn7coDSbzwqB0jflvb+CDnoAlfStC5RogOQo=" crossorigin="anonymous"/>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.17/js/bootstrap-select.min.js"
|
||||||
|
integrity="sha256-QOE02Glo1C1gHzP96JOaxyIMt4XSFv/exZaYLY4dwO0=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$.fn.selectpicker.Constructor.BootstrapVersion = '4';
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Discord theme -->
|
<!-- Discord theme -->
|
||||||
<link href="/assets/css/discordmock.css" rel="stylesheet"/>
|
<link href="/assets/css/discordmock.css" rel="stylesheet"/>
|
||||||
@ -112,54 +68,15 @@
|
|||||||
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
||||||
|
|
||||||
<!-- GA -->
|
<!-- GA -->
|
||||||
|
<script async src='https://www.google-analytics.com/analytics.js'
|
||||||
|
type="80be96f83bbfbba3d4097e23-text/javascript"></script>
|
||||||
|
|
||||||
<script type="80be96f83bbfbba3d4097e23-text/javascript">
|
<script type="80be96f83bbfbba3d4097e23-text/javascript">
|
||||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||||
ga('create', 'UA-161945537-1', 'auto');
|
ga('create', 'UA-161945537-1', 'auto');
|
||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<script async src='https://www.google-analytics.com/analytics.js'
|
|
||||||
type="80be96f83bbfbba3d4097e23-text/javascript"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function showToast(title, content) {
|
|
||||||
const container = document.getElementById('toast-container');
|
|
||||||
|
|
||||||
container.innerHTML += `
|
|
||||||
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
|
||||||
<div class="toast-header">
|
|
||||||
<strong class="mr-auto">${title}</strong>
|
|
||||||
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="toast-body">
|
|
||||||
${content}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
$('.toast').toast('show');
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendTd(tr, content) {
|
|
||||||
const td = document.createElement('td');
|
|
||||||
td.appendChild(document.createTextNode(content));
|
|
||||||
td.classList.add('white');
|
|
||||||
tr.appendChild(td);
|
|
||||||
return td
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendButton(tr, content, onclick) {
|
|
||||||
const tdRemove = document.createElement('td');
|
|
||||||
const removeButton = document.createElement('button');
|
|
||||||
removeButton.type = 'submit';
|
|
||||||
removeButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
|
|
||||||
removeButton.appendChild(document.createTextNode(content));
|
|
||||||
removeButton.onclick = onclick;
|
|
||||||
tdRemove.appendChild(removeButton);
|
|
||||||
tr.appendChild(tdRemove);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
@ -97,6 +97,8 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="collapse" id="advanced" style="width: 100%">
|
<div class="collapse" id="advanced" style="width: 100%">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
<div class="col-md-6 pr-1">
|
<div class="col-md-6 pr-1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="black">Welcome Message
|
<label class="black">Welcome Message
|
||||||
@ -105,6 +107,16 @@
|
|||||||
<textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea>
|
<textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="black" for="mentions">Mention On Open</label>
|
||||||
|
<select class="selectpicker form-control" id="mentions" multiple data-live-search="true" data-dropup-auto="false" data-size="5" data-display="static">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -168,7 +180,8 @@
|
|||||||
colour: parseInt(`0x${document.getElementById('colour').value.slice(1)}`),
|
colour: parseInt(`0x${document.getElementById('colour').value.slice(1)}`),
|
||||||
channel_id: document.getElementById('channel-container').options[document.getElementById('channel-container').selectedIndex].value,
|
channel_id: document.getElementById('channel-container').options[document.getElementById('channel-container').selectedIndex].value,
|
||||||
category_id: document.getElementById('category-container').options[document.getElementById('category-container').selectedIndex].value,
|
category_id: document.getElementById('category-container').options[document.getElementById('category-container').selectedIndex].value,
|
||||||
welcome_message: welcomeMessage === '' ? null : welcomeMessage
|
welcome_message: welcomeMessage === '' ? null : welcomeMessage,
|
||||||
|
mentions: $('.selectpicker').val()
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await axios.put('/api/{{.guildId}}/panels', data);
|
const res = await axios.put('/api/{{.guildId}}/panels', data);
|
||||||
@ -256,13 +269,42 @@
|
|||||||
return res.data.length;
|
return res.data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fillMentions() {
|
||||||
|
const select = document.getElementById('mentions');
|
||||||
|
|
||||||
|
// ticket opener
|
||||||
|
const ticketOpener = document.createElement('option');
|
||||||
|
ticketOpener.value = 'user';
|
||||||
|
ticketOpener.appendChild(document.createTextNode('Ticket Opener'));
|
||||||
|
select.appendChild(ticketOpener);
|
||||||
|
|
||||||
|
// roles
|
||||||
|
const res = await axios.get('/api/{{.guildId}}/roles');
|
||||||
|
if (res.status !== 200 || !res.data.success) {
|
||||||
|
showToast("Error", res.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.data.roles !== null) {
|
||||||
|
for (const role of res.data.roles) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = role.id;
|
||||||
|
option.appendChild(document.createTextNode(role.name));
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.selectpicker').selectpicker('refresh');
|
||||||
|
}
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
const channels = await getChannels();
|
const channels = await getChannels();
|
||||||
|
|
||||||
const panelCount = await fillPanels(channels);
|
const panelCount = await fillPanels(channels);
|
||||||
|
|
||||||
await fillPanelQuota(panelCount);
|
await fillPanelQuota(panelCount);
|
||||||
await fillChannels(channels);
|
await fillChannels(channels);
|
||||||
await fillCategories(channels);
|
await fillCategories(channels);
|
||||||
|
await fillMentions();
|
||||||
}
|
}
|
||||||
|
|
||||||
withLoadingScreen(loadData);
|
withLoadingScreen(loadData);
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
const members = ticket.members.map(member => `${member.username}#${member.discrim}`).join(', ');
|
const members = ticket.members.map(member => `${member.username}#${member.discrim}`).join(', ');
|
||||||
appendTd(tr, members);
|
appendTd(tr, members);
|
||||||
|
|
||||||
appendButton(tr, 'View', () => { console.log(ticket); location.href = '/manage/{{.guildId}}/tickets/view/' + ticket.ticketId });
|
appendButton(tr, 'View', () => { location.href = '/manage/{{.guildId}}/tickets/view/' + ticket.ticketId });
|
||||||
|
|
||||||
container.appendChild(tr);
|
container.appendChild(tr);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user