OSDN Git Service

dcaa65a9d6ec858f2a2e60f2bc37b90a5e65154a
[alterlinux/lightdm-webkit2-theme-alter.git] / js / splashscreen.js
1
2 /**
3 * Creates a splash screen with custom generated content,
4 * diplays when the user inactive.
5 *
6 * A Plugin for the LoginManager class
7 *
8 * EVENTS:
9 * -----------------------------------------------------------------------------
10 * Listens:
11 * 'load' signifies that SplashScreen has finished asyncrhonously
12 * loading its config files.
13 *
14 * 'ready' emitted once the SplashScreen has finished creating its content
15 *
16 * Triggers:
17 * 'init' causes the SplashScreen content to be generated
18 *
19 */
20 text=[]
21 if (window.navigator.language === "en" || window.navigator.language === "en-US" ||window.navigator.language === "fr"  ||window.navigator.language === "es-ES" ) {
22   text[0]="Press any key to login"
23   text[1]="Welcome Back"
24   text[2]="Select Session"
25   text[3]="User"
26   text[4]="Password"
27 }
28 else {
29   text[0]="任意のキーを押すとログインできます。"
30   text[1]="ようこそ"
31   text[2]="セレクトセッション"
32   text[3]="ユーザー"
33   text[4]="パスワード"
34 }
35 class SplashScreen {
36   constructor() {
37     /* Speed of SplashScreen transitions */
38     this._ANIMATION_DUR = 300;
39
40     /* Default options for the  splash screen */
41     this._DEF_OPT = {
42       "fit": true,
43       "filter": false,
44       "vignette": true,
45       "active-timeout": 15,
46       "transition": "fade",
47       "img": false,
48       "content": {
49         "clock": [{
50         //   "format": "dddd, MMMM Do",
51         //   "css": {
52         //     "color": "white"
53         //   },
54         //   "parent-css": {
55         //     "margin-top": "calc(20vh - 70pt)",
56         //     "text-align": "center",
57         //     "font-size": "70pt",
58         //     "font-family": "Noto Sans",
59         //     "font-weight": "lighter",
60         //     "text-shadow": "rgba(0, 0, 0, 0.25) 0px 3px 3px",
61         //   }
62         // },{
63           "format": ["h:mm", "A"],
64           "css": [
65             {"font-size": "65pt", "font-weight": 200 },
66             {"font-size": "30pt", "font-weight": "lighter", "margin-left": "10pt"}
67           ],
68           "parent-css": {
69             "margin-top": "40vh",
70             "color": "white",
71             "font-family": "MyFont",
72             "text-align": "center",
73             "text-shadow": "rgba(0, 0, 0, 0.25) 0px 3px 3px",
74           }
75         }],
76
77         "html": [{
78           "html":"<text style='display: none' class='active-appear'>"+text[0]+"</text>",
79           "css": {
80             "margin-top": "5vh",
81             "font-weight": "200",
82             "font-size": "23pt",
83             "text-align": "center",
84             "color": "rgba(255, 255, 255, 0.8)"
85           }
86         }]
87       }
88     };
89     this.template = `<!-- Autogenerated SplashScreen -->
90     <div id="splash-screen">
91     <div class="vignette"></div>
92     <div id="splash-screen-content"></div>
93     </div>
94     <!-- End Autogenerated SplashScreen -->`;
95     this.$el = $(this.template);
96     $("body").prepend(this.$el);
97
98     this._loadConfig();
99     // listen to the init event
100     $(this).on("init", () => this._init());
101   }
102
103   /**
104   * Generates the content specified by the user config.
105   * Should be called after the load event has been triggered.
106   */
107   _init() {
108     this.$content = $("#splash-screen-content");
109     this._state = "closed";
110     this._last_active = 0;
111     this._active_timeout = 15;
112
113     let options = this._options; // shorthand
114     if (typeof options == "object") {
115
116       // initilize global values if specfied in the config
117       if (typeof options.img == "string") {
118         this.$img = $(`<img class="splash-screen-img" src="${options.img}">`);
119
120         this.$el.prepend(this.$img);
121         this.$img.one("load", () => {
122           // fit background image to sreen size and center
123           adjustBackground($(".splash-screen-img"))
124         })
125       }
126
127       if (typeof options["active-timeout"] == "number")
128       this._active_timeout = options["active-timeout"];
129
130       if (options.filter == true)
131       this.$img.addClass("filter");
132
133       if (options.vignette == true) {
134         this.$vignette = $("#vignette");
135         this.$vignette.show();
136       }
137
138       if (typeof options.transition == "string")
139       this.transition = options.transition;
140
141       if (typeof options.content == "object")
142       this._initContent(options.content);
143
144       $(this).trigger("ready");
145     }
146
147     /******************** Event Listeners ********************/
148     this.clock = setInterval(() => {
149       $(this).trigger("tick");
150
151       if (!this._isActive())
152       $(this).trigger("inactive");
153     }, 500);
154
155     // update last active time
156     $(this).on("active", () => this._last_active = moment());
157
158     $(document).keyup((e) => {
159       // handle events in seperate method
160       this._keyHandler.call(this, e);
161     }).keypress((e) => this._keyHandler.call(this, e));
162
163     /* Bind event listners to trigger activity event. This can be used on the
164     front end to implement spcific behaivours while the user is active */
165     this.$el.click(() => {
166       this._open();
167     }).mousemove((e) => {
168       if (!this._isActive())
169       $(this).trigger("active", e)
170     });
171     setTimeout(() => $(this).trigger("active"), 1);
172   }
173
174   /**
175   * Loops through the user specified content and appends them to the DOM
176   * in the order specified by the user config
177   */
178   _initContent(content) {
179     for (let content_type in content) {
180       if (content_type == "clock")
181       this._initClock(content[content_type]);
182       else if (content_type == "html")
183       this._initHTML(content[content_type]);
184       else
185       log.warn("Specified content " + content_type + " is not valid.");
186     }
187   }
188
189   /**
190   * Asyncrhonously reads JSON config file from json/SplashScreen.json
191   * and overwrites the default options with those specified by the config.
192   *
193   * Triggers: 'load' on completion. Caller (LoginManager) must listen for this
194   *     event to then trigger 'init'
195   */
196   _loadConfig() {
197     let options = {};
198     $.extend(true, options, this._DEF_OPT);
199
200     $.getJSON("json/SplashScreen.json", (data) => {
201       $.extend(true, options, data);
202       this._options = options;
203       $(this).trigger("load");
204     }).fail(() => {
205       $.extend(true, options, {});
206       this._options = options;
207       $(this).trigger("load");
208     });
209   }
210
211   /**
212   * Closes the splash screen if there has been no user activity
213   */
214   _reset() {
215     if (this._state == "open") {
216       this._close();
217       $(this).trigger("timeout");
218     }
219   }
220
221   /**
222   * Determines if there was user acitivty within in a given amount
223   * of time.
224   * Returns 1 if splash screen is active, else 0
225   */
226   _isActive() {
227     if (moment().diff(this._last_active, "seconds", true) > 30) {
228       return 0;
229     }
230     return 1;
231   }
232
233   /**
234   * Creates clock elements based on the usr config.
235   * Appends each clock to the DOM and binds update events using _startClock
236   */
237   _initClock(opts) {
238     if (typeof opts != "object") {
239       log.error("Unable to initialize clock thats not an object");
240       return -1;
241     }
242     // handle arrays and a single clock object
243     if (!Array.isArray(opts))
244     opts = [opts];
245
246     /* loop through each clock in the config and add it to the dom,
247     then initialize an update event using start clock */
248     for (let i in opts) {
249       this.$clock = $("<div id='clock-" + i + "' class='clock'></div>");
250       this.$content.append(this.$clock);
251       this._startClock(this.$clock, opts[i]);
252     }
253   }
254
255   /**
256   * Applys the css specfied in the argument opts to the jQuery oboject $clock.
257   * Subscribes the clock to a tick event
258   */
259   _startClock($clock, opts) {
260     if (typeof opts != "object") {
261       log.error("Clock opts is not a valid object");
262       return -1;
263     }
264     // handle multiple formats for multiple clocks on the same line
265     if(typeof opts.format == "string")
266     opts.format = [opts.format];
267
268     // ensure the format is now an array
269     if(!Array.isArray(opts.format)) {
270       log.error(`Specfied clock format is not a valid type.
271         Type can be a single string or Array.`);
272         return -1;
273       }
274
275       if(!Array.isArray(opts.css))
276       opts.css = [opts.css];
277
278       for (let i in opts.format) {
279
280         let $format = $("<sub></sub>");
281         // create text field in clock
282         $clock.append($format);
283         // apply css styles
284         if (i < opts.css.length && typeof opts.css[i] == "object")
285         $format.css(opts.css[i]);
286
287         // start clock
288         $format.text(moment().format(opts.format[i]));
289         $(this).on("tick", () => {
290           $format.text(moment().format(opts.format[i]));
291         });
292       }
293
294       if (typeof opts["parent-css"] == "object")
295       $clock.css(opts["parent-css"]);
296
297       $clock.show();
298     }
299
300     /**
301     * Inserts HTML specified in the user config into the splash screen
302     * accepts plain strings and objects. String literals are interpreted as
303     * normal text element. Objects are set using the jQuery API
304     */
305     _initHTML(opts) {
306       // handle single objects and strings
307       if (!Array.isArray(opts)) {
308         opts = [opts];
309       }
310
311       for (let el of opts) {
312         if (typeof el == "string") {
313           let $el = $("<text>");
314           $el.text(el);
315           // create simple text element
316           this.$content.append($el);
317         } else if (typeof el == "object") {
318           // let user specify element properites in object el.
319           let $el = $("<div>");
320           for (let prop in el) {
321             $el[prop](el[prop]);
322           }
323           this.$content.append($el);
324
325         } else {
326           log.warn("Splash screen html element is invalid type");
327         }
328       }
329
330     }
331
332     /**
333     * Handles the key events for the SplachScreen and active-inactive events
334     */
335     _keyHandler(e) {
336       switch (e.keyCode) {
337         case 32:
338         case 13: // Enter key
339         if (this._state == "closed")
340         this._open();
341         break;
342         case 27: // ESC key
343         if (this._state == "open")
344         this._close();
345         else if (this._state == "closed")
346         this._open();
347         break;
348         default:
349         if (this._state == "closed")
350         this._open();
351         break;
352       }
353
354       // stop reset timeout since there has been user activity
355       if (this._state == "open")
356       clearTimeout(this.resetTimeout);
357
358       // trigger active event if the user has been inactive long enough
359       if (!this._isActive())
360       $(this).trigger("active", e);
361     }
362
363     /**
364     * _open and _close will toggle the screen and animate it opening and closing
365     * adds a resetTimeout function to automatically close after a period of user
366     * inactivity
367     *
368     * Uses a _state machine consisting of {'open', 'closed', 'moving'}.
369     * Transitions are not possible while the _state == moving. This prevents
370     * fillUserSelect from trigger concurrernt transitions which would lead to
371     * undefined behaivour.
372     */
373     _close()  {
374       if (this._state != "open") {
375         log.warn("Cannot close splash screen when _state is: " + this._state);
376         return;
377       }
378
379       this._state = "moving";
380       if (this.transition == "fade") {
381         this.$el.fadeIn("slow", () => {
382           this._state = "closed";
383           this.$content.fadeIn("slow");
384           clearTimeout(this.resetTimeout);
385         });
386       } else if (this.transition == "slide") {
387         this.$el.animate({
388           top: "0"
389         },"slow", "easeOutQuint", () => {
390           this._state = "closed";
391           clearTimeout(this.resetTimeout);
392         });
393       }
394
395
396     }
397     _open() {
398       if (this._state != "closed") {
399         log.warn("Cannot open splash screen when _state is: " + this._state);
400         return;
401       }
402       clearTimeout(this.resetTimeout);
403       let reset_duration = 60*1000;
404
405       if (this._state == "open" || this._state == "moving") {
406         this.resetTimeout = setTimeout(this.reset, reset_duration);
407         return;
408       }
409       this._state = "moving";
410
411       if (this.transition == "fade") {
412         this.$content.fadeOut("fast", () => {
413           this.$el.fadeOut(this._ANIMATION_DUR, () => {
414             this._state = "open";
415           });
416         });
417
418       } else if (this.transition == "slide") {
419         this.$el.animate({
420           top: "-100%"
421         }, "slow", "easeInCubic", () => {
422           this._state = "open";
423         });
424       }
425
426
427     }
428
429   }