diff --git a/shared/js/main.ts b/shared/js/main.ts
index e37c4dbe..969769a4 100644
--- a/shared/js/main.ts
+++ b/shared/js/main.ts
@@ -397,6 +397,57 @@ function main() {
}
}
+ $(document).on('contextmenu', event => {
+ if(event.isDefaultPrevented())
+ return;
+
+ event.preventDefault();
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, {
+ name: 'Test item 1',
+ type: contextmenu.MenuEntryType.ENTRY,
+ callback: () => console.log("Test 1 item clicked!"),
+
+ disabled: true
+ }, {
+ name: 'Test item 2',
+ type: contextmenu.MenuEntryType.ENTRY,
+ callback: () => console.log("Test 2 item clicked!")
+ }, {
+ name: 'Test item 3',
+ type: contextmenu.MenuEntryType.SUB_MENU,
+ callback: () => console.log("Test 3 item clicked!"),
+ sub_menu: [
+ {
+ name: 'Test sub item 1',
+ type: contextmenu.MenuEntryType.ENTRY,
+ callback: () => console.log("Test sub 1 item clicked!")
+ }, {
+ name: 'Test sub item 2',
+ type: contextmenu.MenuEntryType.ENTRY,
+ callback: () => console.log("Test sub 2 item clicked!")
+ }, {
+ name: 'Test sub item 4',
+ type: contextmenu.MenuEntryType.ENTRY,
+ callback: () => console.log("Test sub 3 item clicked!")
+ }
+ ]
+ },
+ contextmenu.Entry.HR(),
+ {
+ name: 'Test item 4',
+ type: contextmenu.MenuEntryType.ENTRY,
+ callback: () => console.log("Test 4 item clicked!")
+ }, {
+ name: 'Test item 5',
+ type: contextmenu.MenuEntryType.CHECKBOX,
+ callback: () => console.log("Test 5 item clicked!")
+ }, {
+ name: 'Test item 5',
+ type: contextmenu.MenuEntryType.CHECKBOX,
+ callback: () => console.log("Test 5 item clicked!"),
+ checkbox_checked: true
+ }, contextmenu.Entry.CLOSE(() => console.log("Menu closed!")))
+ })
}
const task_teaweb_starter: loader.Task = {
diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts
index f2fd0602..e15ea472 100644
--- a/shared/js/ui/channel.ts
+++ b/shared/js/ui/channel.ts
@@ -452,58 +452,60 @@ class ChannelEntry {
}
let trigger_close = true;
- spawn_context_menu(x, y, {
- type: MenuEntryType.ENTRY,
+
+ const bold = text => contextmenu.get_provider().html_format_enabled() ? "" + text + "" : text;
+ contextmenu.spawn_context_menu(x, y, {
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show channel info"),
callback: () => {
trigger_close = false;
this.channelTree.client.select_info.open_popover()
},
- icon: "client-about",
+ icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover()
}, {
- type: MenuEntryType.HR,
+ type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(),
name: ''
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-channel_switch",
- name: tr("Switch to channel"),
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-channel_switch",
+ name: bold(tr("Switch to channel")),
callback: () => this.joinChannel()
},
...(() => {
const local_client = this.channelTree.client.getClient();
if (!local_client || local_client.currentChannel() !== this)
return [
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
{
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
icon: "client-subscribe_to_channel",
- name: tr("Subscribe to channel"),
+ name: bold(tr("Subscribe to channel")),
callback: () => this.subscribe(),
visible: !this.flag_subscribed
},
{
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_unsubscribed",
- name: tr("Unsubscribe from channel"),
+ name: bold(tr("Unsubscribe from channel")),
callback: () => this.unsubscribe(),
visible: this.flag_subscribed
},
{
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
icon: "client-subscribe_mode",
- name: tr("Use inherited subscribe mode"),
+ name: bold(tr("Use inherited subscribe mode")),
callback: () => this.unsubscribe(true),
visible: this.subscribe_mode != ChannelSubscribeMode.INHERITED
}
];
return [];
})(),
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
{
- type: MenuEntryType.ENTRY,
- icon: "client-channel_edit",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-channel_edit",
name: tr("Edit channel"),
invalidPermission: !channelModify,
callback: () => {
@@ -536,8 +538,8 @@ class ChannelEntry {
}
},
{
- type: MenuEntryType.ENTRY,
- icon: "client-channel_delete",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-channel_delete",
name: tr("Delete channel"),
invalidPermission: !flagDelete,
callback: () => {
@@ -546,10 +548,10 @@ class ChannelEntry {
})
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
{
- type: MenuEntryType.ENTRY,
- icon: "client-addon-collection",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-addon-collection",
name: tr("Create music bot"),
callback: () => {
this.channelTree.client.serverConnection.send_command("musicbotcreate", {cid: this.channelId}).then(() => {
@@ -563,21 +565,21 @@ class ChannelEntry {
});
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
{
- type: MenuEntryType.ENTRY,
- icon: "client-channel_create_sub",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-channel_create_sub",
name: tr("Create sub channel"),
invalidPermission: !(channelCreate && this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_CHILD).granted(1)),
callback: () => this.channelTree.spawnCreateChannel(this)
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-channel_create",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-channel_create",
name: tr("Create channel"),
invalidPermission: !channelCreate,
callback: () => this.channelTree.spawnCreateChannel()
},
- MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})())
+ contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
);
}
diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts
index ed21918a..6a14ef6d 100644
--- a/shared/js/ui/client.ts
+++ b/shared/js/ui/client.ts
@@ -169,20 +169,15 @@ class ClientEntry {
});
}
- protected assignment_context() : ContextMenuEntry[] {
- let server_groups: ContextMenuEntry[] = [];
+ protected assignment_context() : contextmenu.MenuEntry[] {
+ let server_groups: contextmenu.MenuEntry[] = [];
for(let group of this.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) {
if(group.type != GroupType.NORMAL) continue;
- let entry: ContextMenuEntry = {} as any;
-
- {
- let tag = $.spawn("label").addClass("checkbox");
- $.spawn("input").attr("type", "checkbox").prop("checked", this.groupAssigned(group)).appendTo(tag);
- $.spawn("span").addClass("checkmark").appendTo(tag);
- entry.icon = tag;
- }
+ let entry: contextmenu.MenuEntry = {} as any;
+ //TODO: May add the server group icon?
+ entry.checkbox_checked = this.groupAssigned(group);
entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]";
if(this.groupAssigned(group)) {
entry.callback = () => {
@@ -201,21 +196,19 @@ class ClientEntry {
};
entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberAddPower);
}
- entry.type = MenuEntryType.ENTRY;
+ entry.type = contextmenu.MenuEntryType.CHECKBOX;
+
server_groups.push(entry);
}
- let channel_groups: ContextMenuEntry[] = [];
+ let channel_groups: contextmenu.MenuEntry[] = [];
for(let group of this.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) {
if(group.type != GroupType.NORMAL) continue;
- let entry: ContextMenuEntry = {} as any;
- {
- let tag = $.spawn("label").addClass("checkbox");
- $.spawn("input").attr("type", "checkbox").prop("checked", this.assignedChannelGroup() == group.id).appendTo(tag);
- $.spawn("span").addClass("checkmark").appendTo(tag);
- entry.icon = tag;
- }
+ let entry: contextmenu.MenuEntry = {} as any;
+
+ //TODO: May add the channel group icon?
+ entry.checkbox_checked = this.assignedChannelGroup() == group.id;
entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]";
entry.callback = () => {
this.channelTree.client.serverConnection.send_command("setclientchannelgroup", {
@@ -225,17 +218,17 @@ class ClientEntry {
});
};
entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower);
- entry.type = MenuEntryType.ENTRY;
+ entry.type = contextmenu.MenuEntryType.CHECKBOX;
channel_groups.push(entry);
}
return [{
- type: MenuEntryType.SUB_MENU,
- icon: "client-permission_server_groups",
+ type: contextmenu.MenuEntryType.SUB_MENU,
+ icon_class: "client-permission_server_groups",
name: tr("Set server group"),
sub_menu: [
{
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
icon: "client-permission_server_groups",
name: "Server groups dialog",
callback: () => {
@@ -253,19 +246,19 @@ class ClientEntry {
});
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
...server_groups
]
},{
- type: MenuEntryType.SUB_MENU,
- icon: "client-permission_channel",
+ type: contextmenu.MenuEntryType.SUB_MENU,
+ icon_class: "client-permission_channel",
name: tr("Set channel group"),
sub_menu: [
...channel_groups
]
},{
- type: MenuEntryType.SUB_MENU,
- icon: "client-permission_client",
+ type: contextmenu.MenuEntryType.SUB_MENU,
+ icon_class: "client-permission_client",
name: tr("Permissions"),
disabled: true,
sub_menu: [ ]
@@ -274,31 +267,33 @@ class ClientEntry {
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
let trigger_close = true;
- spawn_context_menu(x, y,
+ contextmenu.spawn_context_menu(x, y,
{
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show client info"),
callback: () => {
trigger_close = false;
this.channelTree.client.select_info.open_popover()
},
- icon: "client-about",
+ icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover()
}, {
- type: MenuEntryType.HR,
+ type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(),
name: ''
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-change_nickname",
- name: tr("Open text chat"),
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-change_nickname",
+ name: (contextmenu.get_provider().html_format_enabled() ? "" : "") +
+ tr("Open text chat") +
+ (contextmenu.get_provider().html_format_enabled() ? "" : ""),
callback: () => {
this.channelTree.client.chat.activeChat = this.chat(true);
this.channelTree.client.chat.focus();
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-poke",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-poke",
name: tr("Poke client"),
callback: () => {
createInputModal(tr("Poke client"), tr("Poke message:
"), text => true, result => {
@@ -314,8 +309,8 @@ class ClientEntry {
}, { width: 400, maxLength: 512 }).open();
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-edit",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-edit",
name: tr("Change description"),
callback: () => {
createInputModal(tr("Change client description"), tr("New description:
"), text => true, result => {
@@ -331,11 +326,11 @@ class ClientEntry {
}, { width: 400, maxLength: 1024 }).open();
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
...this.assignment_context(),
- MenuEntry.HR(), {
- type: MenuEntryType.ENTRY,
- icon: "client-move_client_to_own_channel",
+ contextmenu.Entry.HR(), {
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-move_client_to_own_channel",
name: tr("Move client to your channel"),
callback: () => {
this.channelTree.client.serverConnection.send_command("clientmove", {
@@ -344,8 +339,8 @@ class ClientEntry {
});
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-kick_channel",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-kick_channel",
name: tr("Kick client from channel"),
callback: () => {
createInputModal(tr("Kick client from channel"), tr("Kick reason:
"), text => true, result => {
@@ -362,8 +357,8 @@ class ClientEntry {
}, { width: 400, maxLength: 255 }).open();
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-kick_server",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-kick_server",
name: tr("Kick client fom server"),
callback: () => {
createInputModal(tr("Kick client from server"), tr("Kick reason:
"), text => true, result => {
@@ -380,8 +375,8 @@ class ClientEntry {
}, { width: 400, maxLength: 255 }).open();
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-ban_client",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-ban_client",
name: tr("Ban client"),
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
callback: () => {
@@ -398,7 +393,7 @@ class ClientEntry {
});
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
/*
{
type: MenuEntryType.ENTRY,
@@ -418,8 +413,8 @@ class ClientEntry {
MenuEntry.HR(),
*/
{
- type: MenuEntryType.ENTRY,
- icon: "client-volume",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-volume",
name: tr("Change Volume"),
callback: () => {
Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => {
@@ -430,7 +425,7 @@ class ClientEntry {
});
}
},
- MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})())
+ contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
);
}
@@ -839,15 +834,18 @@ class LocalClientEntry extends ClientEntry {
showContextMenu(x: number, y: number, on_close: () => void = undefined): void {
const _self = this;
- spawn_context_menu(x, y,
+ contextmenu.spawn_context_menu(x, y,
{
- name: tr("Change name"),
- icon: "client-change_nickname",
+
+ name: (contextmenu.get_provider().html_format_enabled() ? "" : "") +
+ tr("Change name") +
+ (contextmenu.get_provider().html_format_enabled() ? "" : ""),
+ icon_class: "client-change_nickname",
callback: () =>_self.openRename(),
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
}, {
name: tr("Change description"),
- icon: "client-edit",
+ icon_class: "client-edit",
callback: () => {
createInputModal(tr("Change own description"), tr("New description:
"), text => true, result => {
if(result) {
@@ -860,11 +858,11 @@ class LocalClientEntry extends ClientEntry {
}
}, { width: 400, maxLength: 1024 }).open();
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
...this.assignment_context(),
- MenuEntry.CLOSE(on_close)
+ contextmenu.Entry.CLOSE(on_close)
);
}
@@ -968,23 +966,23 @@ class MusicClientEntry extends ClientEntry {
showContextMenu(x: number, y: number, on_close: () => void = undefined): void {
let trigger_close = true;
- spawn_context_menu(x, y,
+ contextmenu.spawn_context_menu(x, y,
{
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show bot info"),
callback: () => {
trigger_close = false;
this.channelTree.client.select_info.open_popover()
},
- icon: "client-about",
+ icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover()
}, {
- type: MenuEntryType.HR,
+ type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(),
name: ''
}, {
name: tr("Change bot name"),
- icon: "client-change_nickname",
+ icon_class: "client-change_nickname",
disabled: false,
callback: () => {
createInputModal(tr("Change music bots nickname"), tr("New nickname:
"), text => text.length >= 3 && text.length <= 31, result => {
@@ -997,10 +995,10 @@ class MusicClientEntry extends ClientEntry {
}
}, { width: 400, maxLength: 255 }).open();
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
}, {
name: tr("Change bot description"),
- icon: "client-edit",
+ icon_class: "client-edit",
disabled: false,
callback: () => {
createInputModal(tr("Change music bots description"), tr("New description:
"), text => true, result => {
@@ -1013,7 +1011,7 @@ class MusicClientEntry extends ClientEntry {
}
}, { width: 400, maxLength: 255 }).open();
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
},
/*
{
@@ -1026,7 +1024,7 @@ class MusicClientEntry extends ClientEntry {
*/
{
name: tr("Open bot's playlist"),
- icon: "client-edit",
+ icon_class: "client-edit",
disabled: false,
callback: () => {
this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => {
@@ -1041,11 +1039,11 @@ class MusicClientEntry extends ClientEntry {
createErrorModal(tr("Failed to query playlist."), tr("Failed to query playlist info.")).open();
});
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
},
{
name: tr("Quick url replay"),
- icon: "client-edit",
+ icon_class: "client-edit",
disabled: false,
callback: () => {
createInputModal(tr("Please enter the URL"), tr("URL:"), text => true, result => {
@@ -1064,13 +1062,13 @@ class MusicClientEntry extends ClientEntry {
}
}, { width: 400, maxLength: 255 }).open();
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
...super.assignment_context(),
- MenuEntry.HR(),{
- type: MenuEntryType.ENTRY,
- icon: "client-move_client_to_own_channel",
+ contextmenu.Entry.HR(),{
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-move_client_to_own_channel",
name: tr("Move client to your channel"),
callback: () => {
this.channelTree.client.serverConnection.send_command("clientmove", {
@@ -1079,8 +1077,8 @@ class MusicClientEntry extends ClientEntry {
});
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-kick_channel",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-kick_channel",
name: tr("Kick client from channel"),
callback: () => {
createInputModal(tr("Kick client from channel"), tr("Kick reason:
"), text => true, result => {
@@ -1095,10 +1093,10 @@ class MusicClientEntry extends ClientEntry {
}, { width: 400, maxLength: 255 }).open();
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
{
- type: MenuEntryType.ENTRY,
- icon: "client-volume",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-volume",
name: tr("Change local volume"),
callback: () => {
Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => {
@@ -1110,8 +1108,8 @@ class MusicClientEntry extends ClientEntry {
}
},
{
- type: MenuEntryType.ENTRY,
- icon: "client-volume",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-volume",
name: tr("Change remote volume"),
callback: () => {
let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value;
@@ -1132,10 +1130,10 @@ class MusicClientEntry extends ClientEntry {
});
}
},
- MenuEntry.HR(),
+ contextmenu.Entry.HR(),
{
name: tr("Delete bot"),
- icon: "client-delete",
+ icon_class: "client-delete",
disabled: false,
callback: () => {
const tag = $.spawn("div").append(MessageHelper.formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false)));
@@ -1147,9 +1145,9 @@ class MusicClientEntry extends ClientEntry {
}
});
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
},
- MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})())
+ contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
);
}
diff --git a/shared/js/ui/elements/context_menu.ts b/shared/js/ui/elements/context_menu.ts
index c8f89f81..ce5f0488 100644
--- a/shared/js/ui/elements/context_menu.ts
+++ b/shared/js/ui/elements/context_menu.ts
@@ -1,142 +1,220 @@
-let context_menu: JQuery;
+namespace contextmenu {
+ export interface MenuEntry {
+ callback?: () => void;
+ type: MenuEntryType;
+ name: (() => string) | string;
+ icon_class?: string;
+ icon_path?: string;
+ disabled?: boolean;
+ visible?: boolean;
-$(document).bind("click", function (e) {
- let menu = context_menu || (context_menu = $(".context-menu"));
+ checkbox_checked?: boolean;
- if(!menu.is(":visible")) return;
+ invalidPermission?: boolean;
+ sub_menu?: MenuEntry[];
+ }
- if ($(e.target).parents(".context-menu").length == 0) {
+ export enum MenuEntryType {
+ CLOSE,
+ ENTRY,
+ CHECKBOX,
+ HR,
+ SUB_MENU
+ }
+
+ export class Entry {
+ static HR() {
+ return {
+ callback: () => {},
+ type: MenuEntryType.HR,
+ name: "",
+ icon: ""
+ };
+ };
+
+ static CLOSE(callback: () => void) {
+ return {
+ callback: callback,
+ type: MenuEntryType.CLOSE,
+ name: "",
+ icon: ""
+ };
+ }
+ }
+
+ export interface ContextMenuProvider {
despawn_context_menu();
- e.preventDefault();
+ spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]);
+
+ initialize();
+ finalize();
+
+ html_format_enabled() : boolean;
}
-});
-let contextMenuCloseFn = undefined;
-function despawn_context_menu() {
- let menu = context_menu || (context_menu = $(".context-menu"));
+ let provider: ContextMenuProvider;
+ export function spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]) {
+ if(!provider) {
+ console.error(tr("Failed to spawn context menu! Missing provider!"));
+ return;
+ }
- if(!menu.is(":visible")) return;
- menu.animate({opacity: 0}, 100, () => menu.css("display", "none"));
- if(contextMenuCloseFn) contextMenuCloseFn();
-}
+ provider.spawn_context_menu(x, y, ...entries);
+ }
-enum MenuEntryType {
- CLOSE,
- ENTRY,
- HR,
- SUB_MENU
-}
+ export function despawn_context_menu() {
+ if(!provider)
+ return;
-class MenuEntry {
- static HR() {
- return {
- callback: () => {},
- type: MenuEntryType.HR,
- name: "",
- icon: ""
- };
- };
+ provider.despawn_context_menu();
+ }
- static CLOSE(callback: () => void) {
- return {
- callback: callback,
- type: MenuEntryType.CLOSE,
- name: "",
- icon: ""
- };
+ export function get_provider() : ContextMenuProvider { return provider; }
+ export function set_provider(_provider: ContextMenuProvider) {
+ provider = _provider;
+ provider.initialize();
}
}
-interface ContextMenuEntry {
- callback?: () => void;
- type: MenuEntryType;
- name: (() => string) | string;
- icon?: (() => string) | string | JQuery;
- disabled?: boolean;
- visible?: boolean;
+class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
+ private _global_click_listener: (event) => any;
+ private _context_menu: JQuery;
+ private _close_callbacks: (() => any)[] = [];
- invalidPermission?: boolean;
- sub_menu?: ContextMenuEntry[];
-}
+ despawn_context_menu() {
+ let menu = this._context_menu || (this._context_menu = $(".context-menu"));
-function generate_tag(entry: ContextMenuEntry) : JQuery {
- if(entry.type == MenuEntryType.HR) {
- return $.spawn("hr");
- } else if(entry.type == MenuEntryType.ENTRY) {
- console.log(entry.icon);
- let icon = $.isFunction(entry.icon) ? entry.icon() : entry.icon;
- if(typeof(icon) === "string") {
+ if(!menu.is(":visible"))
+ return;
+
+ menu.animate({opacity: 0}, 100, () => menu.css("display", "none"));
+ for(const callback of this._close_callbacks)
+ callback();
+ this._close_callbacks = [];
+ }
+
+ finalize() {
+ $(document).unbind('click', this._global_click_listener);
+ }
+
+ initialize() {
+ this._global_click_listener = this.on_global_click.bind(this);
+ $(document).bind('click', this._global_click_listener);
+ }
+
+ private on_global_click(event) {
+ let menu = this._context_menu || (this._context_menu = $(".context-menu"));
+
+ if(!menu.is(":visible")) return;
+
+ if ($(event.target).parents(".context-menu").length == 0) {
+ this.despawn_context_menu();
+ event.preventDefault();
+ }
+ }
+
+ private generate_tag(entry: contextmenu.MenuEntry) : JQuery {
+ if(entry.type == contextmenu.MenuEntryType.HR) {
+ return $.spawn("hr");
+ } else if(entry.type == contextmenu.MenuEntryType.ENTRY) {
+ let icon = entry.icon_class;
if(!icon || icon.length == 0) icon = "icon_empty";
else icon = "icon " + icon;
- }
- let tag = $.spawn("div").addClass("entry");
- tag.append(typeof(icon) === "string" ? $.spawn("div").addClass(icon) : icon);
- tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
+ let tag = $.spawn("div").addClass("entry");
+ tag.append($.spawn("div").addClass(icon));
+ tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
- if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
- else {
- tag.click(function () {
- if($.isFunction(entry.callback)) entry.callback();
- despawn_context_menu();
- });
- }
- return tag;
- } else if(entry.type == MenuEntryType.SUB_MENU) {
- let icon = $.isFunction(entry.icon) ? entry.icon() : entry.icon;
- if(typeof(icon) === "string") {
- if(!icon || icon.length == 0) icon = "icon_empty";
- else icon = "icon " + icon;
- }
-
- let tag = $.spawn("div").addClass("entry").addClass("sub-container");
- tag.append(typeof(icon) === "string" ? $.spawn("div").addClass(icon) : icon);
- tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
-
- tag.append($.spawn("div").addClass("arrow right"));
-
- if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
- else {
- let menu = $.spawn("div").addClass("sub-menu").addClass("context-menu-container");
- for(const e of entry.sub_menu) {
- if(typeof(entry.visible) === 'boolean' && !entry.visible)
- continue;
- menu.append(generate_tag(e));
+ if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
+ else {
+ tag.click( () => {
+ if($.isFunction(entry.callback))
+ entry.callback();
+ this.despawn_context_menu();
+ });
}
- menu.appendTo(tag);
+ return tag;
+ } else if(entry.type == contextmenu.MenuEntryType.CHECKBOX) {
+ let checkbox = $.spawn("label").addClass("checkbox");
+ $.spawn("input").attr("type", "checkbox").prop("checked", !!entry.checkbox_checked).appendTo(checkbox);
+ $.spawn("span").addClass("checkmark").appendTo(checkbox);
+
+ let tag = $.spawn("div").addClass("entry");
+ tag.append(checkbox);
+ tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
+
+ if(entry.disabled || entry.invalidPermission)
+ tag.addClass("disabled");
+ else {
+ tag.click( () => {
+ if($.isFunction(entry.callback))
+ entry.callback();
+ this.despawn_context_menu();
+ });
+ }
+ return tag;
+ } else if(entry.type == contextmenu.MenuEntryType.SUB_MENU) {
+ let icon = entry.icon_class;
+ if(!icon || icon.length == 0) icon = "icon_empty";
+ else icon = "icon " + icon;
+
+ let tag = $.spawn("div").addClass("entry").addClass("sub-container");
+ tag.append($.spawn("div").addClass(icon));
+ tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
+
+ tag.append($.spawn("div").addClass("arrow right"));
+
+ if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
+ else {
+ let menu = $.spawn("div").addClass("sub-menu").addClass("context-menu-container");
+ for(const e of entry.sub_menu) {
+ if(typeof(entry.visible) === 'boolean' && !entry.visible)
+ continue;
+ menu.append(this.generate_tag(e));
+ }
+ menu.appendTo(tag);
+ }
+ return tag;
}
- return tag;
+ return $.spawn("div").text("undefined");
+ }
+
+ spawn_context_menu(x: number, y: number, ...entries: contextmenu.MenuEntry[]) {
+ let menu_tag = this._context_menu || (this._context_menu = $(".context-menu"));
+ menu_tag.finish().empty().css("opacity", "0");
+
+ const menu_container = $.spawn("div").addClass("context-menu-container");
+ this._close_callbacks = [];
+
+ for(const entry of entries){
+ if(typeof(entry.visible) === 'boolean' && !entry.visible)
+ continue;
+
+ if(entry.type == contextmenu.MenuEntryType.CLOSE) {
+ this._close_callbacks.push(entry.callback);
+ } else
+ menu_container.append(this.generate_tag(entry));
+ }
+
+ menu_tag.append(menu_container);
+ menu_tag.animate({opacity: 1}, 100).css("display", "block");
+
+ const width = menu_container.visible_width();
+ if(x + width + 5 > window.innerWidth)
+ menu_container.addClass("left");
+
+ // In the right position (the mouse)
+ menu_tag.css({
+ "top": y + "px",
+ "left": x + "px"
+ });
+ }
+
+ html_format_enabled(): boolean {
+ return true;
}
- return $.spawn("div").text("undefined");
}
-function spawn_context_menu(x, y, ...entries: ContextMenuEntry[]) {
- let menu_tag = context_menu || (context_menu = $(".context-menu"));
- menu_tag.finish().empty().css("opacity", "0");
-
- const menu_container = $.spawn("div").addClass("context-menu-container");
- contextMenuCloseFn = undefined;
-
- for(const entry of entries){
- if(typeof(entry.visible) === 'boolean' && !entry.visible)
- continue;
-
- if(entry.type == MenuEntryType.CLOSE) {
- contextMenuCloseFn = entry.callback;
- } else
- menu_container.append(generate_tag(entry));
- }
-
- menu_tag.append(menu_container);
- menu_tag.animate({opacity: 1}, 100).css("display", "block");
-
- const width = menu_container.visible_width();
- if(x + width + 5 > window.innerWidth)
- menu_container.addClass("left");
-
- // In the right position (the mouse)
- menu_tag.css({
- "top": y + "px",
- "left": x + "px"
- });
-}
\ No newline at end of file
+//TODO: Improve
+if(!window.require)
+ contextmenu.set_provider(new HTMLContextMenuProvider());
\ No newline at end of file
diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts
index d00c3aaa..46aa3186 100644
--- a/shared/js/ui/frames/ControlBar.ts
+++ b/shared/js/ui/frames/ControlBar.ts
@@ -541,17 +541,17 @@ class ControlBar {
return;
event.preventDefault();
- spawn_context_menu(event.pageX, event.pageY, {
- type: MenuEntryType.ENTRY,
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, {
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Connect"),
- icon: 'client-connect',
+ icon_class: 'client-connect',
callback: () => bookmark_connect(false)
}, {
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Connect in a new tab"),
- icon: 'client-connect',
+ icon_class: 'client-connect',
callback: () => bookmark_connect(true)
- }, MenuEntry.CLOSE(() => {
+ }, contextmenu.Entry.CLOSE(() => {
setTimeout(() => {
this.htmlTag.find(".btn_bookmark.button-dropdown").removeClass("force-show")
}, 250);
diff --git a/shared/js/ui/frames/SelectedItemInfo.ts b/shared/js/ui/frames/SelectedItemInfo.ts
index c2c08ed8..9121c657 100644
--- a/shared/js/ui/frames/SelectedItemInfo.ts
+++ b/shared/js/ui/frames/SelectedItemInfo.ts
@@ -455,7 +455,7 @@ class ChannelInfoManager extends InfoManager {
properties["channel_name"] = channel.generate_tag(false);
properties["channel_type"] = ChannelType.normalize(channel.channelType());
properties["channel_clients"] = channel.channelTree.clientsByChannel(channel).length;
- properties["channel_subscribed"] = true; //TODO
+ properties["channel_subscribed"] = channel.flag_subscribed;
properties["server_encryption"] = channel.channelTree.server.properties.virtualserver_codec_encryption_mode;
for(let key in channel.properties)
@@ -465,16 +465,15 @@ class ChannelInfoManager extends InfoManager {
properties["bbcode_channel_description"] = tag_channel_description;
channel.getChannelDescription().then(description => {
- let result = XBBCODE.process({
- text: description,
- escapeHtml: true,
- addInLineBreaks: true
- });
+ const result = xbbcode.parse(description, {});
+ /*
if(result.error) {
console.log("BBCode parse error: %o", result.errorQueue);
}
+ */
- tag_channel_description.html(result.html)
+ tag_channel_description.empty()
+ .append($.spawn("div").html(result.build_html()).contents())
.css("overflow-y", "auto")
.css("flex-grow", "1");
});
diff --git a/shared/js/ui/frames/chat.ts b/shared/js/ui/frames/chat.ts
index 9cc6d614..b76de264 100644
--- a/shared/js/ui/frames/chat.ts
+++ b/shared/js/ui/frames/chat.ts
@@ -245,10 +245,10 @@ class ChatEntry {
tag.on("contextmenu", (e) => {
e.preventDefault();
- let actions: ContextMenuEntry[] = [];
+ let actions: contextmenu.MenuEntry[] = [];
actions.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "",
name: tr("Clear"),
callback: () => {
this.history = [];
@@ -257,23 +257,23 @@ class ChatEntry {
});
if(this.flag_closeable) {
actions.push({
- type: MenuEntryType.ENTRY,
- icon: "client-tab_close_button",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-tab_close_button",
name: tr("Close"),
callback: () => this.handle.deleteChat(this)
});
}
actions.push({
- type: MenuEntryType.ENTRY,
- icon: "client-tab_close_button",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-tab_close_button",
name: tr("Close all private tabs"),
callback: () => {
//TODO Implement this?
},
visible: false
});
- spawn_context_menu(e.pageX, e.pageY, ...actions);
+ contextmenu.spawn_context_menu(e.pageX, e.pageY, ...actions);
});
tag_close.click(() => {
diff --git a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts
index bb3c95c8..c7409ae1 100644
--- a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts
+++ b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts
@@ -181,24 +181,20 @@ namespace unused {
if(event.isDefaultPrevented()) return;
event.preventDefault();
- spawn_context_menu(event.pageX, event.pageY, {
- type: MenuEntryType.ENTRY,
- icon: "",
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, {
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Expend group"),
callback: () => update_collapse_status(true, false)
}, {
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Expend all"),
callback: () => update_collapse_status(true, true)
}, {
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Collapse group"),
callback: () => update_collapse_status(false, false)
}, {
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Collapse all"),
callback: () => update_collapse_status(false, true)
});
diff --git a/shared/js/ui/modal/permission/ModalPermissionEdit.ts b/shared/js/ui/modal/permission/ModalPermissionEdit.ts
index 43b5307d..7ecb1324 100644
--- a/shared/js/ui/modal/permission/ModalPermissionEdit.ts
+++ b/shared/js/ui/modal/permission/ModalPermissionEdit.ts
@@ -130,14 +130,12 @@ namespace Modals {
if(event.isDefaultPrevented()) return;
event.preventDefault();
- spawn_context_menu(event.pageX, event.pageY, {
- type: MenuEntryType.ENTRY,
- icon: "",
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, {
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Expend all"),
callback: () => this.entry_editor.expend_all()
}, {
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Collapse all"),
callback: () => this.entry_editor.collapse_all()
});
@@ -221,18 +219,16 @@ namespace Modals {
};
entry.on_context_menu = (x, y) => {
- let entries: ContextMenuEntry[] = [];
+ let entries: contextmenu.MenuEntry[] = [];
if(typeof(entry.value) === "undefined") {
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Add permission"),
callback: () => entry.trigger_value_assign()
});
} else {
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Remove permission"),
callback: () => {
entry.value = undefined;
@@ -243,15 +239,13 @@ namespace Modals {
if(typeof(entry.granted) === "undefined") {
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Add grant permission"),
callback: () => entry.trigger_grant_assign()
});
} else {
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Remove grant permission"),
callback: () => {
entry.granted = undefined;
@@ -259,23 +253,20 @@ namespace Modals {
}
});
}
- entries.push(MenuEntry.HR());
+ entries.push(contextmenu.Entry.HR());
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Expend all"),
callback: () => this.entry_editor.expend_all()
});
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Collapse all"),
callback: () => this.entry_editor.collapse_all()
});
- entries.push(MenuEntry.HR());
+ entries.push(contextmenu.Entry.HR());
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show permission description"),
callback: () => {
createInfoModal(
@@ -285,15 +276,14 @@ namespace Modals {
}
});
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "",
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Copy permission name"),
callback: () => {
copy_to_clipboard(permission.name);
}
});
- spawn_context_menu(x, y, ...entries);
+ contextmenu.spawn_context_menu(x, y, ...entries);
}
}
}
@@ -1220,10 +1210,10 @@ namespace Modals {
return;
event.preventDefault();
- spawn_context_menu(event.pageX, event.pageY, {
- type: MenuEntryType.ENTRY,
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, {
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Remove client"),
- icon: 'client-delete',
+ icon_class: 'client-delete',
callback: () => {
connection.serverConnection.send_command("servergroupdelclient", {
sgid: current_group.id,
@@ -1233,9 +1223,9 @@ namespace Modals {
});
}
}, {
- type: MenuEntryType.ENTRY,
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Copy unique id"),
- icon: 'client-copy',
+ icon_class: 'client-copy',
callback: () => copy_to_clipboard(client.client_unique_identifier)
})
});
diff --git a/shared/js/ui/server.ts b/shared/js/ui/server.ts
index 398dc8dc..9ed774da 100644
--- a/shared/js/ui/server.ts
+++ b/shared/js/ui/server.ts
@@ -136,22 +136,22 @@ class ServerEntry {
spawnContextMenu(x: number, y: number, on_close: () => void = () => {}) {
let trigger_close = true;
- spawn_context_menu(x, y, {
- type: MenuEntryType.ENTRY,
+ contextmenu.spawn_context_menu(x, y, {
+ type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show server info"),
callback: () => {
trigger_close = false;
this.channelTree.client.select_info.open_popover()
},
- icon: "client-about",
+ icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover()
}, {
- type: MenuEntryType.HR,
+ type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(),
name: ''
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-virtualserver_edit",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-virtualserver_edit",
name: tr("Edit"),
callback: () => {
Modals.createServerModal(this, properties => {
@@ -164,22 +164,22 @@ class ServerEntry {
});
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-iconviewer",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-iconviewer",
name: tr("View icons"),
callback: () => Modals.spawnIconSelect(this.channelTree.client)
}, {
- type: MenuEntryType.ENTRY,
- icon: 'client-iconsview',
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: 'client-iconsview',
name: tr("View avatars"),
callback: () => Modals.spawnAvatarList(this.channelTree.client)
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-invite_buddy",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-invite_buddy",
name: tr("Invite buddy"),
callback: () => Modals.spawnInviteEditor(this.channelTree.client)
},
- MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})())
+ contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
);
}
diff --git a/shared/js/ui/view.ts b/shared/js/ui/view.ts
index a55f0832..c94ebee8 100644
--- a/shared/js/ui/view.ts
+++ b/shared/js/ui/view.ts
@@ -90,15 +90,15 @@ class ChannelTree {
this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) ||
this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1);
- spawn_context_menu(x, y,
+ contextmenu.spawn_context_menu(x, y,
{
- type: MenuEntryType.ENTRY,
- icon: "client-channel_create",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-channel_create",
name: tr("Create channel"),
invalidPermission: !channelCreate,
callback: () => this.spawnCreateChannel()
},
- MenuEntry.CLOSE(on_close)
+ contextmenu.Entry.CLOSE(on_close)
);
}
@@ -461,11 +461,11 @@ class ChannelTree {
const music_entry = clients.map(e => e instanceof MusicClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0;
const local_client = clients.map(e => e instanceof LocalClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0;
console.log(tr("Music only: %o | Container music: %o | Container local: %o"), music_entry, music_entry, local_client);
- let entries: ContextMenuEntry[] = [];
+ let entries: contextmenu.MenuEntry[] = [];
if (!music_entry && !local_client) { //Music bots or local client cant be poked
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "client-poke",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-poke",
name: tr("Poke clients"),
callback: () => {
createInputModal(tr("Poke clients"), tr("Poke message:
"), text => true, result => {
@@ -482,8 +482,8 @@ class ChannelTree {
});
}
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "client-move_client_to_own_channel",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-move_client_to_own_channel",
name: tr("Move clients to your channel"),
callback: () => {
const target = this.client.getClient().currentChannel().getChannelId();
@@ -495,10 +495,10 @@ class ChannelTree {
}
});
if (!local_client) {//local client cant be kicked and/or banned or kicked
- entries.push(MenuEntry.HR());
+ entries.push(contextmenu.Entry.HR());
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "client-kick_channel",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-kick_channel",
name: tr("Kick clients from channel"),
callback: () => {
createInputModal(tr("Kick clients from channel"), tr("Kick reason:
"), text => true, result => {
@@ -517,8 +517,8 @@ class ChannelTree {
if (!music_entry) { //Music bots cant be banned or kicked
entries.push({
- type: MenuEntryType.ENTRY,
- icon: "client-kick_server",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-kick_server",
name: tr("Kick clients fom server"),
callback: () => {
createInputModal(tr("Kick clients from server"), tr("Kick reason:
"), text => true, result => {
@@ -534,8 +534,8 @@ class ChannelTree {
}, {width: 400, maxLength: 255}).open();
}
}, {
- type: MenuEntryType.ENTRY,
- icon: "client-ban_client",
+ type: contextmenu.MenuEntryType.ENTRY,
+ icon_class: "client-ban_client",
name: tr("Ban clients"),
invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
callback: () => {
@@ -555,10 +555,10 @@ class ChannelTree {
});
}
if(music_only) {
- entries.push(MenuEntry.HR());
+ entries.push(contextmenu.Entry.HR());
entries.push({
name: tr("Delete bots"),
- icon: "client-delete",
+ icon_class: "client-delete",
disabled: false,
callback: () => {
const param_string = clients.map((_, index) => "{" + index + "}").join(', ');
@@ -574,11 +574,11 @@ class ChannelTree {
}
});
},
- type: MenuEntryType.ENTRY
+ type: contextmenu.MenuEntryType.ENTRY
});
}
}
- spawn_context_menu(event.pageX, event.pageY, ...entries);
+ contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries);
}
clientsByGroup(group: Group) : ClientEntry[] {
diff --git a/vendor/xbbcode b/vendor/xbbcode
index d9a47d05..8092afb5 160000
--- a/vendor/xbbcode
+++ b/vendor/xbbcode
@@ -1 +1 @@
-Subproject commit d9a47d059ae9cce559d7a75553a25ba342d36229
+Subproject commit 8092afb59615aa19cff372689cd6985c96e3f2ba