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
22 /* Speed of SplashScreen transitions */
23 this._ANIMATION_DUR = 300;
25 /* Default options for the splash screen */
35 "format": "dddd, MMMM Do",
40 "margin-top": "calc(20vh - 70pt)",
41 "text-align": "center",
43 "font-family": "Noto Sans",
44 "font-weight": "lighter",
45 "text-shadow": "rgba(0, 0, 0, 0.5) 0px 7px 10px",
48 "format": ["h:mm", "A"],
50 {"font-size": "65pt", "font-weight": 200 },
51 {"font-size": "30pt", "font-weight": "lighter", "margin-left": "10pt"}
56 "font-family": "Noto Sans",
57 "text-align": "center",
58 "text-shadow": "rgba(0, 0, 0, 0.5) 0px 7px 10px",
63 "html":"<text style='display: none' class='active-appear'>Press any key to login</text>",
68 "text-align": "center",
69 "color": "rgba(255, 255, 255, 0.8)"
74 this.template = `<!-- Autogenerated SplashScreen -->
75 <div id="splash-screen">
76 <div class="vignette"></div>
77 <div id="splash-screen-content"></div>
79 <!-- End Autogenerated SplashScreen -->`;
80 this.$el = $(this.template);
81 $("body").prepend(this.$el);
84 // listen to the init event
85 $(this).on("init", () => this._init());
89 * Generates the content specified by the user config.
90 * Should be called after the load event has been triggered.
93 this.$content = $("#splash-screen-content");
94 this._state = "closed";
95 this._last_active = 0;
96 this._active_timeout = 15;
98 let options = this._options; // shorthand
99 if (typeof options == "object") {
101 // initilize global values if specfied in the config
102 if (typeof options.img == "string") {
103 this.$img = $(`<img class="splash-screen-img" src="${options.img}">`);
105 this.$el.prepend(this.$img);
106 this.$img.one("load", () => {
107 // fit background image to sreen size and center
108 adjustBackground($(".splash-screen-img"))
112 if (typeof options["active-timeout"] == "number")
113 this._active_timeout = options["active-timeout"];
115 if (options.filter == true)
116 this.$img.addClass("filter");
118 if (options.vignette == true) {
119 this.$vignette = $("#vignette");
120 this.$vignette.show();
123 if (typeof options.transition == "string")
124 this.transition = options.transition;
126 if (typeof options.content == "object")
127 this._initContent(options.content);
129 $(this).trigger("ready");
132 /******************** Event Listeners ********************/
133 this.clock = setInterval(() => {
134 $(this).trigger("tick");
136 if (!this._isActive())
137 $(this).trigger("inactive");
140 // update last active time
141 $(this).on("active", () => this._last_active = moment());
143 $(document).keyup((e) => {
144 // handle events in seperate method
145 this._keyHandler.call(this, e);
146 }).keypress((e) => this._keyHandler.call(this, e));
148 /* Bind event listners to trigger activity event. This can be used on the
149 front end to implement spcific behaivours while the user is active */
150 this.$el.click(() => {
152 }).mousemove((e) => {
153 if (!this._isActive())
154 $(this).trigger("active", e)
156 setTimeout(() => $(this).trigger("active"), 1);
160 * Loops through the user specified content and appends them to the DOM
161 * in the order specified by the user config
163 _initContent(content) {
164 for (let content_type in content) {
165 if (content_type == "clock")
166 this._initClock(content[content_type]);
167 else if (content_type == "html")
168 this._initHTML(content[content_type]);
170 log.warn("Specified content " + content_type + " is not valid.");
175 * Asyncrhonously reads JSON config file from json/SplashScreen.json
176 * and overwrites the default options with those specified by the config.
178 * Triggers: 'load' on completion. Caller (LoginManager) must listen for this
179 * event to then trigger 'init'
183 $.extend(true, options, this._DEF_OPT);
185 $.getJSON("json/SplashScreen.json", (data) => {
186 $.extend(true, options, data);
187 this._options = options;
188 $(this).trigger("load");
190 $.extend(true, options, {});
191 this._options = options;
192 $(this).trigger("load");
197 * Closes the splash screen if there has been no user activity
200 if (this._state == "open") {
202 $(this).trigger("timeout");
207 * Determines if there was user acitivty within in a given amount
209 * Returns 1 if splash screen is active, else 0
212 if (moment().diff(this._last_active, "seconds", true) > 30) {
219 * Creates clock elements based on the usr config.
220 * Appends each clock to the DOM and binds update events using _startClock
223 if (typeof opts != "object") {
224 log.error("Unable to initialize clock thats not an object");
227 // handle arrays and a single clock object
228 if (!Array.isArray(opts))
231 /* loop through each clock in the config and add it to the dom,
232 then initialize an update event using start clock */
233 for (let i in opts) {
234 this.$clock = $("<div id='clock-" + i + "' class='clock'></div>");
235 this.$content.append(this.$clock);
236 this._startClock(this.$clock, opts[i]);
241 * Applys the css specfied in the argument opts to the jQuery oboject $clock.
242 * Subscribes the clock to a tick event
244 _startClock($clock, opts) {
245 if (typeof opts != "object") {
246 log.error("Clock opts is not a valid object");
249 // handle multiple formats for multiple clocks on the same line
250 if(typeof opts.format == "string")
251 opts.format = [opts.format];
253 // ensure the format is now an array
254 if(!Array.isArray(opts.format)) {
255 log.error(`Specfied clock format is not a valid type.
256 Type can be a single string or Array.`);
260 if(!Array.isArray(opts.css))
261 opts.css = [opts.css];
263 for (let i in opts.format) {
265 let $format = $("<sub></sub>");
266 // create text field in clock
267 $clock.append($format);
269 if (i < opts.css.length && typeof opts.css[i] == "object")
270 $format.css(opts.css[i]);
273 $format.text(moment().format(opts.format[i]));
274 $(this).on("tick", () => {
275 $format.text(moment().format(opts.format[i]));
279 if (typeof opts["parent-css"] == "object")
280 $clock.css(opts["parent-css"]);
286 * Inserts HTML specified in the user config into the splash screen
287 * accepts plain strings and objects. String literals are interpreted as
288 * normal text element. Objects are set using the jQuery API
291 // handle single objects and strings
292 if (!Array.isArray(opts)) {
296 for (let el of opts) {
297 if (typeof el == "string") {
298 let $el = $("<text>");
300 // create simple text element
301 this.$content.append($el);
302 } else if (typeof el == "object") {
303 // let user specify element properites in object el.
304 let $el = $("<div>");
305 for (let prop in el) {
308 this.$content.append($el);
311 log.warn("Splash screen html element is invalid type");
318 * Handles the key events for the SplachScreen and active-inactive events
323 case 13: // Enter key
324 if (this._state == "closed")
328 if (this._state == "open")
330 else if (this._state == "closed")
334 if (this._state == "closed")
339 // stop reset timeout since there has been user activity
340 if (this._state == "open")
341 clearTimeout(this.resetTimeout);
343 // trigger active event if the user has been inactive long enough
344 if (!this._isActive())
345 $(this).trigger("active", e);
349 * _open and _close will toggle the screen and animate it opening and closing
350 * adds a resetTimeout function to automatically close after a period of user
353 * Uses a _state machine consisting of {'open', 'closed', 'moving'}.
354 * Transitions are not possible while the _state == moving. This prevents
355 * fillUserSelect from trigger concurrernt transitions which would lead to
356 * undefined behaivour.
359 if (this._state != "open") {
360 log.warn("Cannot close splash screen when _state is: " + this._state);
364 this._state = "moving";
365 if (this.transition == "fade") {
366 this.$el.fadeIn("slow", () => {
367 this._state = "closed";
368 this.$content.fadeIn("slow");
369 clearTimeout(this.resetTimeout);
371 } else if (this.transition == "slide") {
374 },"slow", "easeOutQuint", () => {
375 this._state = "closed";
376 clearTimeout(this.resetTimeout);
383 if (this._state != "closed") {
384 log.warn("Cannot open splash screen when _state is: " + this._state);
387 clearTimeout(this.resetTimeout);
388 let reset_duration = 60*1000;
390 if (this._state == "open" || this._state == "moving") {
391 this.resetTimeout = setTimeout(this.reset, reset_duration);
394 this._state = "moving";
396 if (this.transition == "fade") {
397 this.$content.fadeOut("fast", () => {
398 this.$el.fadeOut(this._ANIMATION_DUR, () => {
399 this._state = "open";
403 } else if (this.transition == "slide") {
406 }, "slow", "easeInCubic", () => {
407 this._state = "open";