OSDN Git Service

fixed lightdm crashing every 9 seconds
[alterlinux/lightdm-webkit2-theme-alter.git] / js / theme.js
1
2 const  DEF_OPT =
3 {
4         "fit": true,
5         "filter": false,
6         "vignette": true,
7         "active-timeout": 15,
8         "content": {
9                 "clock": [{
10                         "format": "dddd, MMMM Do",
11                         "css": {
12                                 "color": "white"
13                         },
14                         "parent-css": {
15                                 "margin-top": "calc(20vh - 70pt)",
16                                 "text-align": "center",
17                                 "font-size": "70pt",
18                                 "font-family": "Noto Sans",
19                                 "font-weight": "lighter",
20                                 "text-shadow": "rgba(0, 0, 0, 0.8) 0px 7px 10px",
21                         }
22                 },{
23                         "format": ["h:mm", "A"],
24                         "css": [
25                                 {"font-size": "65pt", "font-weight": 200 },
26                                 {"font-size": "30pt", "font-weight": "lighter", "margin-left": "10pt"}
27                         ],
28                         "parent-css": {
29                                 "margin-top": "20vh",
30                                 "color": "white",
31                                 "font-family": "Noto Sans",
32                                 "text-align": "center",
33                                 "text-shadow": "rgba(0, 0, 0, 0.8) 0px 7px 10px",
34                         }
35                 }],
36
37                 "html": [{
38                         "html":"<text style='display: none' class='active-appear'>Press any key to login</text>",
39                         "css": {
40
41                                 "margin-top": "5vh",
42                                 "font-weight": "200",
43                                 "font-size": "23pt",
44                                 "text-align": "center",
45                                 "color": "rgba(255, 255, 255, 0.8)"
46                         }
47                 }]
48         }
49 };
50 window.onerror = function() {
51     log.error("Error caught");
52 };
53
54 var log = {
55         error (str) {
56                 if (typeof str == "object") str = JSON.stringify(str,null, 2);
57                 let $line = $("<text>").text(str);
58                 $("#debug").append($line);
59         },
60         warn () {
61
62         },
63         debug () {
64
65         }
66 }
67
68 /**
69  * Scale an image up or down until it's larger than or equal to the viewport
70  * and then center it.
71  */
72 var adjustBackground = function ($img) {
73         var viewportWidth = screen.width;
74         var viewportHeight = screen.height;
75         var viewportAspect = viewportWidth/viewportHeight;
76         var imgWidth = $img.width();
77         var imgHeight = $img.height();
78         var imgAspect = imgWidth/imgHeight;
79
80         /* First determine what is
81            the limiting factor (ie. if the image is wider, then the height is
82            is the limiting factor and needs to be adjustested */
83         if (imgAspect < viewportAspect) {
84                 /* The view port is wider compared to its height than
85                    the image is compared to the image height  meaning
86                    the width is the limiting dimension. Therefore we
87                    set image width = view ports width use the aspect
88                    ratio to set the correct height */
89                 $img.width(viewportWidth);
90                 $img.height(viewportWidth/imgAspect);
91         } else {
92                 /* The image is wider than it is tall compared to the
93                    viewport so we adjust the to fit */
94                 $img.height(viewportHeight);
95                 $img.width(viewportHeight*imgAspect);
96         }
97         this.centerImage($img);
98 }
99
100 var centerImage =  function($img) {
101         var overlapWidth = $img.width() - screen.width;
102         var overlapHeight = $img.height() - screen.height;
103
104
105         // image overlaps viewport, move the image back
106         // half the length of the overlap
107         $img.css({
108                 position: "relative",
109                 right: overlapWidth/2,
110                 bottom: overlapHeight/2
111         });
112 }
113
114 class LoginManager {
115         constructor() {
116                 this.use_splash = true;
117                 $(document).ready(() => {
118                         this.init();
119                 });
120         }
121
122         init() {
123                                 this.lightdm = typeof (lightdm) == "undefined" ? {} : lightdm;
124
125                 if (this.use_splash) {
126                         this.splash = new SplashScreen();
127                 }
128                 $(this).trigger("ready");
129         }
130
131
132         auth(username, password, callback) {
133                 // lightdm must have each of
134                 let req = ["select_user", "is_authenticated", "authenticate"];
135                 if (!req.every((x) => this.lightdm.hasOwnProperty(x) )) {
136                         log.warn("Cannot attempt login because lightdm is missing the " +
137                         "required fields. Please note that lightdm is not explicitly " +
138                         "instantiated in a browser session.");
139
140                         // call async so that events can be binded in cascade
141                         setTimeout(() => $(this).trigger("deny"));
142                         return;
143                 }
144
145                 username = username || this.lightdm.select_user;
146                 password = password || "";
147                 //  session_key = session_key || lightdm.sessions[0].key;
148
149                 let auth_cb = () =>  {
150                     this.lightdm.respond(password);
151     }
152                 let auth_complete_cb = () => {
153                         if (typeof callback == "function")
154                                 callback(this.lightdm.is_authenticated);
155
156                         $(this).trigger(this.lightdm.is_authenticated ? "grant" : "deny");
157                 }
158
159           window.show_prompt = auth_cb;
160                 window.authentication_complete = auth_complete_cb;
161                 this.lightdm.authenticate(username);
162   }
163
164         login(session_key) {
165                 if (!this.lightdm.sessions.find(x => x.key == session_key)) {
166                         log.error("Attempting to login without a valid session.");
167                         return;
168                 }
169
170                 if (!this.lightdm.is_authenticated) {
171                         log.error("Attempting to login without authentication.");
172                         return;
173                 }
174                 this.lightdm.start_session_sync(session_key);
175         }
176         authenticateWithSelected($user_input, $password_input) {
177
178         }
179
180         fillUserSelect($el) {
181                         if (!Array.isArray(this.lightdm.users)) {
182                                         log.warn("Cannot fill empty user list in lightdm.");
183                                         return;
184                         }
185
186                         $el.empty();
187                         for (let s of this.lightdm.users)
188                                         $el.append("<option value=" + s.username + ">" + s.display_name + "</option>");
189                         $el.formSelect();
190         }
191
192         fillSessionSelect($el) {
193                 if (!Array.isArray(this.lightdm.sessions)) {
194                                 log.warn("Cannot fill empty session list in lightdm.");
195                                 return;
196                 }
197
198                 $el.empty();
199
200                 for (let s of this.lightdm.sessions)
201                                 $el.append("<option value=" + s.key + ">" + s.name + "</option>");
202
203
204                 $el.formSelect();
205         }
206
207         get users() {
208                 return this.lightdm.users;
209         }
210
211         get sessions() {
212                 return this.lightdm.sessions;
213         }
214 }
215
216 class SplashScreen {
217         constructor() {
218                 this.$el = $("#splash-screen");
219                 this.$content = $("#splash-screen-content");
220                 this.options = this.getUserOptions();
221                 this.is_open = false;
222                 this.last_active = 0;
223                 this.active_timeout = 15;
224
225                 if (!this.$el.length)
226                         log.error("Missing-screen element.");
227
228                 // fit background image to sreen size and center
229                 this.$img = $(".splash-screen-img");
230                 if (!this.$img.length)
231                         log.warn("No background images supplied for splash screen.");
232                 this.$img.each((i, v) => adjustBackground($(v)));
233
234                 let options = this.options; // shorthand
235                 if (typeof options == "object") {
236                         // initilize global values if specfied in the config
237                         this.is_open = false;
238
239
240                         if (typeof options["active-timeout"] == "number")
241                                 this.active_timeout = options["active-timeout"];
242                         if (options.filter == true)
243                                 this.$img.addClass("filter");
244                         if (options.vignette == true)
245                                 this.$vignette = $("#vignette");
246                                 this.$vignette.show();
247                         if (typeof options.content == "object")
248                                 this.initContent(options.content);
249                 }
250
251                 /******************** Event Listeners ********************/
252                 this.clock = setInterval(() => {
253                         $(this).trigger("tick");
254                                 log.error("tick");
255                         if (!this.isActive())
256                                 $(this).trigger("inactive");
257                 }, 500);
258
259                 // update last active time
260                 $(this).on("active", () => this.last_active = moment());
261
262                 $(document).keyup((e) => {
263                         // handle events in seperate method
264                         this.keyHandler.call(this, e);
265                 }).keypress((e) => this.keyHandler.call(this, e));
266
267                 this.$el.click(() => {
268                         this.open();
269                 }).mousemove((e) => {
270                         if (!this.isActive())
271                                 $(this).trigger("active", e)
272                 });
273                 setTimeout(() => $(this).trigger("active"), 1);
274         }
275
276         /**
277          * Loops through the user specified content and adds them to the DOM in order
278          */
279         initContent(content) {
280                 for (let content_type in content) {
281                         if (content_type == "clock")
282                                 this.initClock(content[content_type]);
283                         else if (content_type == "html")
284                                 this.initHTML(content[content_type]);
285                         else
286                                 log.warn("Specified content " + content_type + " is not valid.");
287                 }
288         }
289
290         getUserOptions() {
291                 let options = {};
292                 $.extend(true, options, DEF_OPT);
293                 $.extend(true, options, {});
294                 return options;
295         }
296
297         /**
298          * open and close will toggle the screen and animate it opening and closing
299          * adds a resetTimeout function to automatically close after a period of user
300          * inactivity */
301         close(time=450)  {
302                 if (!this.is_open)
303                         return
304                 this.$el.animate({
305                         top: "0"
306                 }, time, "easeInCubic", () => {
307                         this.is_open = false
308                         clearTimeout(this.resetTimeout);
309                 });
310         }
311         open(time=400) {
312                 clearTimeout(this.resetTimeout);
313                 let reset_duration = 60*1000;
314
315
316                 if (this.is_open) {
317                         this.resetTimeout = setTimeout(this.reset, reset_duration);
318                         return;
319                 }
320                 this.$el.animate({
321                         top: "-100%"
322                 }, time, "easeInCubic", () => {
323                         this.is_open = true;
324                         // close the screen after 1 minute of inactivty
325                         this.resetTimeout = setTimeout(() => this.reset, reset_duration);
326                 });
327         }
328         reset() {
329                 if (this.is_open == true) {
330
331                         this.close();
332                         $(this).trigger("timeout");
333                 }
334         }
335
336         /**
337          * handles the key events for the splash
338          */
339         keyHandler(e) {
340                 switch (e.keyCode) {
341                         case 32:
342                         case 13:
343                                 this.open();
344                                 break;
345                         case 27:
346                                 if (this.is_open) this.close();
347                                 else this.open();
348                                 break;
349                         default:
350                                 if (e.keyCode != 82 && e.keyCode != 17) // for testing
351                                 this.open();
352                                 break;
353                 }
354
355                 // stop reset timeout since there has been user activity
356                 if (this.is_open)
357                         clearTimeout(this.resetTimeout);
358
359                 if (!this.isActive())
360                         $(this).trigger("active", e);
361         }
362
363         isActive() {
364                 if (moment().diff(this.last_active, "seconds", true) > 30) {
365                         return 0;
366                 }
367                 return 1;
368         }
369
370         /**
371          *  Creates clock elements based on the usr config
372          */
373         initClock(opts) {
374                 if (typeof opts != "object") {
375                         log.error("Unable to initialize clock thats not an object");
376                         return -1;
377                 }
378                 // handle arrays and a single clock object
379                 if (!Array.isArray(opts))
380                         opts = [opts];
381
382                 for (let i in opts) {
383                         this.$clock = $("<div id='clock-" + i + "' class='clock'></div>");
384                         this.$content.append(this.$clock);
385                         this.startClock(this.$clock, opts[i]);
386                 }
387         }
388
389         /**
390          * Applys the css specfied in the argument opts to the jQuery oboject $clock.
391          * Subscribes the clock to a tick event
392          */
393         startClock($clock, opts) {
394                 if (typeof opts != "object") {
395                         log.error("Clock opts is not a valid object");
396                         return -1;
397                 }
398                 // handle multiple formats for multiple clocks on the same line
399                 if(typeof opts.format == "string")
400                         opts.format = [opts.format];
401
402                 // ensure the format is now an array
403                 if(!Array.isArray(opts.format)) {
404                         log.error(`Specfied clock format is not a valid type.
405                                 Type can be a single string or Array.`);
406                         return -1;
407                 }
408
409                 if(!Array.isArray(opts.css))
410                         opts.css = [opts.css];
411
412                 for (let i in opts.format) {
413
414                         let $format = $("<sub></sub>");
415                         // create text field in clock
416                         $clock.append($format);
417                         // apply css styles
418                         if (i < opts.css.length && typeof opts.css[i] == "object")
419                                 $format.css(opts.css[i]);
420
421                         // start clock
422                         $format.text(moment().format(opts.format[i]));
423                         $(this).on("tick", () => {
424                                 $format.text(moment().format(opts.format[i]));
425                         });
426                 }
427
428                 if (typeof opts["parent-css"] == "object")
429                         $clock.css(opts["parent-css"]);
430                 log.debug($clock);
431                 $clock.show();
432         }
433
434         /**
435          * Inserts HTML specified in the user config into the splash screen
436          * accepts plain strings and objects. String literals are interpreted as
437          * normal text element. Objects are set using the jQuery API
438          */
439         initHTML(opts) {
440                 // handle single objects and strings
441                 if (!Array.isArray(opts)) {
442                         opts = [opts];
443                 }
444
445                 for (let el of opts) {
446                         if (typeof el == "string") {
447                                 let $el = $("<text>");
448                                 $el.text(el);
449                                 // create simple text element
450                                 this.$content.append($el);
451                         } else if (typeof el == "object") {
452                                 // let user specify element properites in object el.
453                                 let $el = $("<div>");
454                                 for (let prop in el) {
455                                         $el[prop](el[prop]);
456                                 }
457                                 this.$content.append($el);
458
459                         } else {
460                                 log.warn("Splash screen html element is invalid type");
461                         }
462                 }
463
464         }
465
466
467 }