10 "format": "dddd, MMMM Do",
15 "margin-top": "calc(20vh - 70pt)",
16 "text-align": "center",
18 "font-family": "Noto Sans",
19 "font-weight": "lighter",
20 "text-shadow": "rgba(0, 0, 0, 0.8) 0px 7px 10px",
23 "format": ["h:mm", "A"],
25 {"font-size": "65pt", "font-weight": 200 },
26 {"font-size": "30pt", "font-weight": "lighter", "margin-left": "10pt"}
31 "font-family": "Noto Sans",
32 "text-align": "center",
33 "text-shadow": "rgba(0, 0, 0, 0.8) 0px 7px 10px",
38 "html":"<text style='display: none' class='active-appear'>Press any key to login</text>",
44 "text-align": "center",
45 "color": "rgba(255, 255, 255, 0.8)"
50 window.onerror = function() {
51 log.error("Error caught");
55 $(document).ready(()=> {
56 $log = $("#console .terminal");
57 $autoscroll = $("#console input");
61 if (typeof str == "object") str = JSON.stringify(str,null, 2);
62 str = "[" + moment().format("hh:mm:ss") + "]: " + str;
63 str = "<text style='color: " + color + "'>" + str + "</text>";
64 return $log.html() + str;
67 $log.html(this._parse(str,"white"));
68 if ($autoscroll.prop('checked'))
69 $log[0].scrollTop = $log[0].scrollHeight;
72 $log.html(this._parse(str,"red"));
73 if ($autoscroll.prop('checked'))
74 $log[0].scrollTop = $log[0].scrollHeight;
77 $log.html(this._parse(str,"yellow"));
78 if ($autoscroll.prop('checked'))
79 $log[0].scrollTop = $log[0].scrollHeight;
82 $log.html(this._parse(str,"lightblue"));
83 if ($autoscroll.prop('checked'))
84 $log[0].scrollTop = $log[0].scrollHeight;
89 * Scale an image up or down until it's larger than or equal to the viewport
92 var adjustBackground = function ($img) {
93 var viewportWidth = screen.width;
94 var viewportHeight = screen.height;
95 var viewportAspect = viewportWidth/viewportHeight;
96 var imgWidth = $img.width();
97 var imgHeight = $img.height();
98 var imgAspect = imgWidth/imgHeight;
100 /* First determine what is
101 the limiting factor (ie. if the image is wider, then the height is
102 is the limiting factor and needs to be adjustested */
103 if (imgAspect < viewportAspect) {
104 /* The view port is wider compared to its height than
105 the image is compared to the image height meaning
106 the width is the limiting dimension. Therefore we
107 set image width = view ports width use the aspect
108 ratio to set the correct height */
109 $img.width(viewportWidth);
110 $img.height(viewportWidth/imgAspect);
112 /* The image is wider than it is tall compared to the
113 viewport so we adjust the to fit */
114 $img.height(viewportHeight);
115 $img.width(viewportHeight*imgAspect);
117 this.centerImage($img);
120 var centerImage = function($img) {
121 var overlapWidth = $img.width() - screen.width;
122 var overlapHeight = $img.height() - screen.height;
125 // image overlaps viewport, move the image back
126 // half the length of the overlap
128 position: "relative",
129 right: overlapWidth/2,
130 bottom: overlapHeight/2
136 this.use_splash = true;
137 $(document).ready(() => {
143 this.lightdm = typeof (lightdm) == "undefined" ? {} : lightdm;
145 if (this.use_splash) {
146 this.splash = new SplashScreen();
148 $(this).trigger("ready");
152 auth(username, password, callback) {
153 // lightdm must have each of
154 let req = ["select_user", "is_authenticated", "authenticate"];
155 if (!req.every((x) => this.lightdm.hasOwnProperty(x) )) {
156 log.warn("Cannot attempt login because lightdm is missing the " +
157 "required fields. Please note that lightdm is not explicitly " +
158 "instantiated in a browser session.");
160 // call async so that events can be binded in cascade
161 setTimeout(() => $(this).trigger("deny"));
165 username = username || this.lightdm.select_user;
166 password = password || "";
167 // session_key = session_key || lightdm.sessions[0].key;
169 let auth_cb = () => {
170 this.lightdm.respond(password);
172 let auth_complete_cb = () => {
173 if (typeof callback == "function")
174 callback(this.lightdm.is_authenticated);
176 $(this).trigger(this.lightdm.is_authenticated ? "grant" : "deny");
179 window.show_prompt = auth_cb;
180 window.authentication_complete = auth_complete_cb;
181 this.lightdm.authenticate(username);
185 if (!this.lightdm.sessions.find(x => x.key == session_key)) {
186 log.error("Attempting to login without a valid session.");
190 if (!this.lightdm.is_authenticated) {
191 log.error("Attempting to login without authentication.");
194 this.lightdm.start_session_sync(session_key);
196 authenticateWithSelected($user_input, $password_input) {
200 fillUserSelect($el) {
201 if (!Array.isArray(this.lightdm.users)) {
202 log.warn("Cannot fill empty user list in lightdm.");
207 for (let s of this.lightdm.users)
208 $el.append("<option value=" + s.username + ">" + s.display_name + "</option>");
212 fillSessionSelect($el) {
213 if (!Array.isArray(this.lightdm.sessions)) {
214 log.warn("Cannot fill empty session list in lightdm.");
220 for (let s of this.lightdm.sessions)
221 $el.append("<option value=" + s.key + ">" + s.name + "</option>");
228 return this.lightdm.users;
232 return this.lightdm.sessions;
238 this.$el = $("#splash-screen");
239 this.$content = $("#splash-screen-content");
240 this.options = this.getUserOptions();
241 this.is_open = false;
242 this.last_active = 0;
243 this.active_timeout = 15;
245 if (!this.$el.length)
246 log.error("Missing-screen element.");
248 // fit background image to sreen size and center
249 this.$img = $(".splash-screen-img");
250 if (!this.$img.length)
251 log.warn("No background images supplied for splash screen.");
252 this.$img.each((i, v) => adjustBackground($(v)));
254 let options = this.options; // shorthand
255 if (typeof options == "object") {
256 // initilize global values if specfied in the config
257 this.is_open = false;
260 if (typeof options["active-timeout"] == "number")
261 this.active_timeout = options["active-timeout"];
262 if (options.filter == true)
263 this.$img.addClass("filter");
264 if (options.vignette == true)
265 this.$vignette = $("#vignette");
266 this.$vignette.show();
267 if (typeof options.content == "object")
268 this.initContent(options.content);
271 /******************** Event Listeners ********************/
272 this.clock = setInterval(() => {
273 $(this).trigger("tick");
275 if (!this.isActive())
276 $(this).trigger("inactive");
279 // update last active time
280 $(this).on("active", () => this.last_active = moment());
282 $(document).keyup((e) => {
283 // handle events in seperate method
284 this.keyHandler.call(this, e);
285 }).keypress((e) => this.keyHandler.call(this, e));
287 this.$el.click(() => {
289 }).mousemove((e) => {
290 if (!this.isActive())
291 $(this).trigger("active", e)
293 setTimeout(() => $(this).trigger("active"), 1);
297 * Loops through the user specified content and adds them to the DOM in order
299 initContent(content) {
300 for (let content_type in content) {
301 if (content_type == "clock")
302 this.initClock(content[content_type]);
303 else if (content_type == "html")
304 this.initHTML(content[content_type]);
306 log.warn("Specified content " + content_type + " is not valid.");
312 $.extend(true, options, DEF_OPT);
313 $.extend(true, options, {});
318 * open and close will toggle the screen and animate it opening and closing
319 * adds a resetTimeout function to automatically close after a period of user
322 if (this.state == "closed" || this.state == "moving") {
323 log.warn("Cannot close splash screen when state is: " + this.state);
327 this.state = "moving";
330 }, time, "easeInCubic", () => {
331 this.state = "closed";
332 clearTimeout(this.resetTimeout);
336 if (this.state == "open" || this.state == "moving") {
337 log.warn("Cannot open splash screen when state is: " + this.state);
340 clearTimeout(this.resetTimeout);
341 let reset_duration = 60*1000;
343 if (this.state == "open" || this.state == "moving") {
344 this.resetTimeout = setTimeout(this.reset, reset_duration);
347 this.state = "moving";
350 }, time, "easeInCubic", () => {
352 // close the screen after 1 minute of inactivty
353 this.resetTimeout = setTimeout(() => this.reset, reset_duration);
357 if (this.state == "open") {
359 $(this).trigger("timeout");
364 * handles the key events for the splash
373 if (this.state == "open") this.close();
374 else if (this.state == "closed") this.open();
377 if (e.keyCode != 82 && e.keyCode != 17) // for testing
382 // stop reset timeout since there has been user activity
383 if (this.state == "open")
384 clearTimeout(this.resetTimeout);
386 if (!this.isActive())
387 $(this).trigger("active", e);
391 if (moment().diff(this.last_active, "seconds", true) > 30) {
398 * Creates clock elements based on the usr config
401 if (typeof opts != "object") {
402 log.error("Unable to initialize clock thats not an object");
405 // handle arrays and a single clock object
406 if (!Array.isArray(opts))
409 for (let i in opts) {
410 this.$clock = $("<div id='clock-" + i + "' class='clock'></div>");
411 this.$content.append(this.$clock);
412 this.startClock(this.$clock, opts[i]);
417 * Applys the css specfied in the argument opts to the jQuery oboject $clock.
418 * Subscribes the clock to a tick event
420 startClock($clock, opts) {
421 if (typeof opts != "object") {
422 log.error("Clock opts is not a valid object");
425 // handle multiple formats for multiple clocks on the same line
426 if(typeof opts.format == "string")
427 opts.format = [opts.format];
429 // ensure the format is now an array
430 if(!Array.isArray(opts.format)) {
431 log.error(`Specfied clock format is not a valid type.
432 Type can be a single string or Array.`);
436 if(!Array.isArray(opts.css))
437 opts.css = [opts.css];
439 for (let i in opts.format) {
441 let $format = $("<sub></sub>");
442 // create text field in clock
443 $clock.append($format);
445 if (i < opts.css.length && typeof opts.css[i] == "object")
446 $format.css(opts.css[i]);
449 $format.text(moment().format(opts.format[i]));
450 $(this).on("tick", () => {
451 $format.text(moment().format(opts.format[i]));
455 if (typeof opts["parent-css"] == "object")
456 $clock.css(opts["parent-css"]);
462 * Inserts HTML specified in the user config into the splash screen
463 * accepts plain strings and objects. String literals are interpreted as
464 * normal text element. Objects are set using the jQuery API
467 // handle single objects and strings
468 if (!Array.isArray(opts)) {
472 for (let el of opts) {
473 if (typeof el == "string") {
474 let $el = $("<text>");
476 // create simple text element
477 this.$content.append($el);
478 } else if (typeof el == "object") {
479 // let user specify element properites in object el.
480 let $el = $("<div>");
481 for (let prop in el) {
484 this.$content.append($el);
487 log.warn("Splash screen html element is invalid type");