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/bootstrap.min.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
|
||||
}
|
||||
|
||||
// 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{
|
||||
"success": true,
|
||||
"message_id": strconv.FormatUint(msgId, 10),
|
||||
|
@ -1,19 +1,23 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/TicketsBot/GoPanel/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type panel struct {
|
||||
ChannelId uint64 `json:"channel_id,string"`
|
||||
MessageId uint64 `json:"message_id,string"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Colour uint32 `json:"colour"`
|
||||
CategoryId uint64 `json:"category_id,string"`
|
||||
Emote string `json:"emote"`
|
||||
WelcomeMessage *string `json:"welcome_message"`
|
||||
ChannelId uint64 `json:"channel_id,string"`
|
||||
MessageId uint64 `json:"message_id,string"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Colour uint32 `json:"colour"`
|
||||
CategoryId uint64 `json:"category_id,string"`
|
||||
Emote string `json:"emote"`
|
||||
WelcomeMessage *string `json:"welcome_message"`
|
||||
Mentions []string `json:"mentions"`
|
||||
}
|
||||
|
||||
func ListPanels(ctx *gin.Context) {
|
||||
@ -30,17 +34,56 @@ func ListPanels(ctx *gin.Context) {
|
||||
|
||||
wrapped := make([]panel, len(panels))
|
||||
|
||||
// we will need to lookup role mentions
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
for i, p := range panels {
|
||||
wrapped[i] = panel{
|
||||
ChannelId: p.ChannelId,
|
||||
MessageId: p.MessageId,
|
||||
Title: p.Title,
|
||||
Content: p.Content,
|
||||
Colour: uint32(p.Colour),
|
||||
CategoryId: p.TargetCategory,
|
||||
Emote: p.ReactionEmote,
|
||||
WelcomeMessage: p.WelcomeMessage,
|
||||
}
|
||||
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{
|
||||
MessageId: p.MessageId,
|
||||
ChannelId: p.ChannelId,
|
||||
Title: p.Title,
|
||||
Content: p.Content,
|
||||
Colour: uint32(p.Colour),
|
||||
CategoryId: p.TargetCategory,
|
||||
Emote: p.ReactionEmote,
|
||||
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)
|
||||
|
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("/premium", api.PremiumHandler)
|
||||
guildAuthApi.GET("/user/:user", api.UserHandler)
|
||||
guildAuthApi.GET("/roles", api.RolesHandler)
|
||||
|
||||
guildAuthApi.GET("/settings", api.GetSettingsHandler)
|
||||
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/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306
|
||||
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/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
||||
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;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
/*.dropdown-menu {
|
||||
visibility: hidden;
|
||||
margin: 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:focus {
|
||||
background-color: rgba(255, 74, 85, 0.2);
|
||||
}
|
||||
}*/
|
||||
|
||||
.dropdown-with-icons > li > a {
|
||||
padding-left: 0px;
|
||||
@ -3211,11 +3211,6 @@ fieldset[disabled] .btn-neutral.active {
|
||||
padding-right: 5px;
|
||||
}
|
||||
.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);
|
||||
-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);
|
||||
|
@ -15,7 +15,7 @@ body {
|
||||
color: white;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
html > ::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -161,3 +161,37 @@ body {
|
||||
.content {
|
||||
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);
|
||||
}
|
@ -14,95 +14,51 @@
|
||||
<link href="/assets/css/style.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
#avatar-sidebar {
|
||||
background: url("{{.avatar}}?size=256") center center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
float: right;
|
||||
|
||||
}
|
||||
#avatar-sidebar {
|
||||
background: url("{{.avatar}}?size=256") center center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
float: right;
|
||||
}
|
||||
</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"
|
||||
integrity="sha256-T/f7Sju1ZfNNfBh7skWn0idlCBcI3RwdLSS4/I7NQKQ=" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
async function getToken() {
|
||||
let token = window.localStorage.getItem('token');
|
||||
if (token == null) {
|
||||
let res = await axios.post('/token', {
|
||||
withCredentials: true
|
||||
});
|
||||
<script src="/assets/js/auth.js"></script>
|
||||
<script src="/assets/js/loadingscreen.js"></script>
|
||||
<script src="/assets/js/utils.js"></script>
|
||||
|
||||
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();
|
||||
</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>
|
||||
<!-- Jquery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.slim.min.js"
|
||||
integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<link href="/assets/css/light-bootstrap-dashboard.css?v=1.4.0" rel="stylesheet"/>
|
||||
<link href="/assets/css/animate.min.css" rel="stylesheet"/>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
|
||||
crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css"
|
||||
integrity="sha256-aAr2Zpq8MZ+YA/D6JtRD3xtrwpEz2IqOS+pWD/7XKIw=" crossorigin="anonymous"/>
|
||||
<link href="/assets/css/light-bootstrap-dashboard.css" rel="stylesheet"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
|
||||
integrity="sha256-6hqHMqXTVEds1R8HgKisLm3l/doneQs+rS1a5NLmwwo=" crossorigin="anonymous"/>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
|
||||
integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
|
||||
integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"
|
||||
integrity="sha256-OFRAJNoaD8L3Br5lglV7VyLRf0itmoBzWUoM+Sji4/8=" 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 -->
|
||||
<link href="/assets/css/discordmock.css" rel="stylesheet"/>
|
||||
@ -112,54 +68,15 @@
|
||||
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
||||
|
||||
<!-- GA -->
|
||||
<script async src='https://www.google-analytics.com/analytics.js'
|
||||
type="80be96f83bbfbba3d4097e23-text/javascript"></script>
|
||||
|
||||
<script type="80be96f83bbfbba3d4097e23-text/javascript">
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
ga('create', 'UA-161945537-1', 'auto');
|
||||
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>
|
||||
{{end}}
|
@ -97,12 +97,24 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="collapse" id="advanced" style="width: 100%">
|
||||
<div class="col-md-6 pr-1">
|
||||
<div class="form-group">
|
||||
<label class="black">Welcome Message
|
||||
<i class="fas fa-question pointer" data-toggle="modal" data-target="#modal-substitutions"></i>
|
||||
</label>
|
||||
<textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6 pr-1">
|
||||
<div class="form-group">
|
||||
<label class="black">Welcome Message
|
||||
<i class="fas fa-question pointer" data-toggle="modal" data-target="#modal-substitutions"></i>
|
||||
</label>
|
||||
<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 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>
|
||||
@ -168,7 +180,8 @@
|
||||
colour: parseInt(`0x${document.getElementById('colour').value.slice(1)}`),
|
||||
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,
|
||||
welcome_message: welcomeMessage === '' ? null : welcomeMessage
|
||||
welcome_message: welcomeMessage === '' ? null : welcomeMessage,
|
||||
mentions: $('.selectpicker').val()
|
||||
};
|
||||
|
||||
const res = await axios.put('/api/{{.guildId}}/panels', data);
|
||||
@ -256,13 +269,42 @@
|
||||
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() {
|
||||
const channels = await getChannels();
|
||||
|
||||
const panelCount = await fillPanels(channels);
|
||||
|
||||
await fillPanelQuota(panelCount);
|
||||
await fillChannels(channels);
|
||||
await fillCategories(channels);
|
||||
await fillMentions();
|
||||
}
|
||||
|
||||
withLoadingScreen(loadData);
|
||||
|
@ -39,7 +39,7 @@
|
||||
const members = ticket.members.map(member => `${member.username}#${member.discrim}`).join(', ');
|
||||
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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user