2020-03-12 16:23:56 +01:00
//META{"name":"BetterTypingUsers","source":"https://github.com/1Lighty/BetterDiscordPlugins/blob/master/Plugins/BetterTypingUsers/BetterTypingUsers.plugin.js","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterTypingUsers","authorId":"239513071272329217","invite":"NYvWdN5","donate":"https://paypal.me/lighty13"}*//
2020-02-19 21:47:08 +01:00
/ * @ c c _ o n
@ if ( @ _jscript )
// Offer to self-install for clueless users that try to run this directly.
var shell = WScript . CreateObject ( 'WScript.Shell' ) ;
var fs = new ActiveXObject ( 'Scripting.FileSystemObject' ) ;
var pathPlugins = shell . ExpandEnvironmentStrings ( '%APPDATA%\\BetterDiscord\\plugins' ) ;
var pathSelf = WScript . ScriptFullName ;
// Put the user at ease by addressing them in the first person
shell . Popup ( 'It looks like you\'ve mistakenly tried to run me directly. \n(Don\'t do that!)' , 0 , 'I\'m a plugin for BetterDiscord' , 0x30 ) ;
if ( fs . GetParentFolderName ( pathSelf ) === fs . GetAbsolutePathName ( pathPlugins ) ) {
shell . Popup ( 'I\'m in the correct folder already.\nJust reload Discord with Ctrl+R.' , 0 , 'I\'m already installed' , 0x40 ) ;
} else if ( ! fs . FolderExists ( pathPlugins ) ) {
shell . Popup ( 'I can\'t find the BetterDiscord plugins folder.\nAre you sure it\'s even installed?' , 0 , 'Can\'t install myself' , 0x10 ) ;
} else if ( shell . Popup ( 'Should I copy myself to BetterDiscord\'s plugins folder for you?' , 0 , 'Do you need some help?' , 0x34 ) === 6 ) {
fs . CopyFile ( pathSelf , fs . BuildPath ( pathPlugins , fs . GetFileName ( pathSelf ) ) , true ) ;
// Show the user where to put plugins in the future
shell . Exec ( 'explorer ' + pathPlugins ) ;
shell . Popup ( 'I\'m installed!\nJust reload Discord with Ctrl+R.' , 0 , 'Successfully installed' , 0x40 ) ;
}
WScript . Quit ( ) ;
@ else @ * /
/ *
* Copyright © 2019 - 2020 , _Lighty _
* All rights reserved .
* Code may not be redistributed , modified or otherwise taken without explicit permission .
* /
var BetterTypingUsers = ( ( ) => {
/* Setup */
const config = {
main : 'index.js' ,
info : {
name : 'BetterTypingUsers' ,
authors : [
{
name : 'Lighty' ,
discord _id : '239513071272329217' ,
github _username : 'LightyPon' ,
twitter _username : ''
}
] ,
version : '1.0.0' ,
description : 'Replaces "Several people are typing" with who is actually typing, plus "x others" if it can\'t fit. Number of shown people typing can be changed.' ,
github : 'https://github.com/1Lighty' ,
github _raw : 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/BetterTypingUsers/BetterTypingUsers.plugin.js'
} ,
changelog : [
{
title : 'Initial release!' ,
type : 'added' ,
items : [ 'Max users typing changed to 5, with showing and "x others" if more people than that are typing.' ]
} ,
{
title : 'future plans' ,
type : 'progress' ,
items : [ 'Auto fit max number of people typing, until there is no space left.' ]
}
] ,
defaultConfig : [
{
name : 'Max visible typing users' ,
id : 'maxVisible' ,
type : 'slider' ,
value : 5 ,
min : 3 ,
max : 20 ,
markers : Array . from ( Array ( 18 ) , ( _ , i ) => i + 3 ) ,
stickToMarkers : true
} ,
{
name : 'Previews' ,
type : 'preview'
}
] ,
strings : {
en : { AND _1 _OTHER : ' and 1 other' , AND _X _OTHERS : ' and ${count} others' , AND : ' and ' , ARE _TYPING : ' are typing...' } ,
de : { AND _1 _OTHER : ' und 1 andere' , AND _X _OTHERS : ' und ${count} andere' , AND : ' und ' , ARE _TYPING : ' schreiben...' } ,
da : { AND _1 _OTHER : ' og 1 anden' , AND _X _OTHERS : ' og ${count} andre' , AND : ' og ' , ARE _TYPING : ' skriver...' } ,
es : { AND _1 _OTHER : ' y 1 otro' , AND _X _OTHERS : ' y otros ${count}' , AND : ' y ' , ARE _TYPING : ' están escribiendo...' } ,
fr : { AND _1 _OTHER : ' et 1 autre' , AND _X _OTHERS : ' et ${count} autres' , AND : ' et ' , ARE _TYPING : ' écrivent...' } ,
hr : { AND _1 _OTHER : ' i 1 drugi' , AND _X _OTHERS : ' i ${count} drugih' , AND : ' i ' , ARE _TYPING : ' pišu...' } ,
it : { AND _1 _OTHER : ' e 1 altro' , AND _X _OTHERS : ' e altri ${count}' , AND : ' e ' , ARE _TYPING : ' stanno scrivendo...' } ,
tr : { AND _1 _OTHER : ' ve 1 kişi daha' , AND _X _OTHERS : ' ve ${count} kişi daha' , AND : ' ve ' , ARE _TYPING : ' yazı yor...' }
}
} ;
/* Build */
const buildPlugin = ( [ Plugin , Api ] ) => {
const { ContextMenu , EmulatedTooltip , Toasts , Settings , Popouts , Modals , Utilities , WebpackModules , Filters , DiscordModules , ColorConverter , DOMTools , DiscordClasses , DiscordSelectors , ReactTools , ReactComponents , DiscordAPI , Logger , Patcher , PluginUpdater , PluginUtilities , DiscordClassModules , Structs } = Api ;
const { React , ModalStack , ContextMenuActions , ContextMenuItem , ContextMenuItemsGroup , ReactDOM , ChannelStore , GuildStore , UserStore , DiscordConstants , Dispatcher , GuildMemberStore , GuildActions , SwitchRow , EmojiUtils , RadioGroup , Permissions , TextElement , FlexChild , PopoutOpener , Textbox , RelationshipStore , UserSettingsStore } = DiscordModules ;
const NameUtils = WebpackModules . getByProps ( 'getName' ) ;
const CUser = WebpackModules . getByPrototypes ( 'getAvatarSource' , 'isLocalBot' ) ;
const CChannel = WebpackModules . getByPrototypes ( 'isGroupDM' , 'isMultiUserDM' ) ;
const L337 = ( ( ) => {
try {
return new CChannel ( { id : '1337' } ) ;
} catch ( e ) {
Logger . stacktrace ( 'Failed to create 1337 channel' , e ) ;
}
} ) ( ) ;
let CTypingUsers = ( ( ) => {
try {
const WrappedTypingUsers = WebpackModules . find ( m => m . displayName && m . displayName . indexOf ( 'TypingUsers' ) !== - 1 ) ;
return new WrappedTypingUsers ( { channel : L337 } ) . render ( ) . type ;
} catch ( e ) {
Logger . stacktrace ( 'Failed to get TypingUsers!' , e ) ;
return null ;
}
} ) ( ) ;
const ComponentDispatch = ( ( ) => {
try {
return WebpackModules . getByProps ( 'ComponentDispatch' ) . ComponentDispatch ;
} catch ( e ) {
Logger . stacktrace ( 'Failed to get ComponentDispatch' , e ) ;
}
} ) ( ) ;
class CTypingUsersPreview extends React . PureComponent {
constructor ( props ) {
super ( props ) ;
this . forceUpdate = this . forceUpdate . bind ( this ) ;
const iUsers = UserStore . getUsers ( ) ;
for ( let i = 0 ; i < 20 ; i ++ ) iUsers [ ( 1337 + i ) . toString ( ) ] = new CUser ( { username : ` User ${ i + 1 } ` , id : ( 1337 + i ) . toString ( ) , discriminator : '9999' } ) ;
}
componentDidMount ( ) {
ComponentDispatch . subscribe ( 'BTU_SETTINGS_UPDATED' , this . forceUpdate ) ;
}
componentWillUnmount ( ) {
ComponentDispatch . unsubscribe ( 'BTU_SETTINGS_UPDATED' , this . forceUpdate ) ;
const iUsers = UserStore . getUsers ( ) ;
for ( let i = 0 ; i < 20 ; i ++ ) delete iUsers [ ( 1337 + i ) . toString ( ) ] ;
}
renderTyping ( num ) {
const typingUsers = { } ;
for ( let i = 0 ; i < num ; i ++ ) typingUsers [ ( 1337 + i ) . toString ( ) ] = 1 ;
return React . createElement ( CTypingUsers , {
channel : L337 ,
guildId : '' ,
isFocused : true ,
slowmodeCooldownGuess : 0 ,
theme : UserSettingsStore . theme ,
typingUsers
} ) ;
}
render ( ) {
return React . createElement (
'div' ,
{
className : 'BTU-preview'
} ,
this . renderTyping ( 4 ) ,
this . renderTyping ( 6 ) ,
this . renderTyping ( 20 )
) ;
}
}
class TypingUsersPreview extends Settings . SettingField {
constructor ( name , note ) {
super ( name , note , null , CTypingUsersPreview ) ;
}
}
/ * s i n c e X e n o L i b i s a b s e n t f r o m t h i s p l u g i n ( s i n c e i t s e r v e s n o r e a l p u r p o s e ) ,
we can only hope the user doesn ' t rename the plugin . .
* /
return class BetterTypingUsers extends Plugin {
2020-03-12 16:23:56 +01:00
constructor ( ) {
2020-04-12 17:19:05 +02:00
super ( ) ;
2020-03-12 16:23:56 +01:00
try {
ModalStack . popWithKey ( ` ${ this . name } _DEP_MODAL ` ) ;
} catch ( e ) { }
}
2020-02-19 21:47:08 +01:00
onStart ( ) {
this . promises = { state : { cancelled : false } } ;
this . patchAll ( ) ;
PluginUtilities . addStyle (
this . short + '-CSS' ,
`
. BTU - preview > . $ { WebpackModules . getByProps ( 'slowModeIcon' , 'typing' ) . typing . split ( ' ' ) [ 0 ] } {
position : unset ! important ;
}
`
) ;
2020-03-12 16:23:56 +01:00
DiscordConstants . MAX _TYPING _USERS = 99 ;
/* theoretical max is 5 users typing at once.. welp */
2020-02-19 21:47:08 +01:00
}
onStop ( ) {
this . promises . state . cancelled = true ;
Patcher . unpatchAll ( ) ;
PluginUtilities . removeStyle ( this . short + '-CSS' ) ;
}
/* zlib uses reference to defaultSettings instead of a cloned object, which sets settings as default settings, messing everything up */
loadSettings ( defaultSettings ) {
return PluginUtilities . loadSettings ( this . name , Utilities . deepclone ( this . defaultSettings ? this . defaultSettings : defaultSettings ) ) ;
}
buildSetting ( data ) {
if ( data . type === 'preview' ) return new TypingUsersPreview ( data . name , data . note ) ;
return super . buildSetting ( data ) ;
}
saveSettings ( _ , setting , value ) {
super . saveSettings ( _ , setting , value ) ;
ComponentDispatch . safeDispatch ( 'BTU_SETTINGS_UPDATED' ) ;
}
filterTypingUsers ( typingUsers ) {
return Object . keys ( typingUsers )
. filter ( e => e != DiscordAPI . currentUser . id )
. filter ( e => ! RelationshipStore . isBlocked ( e ) )
. map ( e => UserStore . getUser ( e ) )
. filter ( e => e != null ) ;
}
/* PATCHES */
patchAll ( ) {
Utilities . suppressErrors ( this . patchBetterRoleColors . bind ( this ) , 'BetterRoleColors patch' ) ( ) ;
Utilities . suppressErrors ( this . patchTypingUsers . bind ( this ) , 'TypingUsers patch' ) ( this . promises . state ) ;
}
patchBetterRoleColors ( ) {
const BetterRoleColors = BdApi . getPlugin ( 'BetterRoleColors' ) ;
if ( ! BetterRoleColors ) return ;
/* stop errors */
/ * m o d i f y B R C s b e h a v i o r s o i t w o n ' t u n e x p e c t e d l y t r y t o m o d i f y a n e n t r y t h a t d o e s n o t e x i s t
by simply limiting it to the max number of usernames visible in total
* /
2020-03-12 16:23:56 +01:00
Patcher . after ( BetterRoleColors , 'filterTypingUsers' , ( _this , _ _ , ret ) => ret . slice ( 0 , this . settings . maxVisible ) ) ;
2020-02-19 21:47:08 +01:00
}
async patchTypingUsers ( promiseState ) {
const TypingUsers = await ReactComponents . getComponentByName ( 'TypingUsers' , DiscordSelectors . Typing . typing ) ;
2020-03-12 16:23:56 +01:00
if ( ! TypingUsers . selector ) TypingUsers . selector = DiscordSelectors . Typing . typing ;
2020-02-19 21:47:08 +01:00
const TypingTextClassname = WebpackModules . getByProps ( 'typing' , 'text' ) . text . split ( ' ' ) [ 0 ] ;
if ( promiseState . cancelled ) return ;
if ( ! CTypingUsers ) CTypingUsers = typingUsers . component ; /* failsafe */
/* use `instead` so that we modify the return before BetterRoleColors */
2020-03-12 16:23:56 +01:00
/ * P a t c h e r . a f t e r ( T y p i n g U s e r s . c o m p o n e n t . p r o t o t y p e , ' c o m p o n e n t D i d U p d a t e ' , ( _ t h i s , [ p r o p s , s t a t e ] , r e t ) = > {
const filtered1 = this . filterTypingUsers ( _this . props . typingUsers ) ;
const filtered2 = this . filterTypingUsers ( props . typingUsers ) ;
if ( filtered1 . length !== filtered2 . length || _this . state . numLess === state . numLess ) {
_this . state . numLess = 0 ;
_this . triedLess = false ;
_this . triedMore = false ;
}
} ) ; * /
2020-02-19 21:47:08 +01:00
Patcher . instead ( TypingUsers . component . prototype , 'render' , ( _this , _ , orig ) => {
2020-03-12 16:23:56 +01:00
/* if (!_this.state) _this.state = { numLess: 0 }; */
2020-02-19 21:47:08 +01:00
const ret = orig ( ) ;
2020-03-12 16:23:56 +01:00
if ( ! ret ) {
/* _this.state.numLess = 0; */
return ret ;
}
2020-02-19 21:47:08 +01:00
const filtered = this . filterTypingUsers ( _this . props . typingUsers ) ;
if ( filtered . length <= 3 ) return ret ;
2020-03-12 16:23:56 +01:00
/ * r e t . r e f = e = > {
_this . _ _baseRef = e ;
if ( ! e ) return ;
if ( ! _this . _ _textRef ) return ;
_this . maxWidth = parseInt ( getComputedStyle ( _this . _ _baseRef . parentElement ) . width ) - ( _this . _ _textRef . offsetLeft + parseInt ( getComputedStyle ( _this . _ _textRef ) [ 'margin-left' ] ) - _this . _ _baseRef . offsetLeft ) ;
if ( _this . _ _textRef . scrollWidth > _this . maxWidth ) {
if ( _this . triedMore ) return ;
if ( filtered . length - _this . state . numLess <= 3 ) return ;
_this . setState ( { numLess : _this . state . numLess + 1 } ) ;
}
} ; * /
2020-02-19 21:47:08 +01:00
const typingUsers = Utilities . findInReactTree ( ret , e => e && e . props && typeof e . props . className === 'string' && e . props . className . indexOf ( TypingTextClassname ) !== - 1 ) ;
if ( ! typingUsers ) return ret ;
2020-03-12 16:23:56 +01:00
/ * i f ( t y p e o f _ t h i s . s t a t e . n u m L e s s ! = = ' n u m b e r ' ) _ t h i s . s t a t e . n u m L e s s = 0 ;
typingUsers . ref = e => {
_this . _ _textRef = e ;
} ; * /
2020-02-19 21:47:08 +01:00
typingUsers . props . children = [ ] ;
/* I don't think this method works for every language..? */
for ( let i = 0 ; i < filtered . length ; i ++ ) {
2020-03-12 16:23:56 +01:00
if ( this . settings . maxVisible /* filtered.length - _this.state.numLess */ === i ) {
2020-02-19 21:47:08 +01:00
const others = filtered . length - i ;
if ( others === 1 ) typingUsers . props . children . push ( this . strings . AND _1 _OTHER ) ;
else typingUsers . props . children . push ( Utilities . formatTString ( this . strings . AND _X _OTHERS , { count : others } ) ) ;
break ;
} else if ( i === filtered . length - 1 ) typingUsers . props . children . push ( this . strings . AND ) ;
else if ( i !== 0 ) typingUsers . props . children . push ( ', ' ) ;
const name = NameUtils . getName ( _this . props . guildId , _this . props . channel . id , filtered [ i ] ) ;
typingUsers . props . children . push ( React . createElement ( 'strong' , { } , name ) ) ;
}
typingUsers . props . children . push ( this . strings . ARE _TYPING ) ;
return ret ;
} ) ;
TypingUsers . forceUpdateAll ( ) ;
}
/* PATCHES */
getSettingsPanel ( ) {
return this . buildSettingsPanel ( ) . getElement ( ) ;
}
get [ Symbol . toStringTag ] ( ) {
return 'Plugin' ;
}
get name ( ) {
return config . info . name ;
}
get short ( ) {
let string = '' ;
for ( let i = 0 , len = config . info . name . length ; i < len ; i ++ ) {
const char = config . info . name [ i ] ;
if ( char === char . toUpperCase ( ) ) string += char ;
}
return string ;
}
get author ( ) {
return config . info . authors . map ( author => author . name ) . join ( ', ' ) ;
}
get version ( ) {
return config . info . version ;
}
get description ( ) {
return config . info . description ;
}
} ;
} ;
/* Finalize */
let ZeresPluginLibraryOutdated = false ;
try {
2020-03-12 16:23:56 +01:00
if ( global . BdApi && 'function' == typeof BdApi . getPlugin ) {
const a = ( c , a ) => ( ( c = c . split ( '.' ) . map ( b => parseInt ( b ) ) ) , ( a = a . split ( '.' ) . map ( b => parseInt ( b ) ) ) , ! ! ( a [ 0 ] > c [ 0 ] ) ) || ! ! ( a [ 0 ] == c [ 0 ] && a [ 1 ] > c [ 1 ] ) || ! ! ( a [ 0 ] == c [ 0 ] && a [ 1 ] == c [ 1 ] && a [ 2 ] > c [ 2 ] ) ,
b = BdApi . getPlugin ( 'ZeresPluginLibrary' ) ;
2020-04-12 17:19:05 +02:00
( ( b , c ) => b && b . _config && b . _config . info && b . _config . info . version && a ( b . _config . info . version , c ) ) ( b , '1.2.14' ) && ( ZeresPluginLibraryOutdated = ! 0 ) ;
2020-02-19 21:47:08 +01:00
}
} catch ( e ) {
console . error ( 'Error checking if ZeresPluginLibrary is out of date' , e ) ;
}
return ! global . ZeresPluginLibrary || ZeresPluginLibraryOutdated
? class {
2020-03-12 16:23:56 +01:00
constructor ( ) {
this . _config = config ;
2020-04-12 17:19:05 +02:00
this . start = this . load = this . handleMissingLib ;
2020-03-12 16:23:56 +01:00
}
2020-02-19 21:47:08 +01:00
getName ( ) {
return this . name . replace ( /\s+/g , '' ) ;
}
getAuthor ( ) {
return this . author ;
}
getVersion ( ) {
return this . version ;
}
getDescription ( ) {
2020-04-12 17:19:05 +02:00
return this . description + ' You are missing ZeresPluginLibrary for this plugin, please enable the plugin and click Download Now.' ;
2020-02-19 21:47:08 +01:00
}
stop ( ) { }
2020-04-12 17:19:05 +02:00
handleMissingLib ( ) {
const a = BdApi . findModuleByProps ( 'isModalOpenWithKey' ) ;
if ( a && a . isModalOpenWithKey ( ` ${ this . name } _DEP_MODAL ` ) ) return ;
2020-03-12 16:23:56 +01:00
const b = ! global . ZeresPluginLibrary ,
c = ZeresPluginLibraryOutdated ? 'Outdated Library' : 'Missing Library' ,
d = ` The Library ZeresPluginLibrary required for ${ this . name } is ${ ZeresPluginLibraryOutdated ? 'outdated' : 'missing' } . ` ,
e = BdApi . findModuleByProps ( 'push' , 'update' , 'pop' , 'popWithKey' ) ,
2020-04-12 17:19:05 +02:00
f = BdApi . findModuleByDisplayName ( 'Text' ) ,
2020-03-12 16:23:56 +01:00
g = BdApi . findModule ( a => a . defaultProps && a . key && 'confirm-modal' === a . key ( ) ) ,
2020-04-12 17:19:05 +02:00
h = ( ) => BdApi . alert ( c , BdApi . React . createElement ( 'span' , { } , BdApi . React . createElement ( 'div' , { } , d ) , ` Due to a slight mishap however, you'll have to download the libraries yourself. ` , b || ZeresPluginLibraryOutdated ? BdApi . React . createElement ( 'div' , { } , BdApi . React . createElement ( 'a' , { href : 'https://betterdiscord.net/ghdl?id=2252' , target : '_blank' } , 'Click here to download ZeresPluginLibrary' ) ) : null ) ) ;
2020-03-12 16:23:56 +01:00
if ( ! e || ! g || ! f ) return h ( ) ;
class i extends BdApi . React . PureComponent {
constructor ( a ) {
super ( a ) , ( this . state = { hasError : ! 1 } ) ;
2020-02-19 21:47:08 +01:00
}
2020-03-12 16:23:56 +01:00
componentDidCatch ( a ) {
console . error ( ` Error in ${ this . props . label } , screenshot or copy paste the error above to Lighty for help. ` ) , this . setState ( { hasError : ! 0 } ) , 'function' == typeof this . props . onError && this . props . onError ( a ) ;
2020-02-19 21:47:08 +01:00
}
render ( ) {
2020-03-12 16:23:56 +01:00
return this . state . hasError ? null : this . props . children ;
}
}
class j extends g {
submitModal ( ) {
this . props . onConfirm ( ) ;
2020-02-19 21:47:08 +01:00
}
}
2020-03-12 16:23:56 +01:00
let k = ! 1 ;
const l = e . push (
a =>
2020-02-19 21:47:08 +01:00
BdApi . React . createElement (
2020-03-12 16:23:56 +01:00
i ,
{
label : 'missing dependency modal' ,
onError : ( ) => {
e . popWithKey ( l ) , h ( ) ;
}
} ,
BdApi . React . createElement (
j ,
Object . assign (
{
header : c ,
2020-04-12 17:19:05 +02:00
children : [ BdApi . React . createElement ( f , { size : f . Sizes . SIZE _16 , children : [ ` ${ d } Please click Download Now to download it. ` ] } ) ] ,
2020-03-12 16:23:56 +01:00
red : ! 1 ,
confirmText : 'Download Now' ,
cancelText : 'Cancel' ,
onConfirm : ( ) => {
if ( k ) return ;
k = ! 0 ;
const a = require ( 'request' ) ,
b = require ( 'fs' ) ,
c = require ( 'path' ) ;
2020-04-12 17:19:05 +02:00
a ( 'https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js' , ( a , d , f ) => ( a || 200 !== d . statusCode ? ( e . popWithKey ( l ) , h ( ) ) : void b . writeFile ( c . join ( BdApi . Plugins . folder , '0PluginLibrary.plugin.js' ) , f , ( ) => { } ) ) ) ;
2020-03-12 16:23:56 +01:00
}
} ,
a
)
2020-02-19 21:47:08 +01:00
)
2020-03-12 16:23:56 +01:00
) ,
void 0 ,
` ${ this . name } _DEP_MODAL `
) ;
2020-02-19 21:47:08 +01:00
}
get [ Symbol . toStringTag ] ( ) {
return 'Plugin' ;
}
get name ( ) {
return config . info . name ;
}
get short ( ) {
let string = '' ;
for ( let i = 0 , len = config . info . name . length ; i < len ; i ++ ) {
const char = config . info . name [ i ] ;
if ( char === char . toUpperCase ( ) ) string += char ;
}
return string ;
}
get author ( ) {
return config . info . authors . map ( author => author . name ) . join ( ', ' ) ;
}
get version ( ) {
return config . info . version ;
}
get description ( ) {
return config . info . description ;
}
}
: buildPlugin ( global . ZeresPluginLibrary . buildPlugin ( config ) ) ;
} ) ( ) ;
/*@end@*/