2020-04-01 13:36:37 +00:00
import * as loader from "../loader/loader" ;
2020-06-10 20:44:24 +00:00
import { config } from "../loader/loader" ;
2019-08-30 21:06:39 +00:00
2020-03-30 11:44:18 +00:00
declare global {
interface Window {
native_client : boolean ;
2019-08-30 21:06:39 +00:00
}
2020-03-30 11:44:18 +00:00
}
2019-08-30 21:06:39 +00:00
2020-03-30 11:44:18 +00:00
const node_require : typeof require = window . require ;
2019-08-30 21:06:39 +00:00
2020-03-30 23:27:59 +00:00
function cache_tag() {
const ui = ui_version ( ) ;
return "?_ts=" + ( ! ! ui && ui !== "unknown" ? ui : Date.now ( ) ) ;
}
2020-03-30 11:44:18 +00:00
let _ui_version ;
export function ui_version() {
if ( typeof ( _ui_version ) !== "string" ) {
const version_node = document . getElementById ( "app_version" ) ;
if ( ! version_node ) return undefined ;
2019-08-30 21:06:39 +00:00
2020-03-30 11:44:18 +00:00
const version = version_node . hasAttribute ( "value" ) ? version_node . getAttribute ( "value" ) : undefined ;
if ( ! version ) return undefined ;
2019-08-30 21:06:39 +00:00
2020-03-30 11:44:18 +00:00
return ( _ui_version = version ) ;
2019-08-30 21:06:39 +00:00
}
2020-03-30 11:44:18 +00:00
return _ui_version ;
2019-08-30 21:06:39 +00:00
}
2020-03-30 23:27:59 +00:00
interface Manifest {
version : number ;
chunks : { [ key : string ] : {
2020-04-01 19:47:33 +00:00
files : {
hash : string ,
file : string
} [ ] ,
module s : {
id : string ,
context : string ,
resource : string
} [ ]
} } ;
2020-04-01 13:36:37 +00:00
}
2019-08-30 21:06:39 +00:00
/* all javascript loaders */
const loader_javascript = {
load_scripts : async ( ) = > {
if ( ! window . require ) {
2020-03-30 23:27:59 +00:00
await loader . scripts . load ( [ "vendor/jquery/jquery.min.js" ] , { cache_tag : cache_tag ( ) } ) ;
2019-08-30 21:06:39 +00:00
} else {
/ *
loader . register_task ( loader . Stage . JAVASCRIPT_INITIALIZING , {
name : "forum sync" ,
priority : 10 ,
function : async ( ) = > {
forum . sync_main ( ) ;
}
} ) ;
* /
}
2020-03-30 23:27:59 +00:00
await loader . scripts . load_multiple ( [
2020-03-31 14:43:21 +00:00
[ "vendor/jsrender/jsrender.min.js" ] ,
2019-08-30 21:06:39 +00:00
[ "vendor/xbbcode/src/parser.js" ] ,
2020-03-31 14:43:21 +00:00
[ "vendor/emoji-picker/src/jquery.lsxemojipicker.js" ] ,
2019-08-30 21:06:39 +00:00
[ "vendor/twemoji/twemoji.min.js" , "" ] , /* empty string means not required */
[ "vendor/highlight/highlight.pack.js" , "" ] , /* empty string means not required */
[ "vendor/remarkable/remarkable.min.js" , "" ] , /* empty string means not required */
2020-03-30 23:27:59 +00:00
] , {
cache_tag : cache_tag ( ) ,
max_parallel_requests : - 1
} ) ;
let manifest : Manifest ;
try {
2020-06-10 20:44:24 +00:00
const response = await fetch ( config . baseUrl + "js/manifest.json" ) ;
2020-03-30 23:27:59 +00:00
if ( ! response . ok ) throw response . status + " " + response . statusText ;
manifest = await response . json ( ) ;
} catch ( error ) {
console . error ( "Failed to load javascript manifest: %o" , error ) ;
loader . critical_error ( "Failed to load manifest.json" , error ) ;
throw "failed to load manifest.json" ;
2019-08-30 21:06:39 +00:00
}
2020-04-01 19:47:33 +00:00
if ( manifest . version !== 2 )
2020-03-30 23:27:59 +00:00
throw "invalid manifest version" ;
2019-08-30 21:06:39 +00:00
2020-04-01 19:47:33 +00:00
const chunk_name = __build . entry_chunk_name ;
if ( typeof manifest . chunks [ chunk_name ] !== "object" ) {
2020-04-01 13:36:37 +00:00
loader . critical_error ( "Missing entry chunk in manifest.json" , "Chunk " + chunk_name + " is missing." ) ;
throw "missing entry chunk" ;
}
2020-04-01 19:47:33 +00:00
loader . module _mapping ( ) . push ( {
application : chunk_name ,
module s : manifest . chunks [ chunk_name ] . module s
} ) ;
await loader . scripts . load_multiple ( manifest . chunks [ chunk_name ] . files . map ( e = > "js/" + e . file ) , {
2020-03-30 23:27:59 +00:00
cache_tag : undefined ,
max_parallel_requests : - 1
} ) ;
2019-08-30 21:06:39 +00:00
}
} ;
const loader_webassembly = {
test_webassembly : async ( ) = > {
/ * W e d o n t r e q u i r e d W e b A s s e m b l y a n y m o r e f o r f u n d a m e n t a l f u n c t i o n s , o n l y f o r a u t o d e c o d i n g
if ( typeof ( WebAssembly ) === "undefined" || typeof ( WebAssembly . compile ) === "undefined" ) {
console . log ( navigator . browserSpecs ) ;
if ( navigator . browserSpecs . name == 'Safari' ) {
if ( parseInt ( navigator . browserSpecs . version ) < 11 ) {
displayCriticalError ( "You require Safari 11 or higher to use the web client!<br>Safari " + navigator . browserSpecs . version + " does not support WebAssambly!" ) ;
return ;
}
}
else {
// Do something for all other browsers.
}
displayCriticalError ( "You require WebAssembly for TeaSpeak-Web!" ) ;
throw "Missing web assembly" ;
}
* /
}
} ;
const loader_style = {
load_style : async ( ) = > {
2020-03-30 23:27:59 +00:00
const options = {
cache_tag : cache_tag ( ) ,
max_parallel_requests : - 1
} ;
await loader . style . load_multiple ( [
2019-08-30 21:06:39 +00:00
"vendor/xbbcode/src/xbbcode.css"
2020-03-30 23:27:59 +00:00
] , options ) ;
await loader . style . load_multiple ( [
2019-08-30 21:06:39 +00:00
"vendor/emoji-picker/src/jquery.lsxemojipicker.css"
2020-03-30 23:27:59 +00:00
] , options ) ;
await loader . style . load_multiple ( [
2019-08-30 21:06:39 +00:00
[ "vendor/highlight/styles/darcula.css" , "" ] , /* empty string means not required */
2020-03-30 23:27:59 +00:00
] , options ) ;
2019-08-30 21:06:39 +00:00
2020-04-01 19:47:33 +00:00
if ( __build . mode === "debug" ) {
2019-08-30 21:06:39 +00:00
await loader_style . load_style_debug ( ) ;
} else {
await loader_style . load_style_release ( ) ;
}
} ,
load_style_debug : async ( ) = > {
2020-03-30 23:27:59 +00:00
await loader . style . load_multiple ( [
2019-08-30 21:06:39 +00:00
"css/static/main.css" ,
"css/static/main-layout.css" ,
"css/static/scroll.css" ,
"css/static/channel-tree.css" ,
"css/static/ts/tab.css" ,
"css/static/ts/icons.css" ,
"css/static/ts/icons_em.css" ,
"css/static/ts/country.css" ,
"css/static/general.css" ,
"css/static/modal.css" ,
"css/static/modals.css" ,
"css/static/modal-about.css" ,
"css/static/modal-avatar.css" ,
"css/static/modal-icons.css" ,
"css/static/modal-bookmarks.css" ,
"css/static/modal-connect.css" ,
"css/static/modal-channel.css" ,
"css/static/modal-query.css" ,
2019-10-13 19:33:07 +00:00
"css/static/modal-volume.css" ,
2019-11-09 14:56:01 +00:00
"css/static/modal-latency.css" ,
2019-08-30 21:06:39 +00:00
"css/static/modal-invite.css" ,
"css/static/modal-banlist.css" ,
2019-09-18 23:25:57 +00:00
"css/static/modal-banclient.css" ,
"css/static/modal-channelinfo.css" ,
2019-08-30 21:06:39 +00:00
"css/static/modal-clientinfo.css" ,
"css/static/modal-serverinfo.css" ,
2020-02-22 13:30:17 +00:00
"css/static/modal-musicmanage.css" ,
2019-09-12 21:59:35 +00:00
"css/static/modal-serverinfobandwidth.css" ,
2019-08-30 21:06:39 +00:00
"css/static/modal-identity.css" ,
2020-03-27 15:15:15 +00:00
"css/static/modal-newcomer.css" ,
2019-08-30 21:06:39 +00:00
"css/static/modal-settings.css" ,
"css/static/modal-poke.css" ,
"css/static/modal-server.css" ,
"css/static/modal-keyselect.css" ,
"css/static/modal-group-assignment.css" ,
2020-03-19 13:31:45 +00:00
"css/static/overlay-image-preview.css" ,
2019-08-30 21:06:39 +00:00
"css/static/context_menu.css" ,
"css/static/frame-chat.css" ,
"css/static/connection_handlers.css" ,
"css/static/server-log.css" ,
"css/static/htmltags.css" ,
"css/static/hostbanner.css" ,
"css/static/menu-bar.css"
2020-03-30 23:27:59 +00:00
] , {
cache_tag : cache_tag ( ) ,
max_parallel_requests : - 1
} ) ;
2019-08-30 21:06:39 +00:00
} ,
load_style_release : async ( ) = > {
2020-03-30 23:27:59 +00:00
await loader . style . load_multiple ( [
2019-08-30 21:06:39 +00:00
"css/static/base.css" ,
"css/static/main.css" ,
2020-03-30 23:27:59 +00:00
] , {
cache_tag : cache_tag ( ) ,
max_parallel_requests : - 1
} ) ;
2019-08-30 21:06:39 +00:00
}
} ;
/* register tasks */
loader . register_task ( loader . Stage . INITIALIZING , {
name : "safari fix" ,
function : async ( ) = > {
/* safari remove "fix" */
if ( Element . prototype . remove === undefined )
Object . defineProperty ( Element . prototype , "remove" , {
enumerable : false ,
configurable : false ,
writable : false ,
value : function ( ) {
this . parentElement . removeChild ( this ) ;
}
} ) ;
} ,
priority : 50
} ) ;
loader . register_task ( loader . Stage . INITIALIZING , {
name : "Browser detection" ,
function : async ( ) = > {
navigator . browserSpecs = ( function ( ) {
let ua = navigator . userAgent , tem , M = ua . match ( /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i ) || [ ] ;
if ( /trident/i . test ( M [ 1 ] ) ) {
tem = /\brv[ :]+(\d+)/g . exec ( ua ) || [ ] ;
return { name : 'IE' , version : ( tem [ 1 ] || '' ) } ;
}
if ( M [ 1 ] === 'Chrome' ) {
tem = ua . match ( /\b(OPR|Edge)\/(\d+)/ ) ;
if ( tem != null ) return { name :tem [ 1 ] . replace ( 'OPR' , 'Opera' ) , version :tem [ 2 ] } ;
}
M = M [ 2 ] ? [ M [ 1 ] , M [ 2 ] ] : [ navigator . appName , navigator . appVersion , '-?' ] ;
if ( ( tem = ua . match ( /version\/(\d+)/i ) ) != null )
M . splice ( 1 , 1 , tem [ 1 ] ) ;
return { name :M [ 0 ] , version :M [ 1 ] } ;
} ) ( ) ;
2019-09-12 21:59:35 +00:00
console . log ( "Resolved browser manufacturer to \"%s\" version \"%s\"" , navigator . browserSpecs . name , navigator . browserSpecs . version ) ;
2019-08-30 21:06:39 +00:00
} ,
priority : 30
} ) ;
loader . register_task ( loader . Stage . INITIALIZING , {
name : "secure tester" ,
function : async ( ) = > {
/* we need https or localhost to use some things like the storage API */
if ( typeof isSecureContext === "undefined" )
( < any > window ) [ "isSecureContext" ] = location . protocol !== 'https:' && location . hostname !== 'localhost' ;
if ( ! isSecureContext ) {
loader . critical_error ( "TeaWeb cant run on unsecured sides." , "App requires to be loaded via HTTPS!" ) ;
throw "App requires a secure context!"
}
} ,
priority : 20
} ) ;
loader . register_task ( loader . Stage . INITIALIZING , {
name : "webassembly tester" ,
function : loader_webassembly . test_webassembly ,
priority : 20
} ) ;
loader . register_task ( loader . Stage . JAVASCRIPT , {
name : "javascript" ,
function : loader_javascript . load_scripts ,
priority : 10
} ) ;
loader . register_task ( loader . Stage . STYLE , {
name : "style" ,
function : loader_style . load_style ,
priority : 10
} ) ;
loader . register_task ( loader . Stage . TEMPLATES , {
name : "templates" ,
2020-02-22 13:30:17 +00:00
function : async ( ) = > {
2020-03-30 23:27:59 +00:00
await loader . templates . load_multiple ( [
2020-02-22 13:30:17 +00:00
"templates.html" ,
2020-03-27 15:15:15 +00:00
"templates/modal/musicmanage.html" ,
"templates/modal/newcomer.html" ,
2020-03-30 23:27:59 +00:00
] , {
cache_tag : cache_tag ( ) ,
max_parallel_requests : - 1
} ) ;
2020-02-22 13:30:17 +00:00
} ,
2019-08-30 21:06:39 +00:00
priority : 10
} ) ;
loader . register_task ( loader . Stage . LOADED , {
name : "loaded handler" ,
2020-03-30 11:44:18 +00:00
function : async ( ) = > loader . hide_overlay ( ) ,
2019-09-18 23:25:57 +00:00
priority : 5
2019-08-30 21:06:39 +00:00
} ) ;
loader . register_task ( loader . Stage . JAVASCRIPT_INITIALIZING , {
name : "lsx emoji picker setup" ,
function : async ( ) = > await ( window as any ) . setup_lsx_emoji_picker ( { twemoji : typeof ( window . twemoji ) !== "undefined" } ) ,
priority : 10
} ) ;
2019-08-31 16:31:01 +00:00
loader . register_task ( loader . Stage . SETUP , {
name : "page setup" ,
function : async ( ) = > {
const body = document . body ;
/* top menu */
{
const container = document . createElement ( "div" ) ;
container . setAttribute ( 'id' , "top-menu-bar" ) ;
body . append ( container ) ;
}
/* template containers */
{
const container = document . createElement ( "div" ) ;
container . setAttribute ( 'id' , "templates" ) ;
body . append ( container ) ;
}
/* sounds container */
{
const container = document . createElement ( "div" ) ;
container . setAttribute ( 'id' , "sounds" ) ;
body . append ( container ) ;
}
/* mouse move container */
{
const container = document . createElement ( "div" ) ;
container . setAttribute ( 'id' , "mouse-move" ) ;
body . append ( container ) ;
}
/* tooltip container */
{
const container = document . createElement ( "div" ) ;
container . setAttribute ( 'id' , "global-tooltip" ) ;
container . append ( document . createElement ( "a" ) ) ;
body . append ( container ) ;
}
} ,
priority : 10
} ) ;
2019-09-18 23:25:57 +00:00
/* test if we're getting loaded within a TeaClient preview window */
loader . register_task ( loader . Stage . SETUP , {
name : "TeaClient tester" ,
function : async ( ) = > {
//@ts-ignore
if ( typeof __teaclient_preview_notice !== "undefined" && typeof __teaclient_preview_error !== "undefined" ) {
loader . critical_error ( "Why you're opening TeaWeb within the TeaSpeak client?!" ) ;
throw "we're already a TeaClient!" ;
}
} ,
priority : 100
} ) ;
2020-03-30 11:44:18 +00:00
export function run() {
2020-04-01 19:47:33 +00:00
window [ "Module" ] = ( window [ "Module" ] || { } ) as any ; /* Why? */
2020-03-30 11:44:18 +00:00
/* TeaClient */
if ( node_require ) {
2020-04-01 19:47:33 +00:00
if ( __build . target !== "client" ) {
loader . critical_error ( "App seems not to be compiled for the client." , "This app has been compiled for " + __build . target ) ;
return ;
}
2020-04-01 13:36:37 +00:00
window . native_client = true ;
2020-03-30 11:44:18 +00:00
const path = node_require ( "path" ) ;
const remote = node_require ( 'electron' ) . remote ;
2020-04-01 13:36:37 +00:00
const render_entry = path . join ( remote . app . getAppPath ( ) , "/modules/" , "renderer" ) ;
const render = node_require ( render_entry ) ;
2020-03-30 11:44:18 +00:00
loader . register_task ( loader . Stage . INITIALIZING , {
name : "teaclient initialize" ,
2020-04-01 13:36:37 +00:00
function : render . initialize ,
2020-03-30 11:44:18 +00:00
priority : 40
} ) ;
2020-04-01 13:36:37 +00:00
} else {
2020-04-01 19:47:33 +00:00
if ( __build . target !== "web" ) {
loader . critical_error ( "App seems not to be compiled for the web." , "This app has been compiled for " + __build . target ) ;
return ;
}
2020-04-01 13:36:37 +00:00
window . native_client = false ;
2020-03-30 11:44:18 +00:00
}
2019-08-30 21:06:39 +00:00
2020-03-30 11:44:18 +00:00
if ( ! loader . running ( ) ) {
/* we know that we want to load the app */
loader . execute_managed ( ) ;
}
2019-08-30 21:06:39 +00:00
}