3 * Creates a splash screen with custom generated content,
4 * diplays when the user inactive.
6 * A Plugin for the LoginManager class
9 * -----------------------------------------------------------------------------
11 * 'load' signifies that SplashScreen has finished asyncrhonously
12 * loading its config files.
14 * 'ready' emitted once the SplashScreen has finished creating its content
17 * 'init' causes the SplashScreen content to be generated
21 if (window.navigator.language === "en" || window.navigator.language === "en-US" ||window.navigator.language === "en-GB" ||window.navigator.language === "en-CA" ||window.navigator.language === "en-AU" ||window.navigator.language === "es-ES" ) {
22 text[0]="Press any EnterKey to login"
23 text[1]="WELCOME BACK"
24 text[2]="Select Session"
28 else if(window.navigator.language === "fr" || window.navigator.language === "fr-CA" ){
29 text[0] = "Appuyez sur n'importe quelle touche EnterKey pour vous connecter"
30 text[1] = "Bienvenue à nouveau"
31 text[2] = "Sélectionner une session"
32 text[3] = "Utilisateur"
33 text[4] = "Mot de passe"
35 else if(window.navigator.language === "zh" || window.navigator.language === "zh-CN" || window.navigator.language === "zh-TW" ){
36 text[0] ="按任意Enter键登录"
43 text[0]="Enterキーを押すとログインできます。"
49 // document.onkeydown = function (e){
50 // document.getElementById("inputPassword").focus();
52 document.onselectstart = function(){return false;};
53 document.ondragstart = function(){return false;};
56 /* Speed of SplashScreen transitions */
57 this._ANIMATION_DUR = 300;
59 /* Default options for the splash screen */
69 // "format": "dddd, MMMM Do",
74 // "margin-top": "calc(20vh - 70pt)",
75 // "text-align": "center",
76 // "font-size": "70pt",
77 // "font-family": "Noto Sans",
78 // "font-weight": "lighter",
79 // "text-shadow": "rgba(0, 0, 0, 0.25) 0px 3px 3px",
82 "format": ["h:mm", "A"],
84 {'font-family': 'MyFont3',"font-size": "40pt", "font-weight": 200 },
85 {'font-family': 'MyFont3',"font-size": "20pt", "font-weight": "lighter", "margin-left": "10pt"}
89 "margin-left": "20vw",
91 "font-family": 'MyFont3',
93 "text-shadow": "rgba(0, 0, 0, 0.25) 0px 3px 3px",
98 "html":"<text style='display: none' class='active-appear'>"+text[0]+"</text>",
100 "margin-top": "40vh",
101 "font-weight": "200",
103 "text-align": "center",
104 "color": "rgba(255, 255, 255, 0.8)"
109 this.template = `<!-- Autogenerated SplashScreen -->
110 <div id="splash-screen">
111 <div class="vignette"></div>
112 <div id="splash-screen-content"></div>
114 <!-- End Autogenerated SplashScreen -->`;
115 this.$el = $(this.template);
116 $("body").prepend(this.$el);
119 // listen to the init event
120 $(this).on("init", () => this._init());
124 * Generates the content specified by the user config.
125 * Should be called after the load event has been triggered.
128 this.$content = $("#splash-screen-content");
129 this._state = "closed";
130 this._last_active = 0;
131 this._active_timeout = 15;
133 let options = this._options; // shorthand
134 if (typeof options == "object") {
136 // initilize global values if specfied in the config
137 if (typeof options.img == "string") {
138 this.$img = $(`<img class="splash-screen-img" src="${options.img}">`);
140 this.$el.prepend(this.$img);
141 this.$img.one("load", () => {
142 // fit background image to sreen size and center
143 adjustBackground($(".splash-screen-img"))
147 if (typeof options["active-timeout"] == "number")
148 this._active_timeout = options["active-timeout"];
150 if (options.filter == true)
151 this.$img.addClass("filter");
153 if (options.vignette == true) {
154 this.$vignette = $("#vignette");
155 this.$vignette.show();
158 if (typeof options.transition == "string")
159 this.transition = options.transition;
161 if (typeof options.content == "object")
162 this._initContent(options.content);
164 $(this).trigger("ready");
167 /******************** Event Listeners ********************/
168 this.clock = setInterval(() => {
169 $(this).trigger("tick");
171 if (!this._isActive())
172 $(this).trigger("inactive");
175 // update last active time
176 $(this).on("active", () => this._last_active = moment());
178 $(document).keyup((e) => {
179 // handle events in seperate method
180 this._keyHandler.call(this, e);
181 }).keypress((e) => this._keyHandler.call(this, e));
183 /* Bind event listners to trigger activity event. This can be used on the
184 front end to implement spcific behaivours while the user is active */
185 this.$el.click(() => {
187 document.getElementById("inputPassword").focus();
188 }).mousemove((e) => {
189 if (!this._isActive())
190 $(this).trigger("active", e)
192 setTimeout(() => $(this).trigger("active"), 1);
196 * Loops through the user specified content and appends them to the DOM
197 * in the order specified by the user config
199 _initContent(content) {
200 for (let content_type in content) {
201 if (content_type == "clock")
202 this._initClock(content[content_type]);
203 else if (content_type == "html")
204 this._initHTML(content[content_type]);
206 log.warn("Specified content " + content_type + " is not valid.");
211 * Asyncrhonously reads JSON config file from json/SplashScreen.json
212 * and overwrites the default options with those specified by the config.
214 * Triggers: 'load' on completion. Caller (LoginManager) must listen for this
215 * event to then trigger 'init'
219 $.extend(true, options, this._DEF_OPT);
221 $.getJSON("json/SplashScreen.json", (data) => {
222 $.extend(true, options, data);
223 this._options = options;
224 $(this).trigger("load");
226 $.extend(true, options, {});
227 this._options = options;
228 $(this).trigger("load");
233 * Closes the splash screen if there has been no user activity
236 if (this._state == "open") {
238 $(this).trigger("timeout");
243 * Determines if there was user acitivty within in a given amount
245 * Returns 1 if splash screen is active, else 0
248 if (moment().diff(this._last_active, "seconds", true) > 30) {
255 * Creates clock elements based on the usr config.
256 * Appends each clock to the DOM and binds update events using _startClock
259 if (typeof opts != "object") {
260 log.error("Unable to initialize clock thats not an object");
263 // handle arrays and a single clock object
264 if (!Array.isArray(opts))
267 /* loop through each clock in the config and add it to the dom,
268 then initialize an update event using start clock */
269 for (let i in opts) {
270 this.$clock = $("<div id='clock-" + i + "' class='clock'></div>");
271 this.$content.append(this.$clock);
272 this._startClock(this.$clock, opts[i]);
277 * Applys the css specfied in the argument opts to the jQuery oboject $clock.
278 * Subscribes the clock to a tick event
280 _startClock($clock, opts) {
281 if (typeof opts != "object") {
282 log.error("Clock opts is not a valid object");
285 // handle multiple formats for multiple clocks on the same line
286 if(typeof opts.format == "string")
287 opts.format = [opts.format];
289 // ensure the format is now an array
290 if(!Array.isArray(opts.format)) {
291 log.error(`Specfied clock format is not a valid type.
292 Type can be a single string or Array.`);
296 if(!Array.isArray(opts.css))
297 opts.css = [opts.css];
299 for (let i in opts.format) {
301 let $format = $("<sub></sub>");
302 // create text field in clock
303 $clock.append($format);
305 if (i < opts.css.length && typeof opts.css[i] == "object")
306 $format.css(opts.css[i]);
309 $format.text(moment().format(opts.format[i]));
310 $(this).on("tick", () => {
311 $format.text(moment().format(opts.format[i]));
315 if (typeof opts["parent-css"] == "object")
316 $clock.css(opts["parent-css"]);
322 * Inserts HTML specified in the user config into the splash screen
323 * accepts plain strings and objects. String literals are interpreted as
324 * normal text element. Objects are set using the jQuery API
327 // handle single objects and strings
328 if (!Array.isArray(opts)) {
332 for (let el of opts) {
333 if (typeof el == "string") {
334 let $el = $("<text>");
336 // create simple text element
337 this.$content.append($el);
338 } else if (typeof el == "object") {
339 // let user specify element properites in object el.
340 let $el = $("<div>");
341 for (let prop in el) {
344 this.$content.append($el);
347 log.warn("Splash screen html element is invalid type");
354 * Handles the key events for the SplachScreen and active-inactive events
359 case 13: // Enter key
360 if (this._state == "closed")
361 document.getElementById("inputPassword").focus();
362 $('#inputPassword').select();
366 if (this._state == "open"){
367 if(document.activeElement){
368 document.activeElement.blur();
371 else if (this._state == "closed"){
372 document.getElementById("inputPassword").focus();
373 $('#inputPassword').select();
377 // // if (this._state == "closed")
382 // stop reset timeout since there has been user activity
383 if (this._state == "open")
384 clearTimeout(this.resetTimeout);
386 // trigger active event if the user has been inactive long enough
387 if (!this._isActive())
388 $(this).trigger("active", e);
392 * _open and _close will toggle the screen and animate it opening and closing
393 * adds a resetTimeout function to automatically close after a period of user
396 * Uses a _state machine consisting of {'open', 'closed', 'moving'}.
397 * Transitions are not possible while the _state == moving. This prevents
398 * fillUserSelect from trigger concurrernt transitions which would lead to
399 * undefined behaivour.
402 if (this._state != "open") {
403 log.warn("Cannot close splash screen when _state is: " + this._state);
407 this._state = "moving";
408 if (this.transition == "fade") {
409 this.$el.fadeIn("slow", () => {
410 this._state = "closed";
411 this.$content.fadeIn("slow");
412 clearTimeout(this.resetTimeout);
414 } else if (this.transition == "slide") {
417 },"slow", "easeOutQuint", () => {
418 this._state = "closed";
419 clearTimeout(this.resetTimeout);
426 if (this._state != "closed") {
427 log.warn("Cannot open splash screen when _state is: " + this._state);
430 clearTimeout(this.resetTimeout);
431 let reset_duration = 60*1000;
433 if (this._state == "open" || this._state == "moving") {
434 this.resetTimeout = setTimeout(this.reset, reset_duration);
437 this._state = "moving";
439 if (this.transition == "fade") {
440 this.$content.fadeOut("fast", () => {
441 this.$el.fadeOut(this._ANIMATION_DUR, () => {
442 this._state = "open";
446 } else if (this.transition == "slide") {
449 }, "slow", "easeInCubic", () => {
450 this._state = "open";