OSDN Git Service

dd0cb2c6e6eccd9b11ec25583685d5dd8a1219d4
[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 var $log;
54 var $autoscroll;
55 $(document).ready(()=> {
56         $log = $("#console .terminal");
57         $autoscroll = $("#console input");
58 });
59 var log = {
60         _parse(str, color) {
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;
65         },
66         normal (str) {
67                 $log.html(this._parse(str,"white"));
68                 if ($autoscroll.prop('checked'))
69                         $log[0].scrollTop = $log[0].scrollHeight;
70         },
71         error (str) {
72                 $log.html(this._parse(str,"red"));
73                 if ($autoscroll.prop('checked'))
74                         $log[0].scrollTop = $log[0].scrollHeight;
75         },
76         warn (str) {
77                 $log.html(this._parse(str,"yellow"));
78                 if ($autoscroll.prop('checked'))
79                         $log[0].scrollTop = $log[0].scrollHeight;
80         },
81         debug (str) {
82                 $log.html(this._parse(str,"lightblue"));
83                 if ($autoscroll.prop('checked'))
84                         $log[0].scrollTop = $log[0].scrollHeight;
85         }
86 }
87
88 /**
89  * Scale an image up or down until it's larger than or equal to the viewport
90  * and then center it.
91  */
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;
99
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);
111         } else {
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);
116         }
117         this.centerImage($img);
118 }
119
120 var centerImage =  function($img) {
121         var overlapWidth = $img.width() - screen.width;
122         var overlapHeight = $img.height() - screen.height;
123
124
125         // image overlaps viewport, move the image back
126         // half the length of the overlap
127         $img.css({
128                 position: "relative",
129                 right: overlapWidth/2,
130                 bottom: overlapHeight/2
131         });
132 }
133
134 class LoginManager {
135         constructor() {
136                 this.use_splash = true;
137                 $(document).ready(() => {
138                         this.init();
139                 });
140         }
141
142         init() {
143                                 this.lightdm = typeof (lightdm) == "undefined" ? {} : lightdm;
144
145                 if (this.use_splash) {
146                         this.splash = new SplashScreen();
147                 }
148                 $(this).trigger("ready");
149         }
150
151
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.");
159
160                         // call async so that events can be binded in cascade
161                         setTimeout(() => $(this).trigger("deny"));
162                         return;
163                 }
164
165                 username = username || this.lightdm.select_user;
166                 password = password || "";
167                 //  session_key = session_key || lightdm.sessions[0].key;
168
169                 let auth_cb = () =>  {
170                     this.lightdm.respond(password);
171     }
172                 let auth_complete_cb = () => {
173                         if (typeof callback == "function")
174                                 callback(this.lightdm.is_authenticated);
175
176                         $(this).trigger(this.lightdm.is_authenticated ? "grant" : "deny");
177                 }
178
179           window.show_prompt = auth_cb;
180                 window.authentication_complete = auth_complete_cb;
181                 this.lightdm.authenticate(username);
182   }
183
184         login(session_key) {
185                 if (!this.lightdm.sessions.find(x => x.key == session_key)) {
186                         log.error("Attempting to login without a valid session.");
187                         return;
188                 }
189
190                 if (!this.lightdm.is_authenticated) {
191                         log.error("Attempting to login without authentication.");
192                         return;
193                 }
194                 this.lightdm.start_session_sync(session_key);
195         }
196         authenticateWithSelected($user_input, $password_input) {
197
198         }
199
200         fillUserSelect($el) {
201                         if (!Array.isArray(this.lightdm.users)) {
202                                         log.warn("Cannot fill empty user list in lightdm.");
203                                         return;
204                         }
205
206                         $el.empty();
207                         for (let s of this.lightdm.users)
208                                         $el.append("<option value=" + s.username + ">" + s.display_name + "</option>");
209                         $el.formSelect();
210         }
211
212         fillSessionSelect($el) {
213                 if (!Array.isArray(this.lightdm.sessions)) {
214                                 log.warn("Cannot fill empty session list in lightdm.");
215                                 return;
216                 }
217
218                 $el.empty();
219
220                 for (let s of this.lightdm.sessions)
221                                 $el.append("<option value=" + s.key + ">" + s.name + "</option>");
222
223
224                 $el.formSelect();
225         }
226
227         get users() {
228                 return this.lightdm.users;
229         }
230
231         get sessions() {
232                 return this.lightdm.sessions;
233         }
234 }
235
236 class SplashScreen {
237         constructor() {
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;
244
245                 if (!this.$el.length)
246                         log.error("Missing-screen element.");
247
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)));
253
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;
258
259
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);
269                 }
270
271                 /******************** Event Listeners ********************/
272                 this.clock = setInterval(() => {
273                         $(this).trigger("tick");
274                                 log.error("tick");
275                         if (!this.isActive())
276                                 $(this).trigger("inactive");
277                 }, 500);
278
279                 // update last active time
280                 $(this).on("active", () => this.last_active = moment());
281
282                 $(document).keyup((e) => {
283                         // handle events in seperate method
284                         this.keyHandler.call(this, e);
285                 }).keypress((e) => this.keyHandler.call(this, e));
286
287                 this.$el.click(() => {
288                         this.open();
289                 }).mousemove((e) => {
290                         if (!this.isActive())
291                                 $(this).trigger("active", e)
292                 });
293                 setTimeout(() => $(this).trigger("active"), 1);
294         }
295
296         /**
297          * Loops through the user specified content and adds them to the DOM in order
298          */
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]);
305                         else
306                                 log.warn("Specified content " + content_type + " is not valid.");
307                 }
308         }
309
310         getUserOptions() {
311                 let options = {};
312                 $.extend(true, options, DEF_OPT);
313                 $.extend(true, options, {});
314                 return options;
315         }
316
317         /**
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
320          * inactivity */
321         close(time=450)  {
322                 if (!this.is_open)
323                         return
324                 this.$el.animate({
325                         top: "0"
326                 }, time, "easeInCubic", () => {
327                         this.is_open = false
328                         clearTimeout(this.resetTimeout);
329                 });
330         }
331         open(time=400) {
332                 clearTimeout(this.resetTimeout);
333                 let reset_duration = 60*1000;
334
335
336                 if (this.is_open) {
337                         this.resetTimeout = setTimeout(this.reset, reset_duration);
338                         return;
339                 }
340                 this.$el.animate({
341                         top: "-100%"
342                 }, time, "easeInCubic", () => {
343                         this.is_open = true;
344                         // close the screen after 1 minute of inactivty
345                         this.resetTimeout = setTimeout(() => this.reset, reset_duration);
346                 });
347         }
348         reset() {
349                 if (this.is_open == true) {
350
351                         this.close();
352                         $(this).trigger("timeout");
353                 }
354         }
355
356         /**
357          * handles the key events for the splash
358          */
359         keyHandler(e) {
360                 switch (e.keyCode) {
361                         case 32:
362                         case 13:
363                                 this.open();
364                                 break;
365                         case 27:
366                                 if (this.is_open) this.close();
367                                 else this.open();
368                                 break;
369                         default:
370                                 if (e.keyCode != 82 && e.keyCode != 17) // for testing
371                                 this.open();
372                                 break;
373                 }
374
375                 // stop reset timeout since there has been user activity
376                 if (this.is_open)
377                         clearTimeout(this.resetTimeout);
378
379                 if (!this.isActive())
380                         $(this).trigger("active", e);
381         }
382
383         isActive() {
384                 if (moment().diff(this.last_active, "seconds", true) > 30) {
385                         return 0;
386                 }
387                 return 1;
388         }
389
390         /**
391          *  Creates clock elements based on the usr config
392          */
393         initClock(opts) {
394                 if (typeof opts != "object") {
395                         log.error("Unable to initialize clock thats not an object");
396                         return -1;
397                 }
398                 // handle arrays and a single clock object
399                 if (!Array.isArray(opts))
400                         opts = [opts];
401
402                 for (let i in opts) {
403                         this.$clock = $("<div id='clock-" + i + "' class='clock'></div>");
404                         this.$content.append(this.$clock);
405                         this.startClock(this.$clock, opts[i]);
406                 }
407         }
408
409         /**
410          * Applys the css specfied in the argument opts to the jQuery oboject $clock.
411          * Subscribes the clock to a tick event
412          */
413         startClock($clock, opts) {
414                 if (typeof opts != "object") {
415                         log.error("Clock opts is not a valid object");
416                         return -1;
417                 }
418                 // handle multiple formats for multiple clocks on the same line
419                 if(typeof opts.format == "string")
420                         opts.format = [opts.format];
421
422                 // ensure the format is now an array
423                 if(!Array.isArray(opts.format)) {
424                         log.error(`Specfied clock format is not a valid type.
425                                 Type can be a single string or Array.`);
426                         return -1;
427                 }
428
429                 if(!Array.isArray(opts.css))
430                         opts.css = [opts.css];
431
432                 for (let i in opts.format) {
433
434                         let $format = $("<sub></sub>");
435                         // create text field in clock
436                         $clock.append($format);
437                         // apply css styles
438                         if (i < opts.css.length && typeof opts.css[i] == "object")
439                                 $format.css(opts.css[i]);
440
441                         // start clock
442                         $format.text(moment().format(opts.format[i]));
443                         $(this).on("tick", () => {
444                                 $format.text(moment().format(opts.format[i]));
445                         });
446                 }
447
448                 if (typeof opts["parent-css"] == "object")
449                         $clock.css(opts["parent-css"]);
450                 log.debug($clock);
451                 $clock.show();
452         }
453
454         /**
455          * Inserts HTML specified in the user config into the splash screen
456          * accepts plain strings and objects. String literals are interpreted as
457          * normal text element. Objects are set using the jQuery API
458          */
459         initHTML(opts) {
460                 // handle single objects and strings
461                 if (!Array.isArray(opts)) {
462                         opts = [opts];
463                 }
464
465                 for (let el of opts) {
466                         if (typeof el == "string") {
467                                 let $el = $("<text>");
468                                 $el.text(el);
469                                 // create simple text element
470                                 this.$content.append($el);
471                         } else if (typeof el == "object") {
472                                 // let user specify element properites in object el.
473                                 let $el = $("<div>");
474                                 for (let prop in el) {
475                                         $el[prop](el[prop]);
476                                 }
477                                 this.$content.append($el);
478
479                         } else {
480                                 log.warn("Splash screen html element is invalid type");
481                         }
482                 }
483
484         }
485
486
487 }