more work on js-rewrite
[enigma2-plugins.git] / webinterface / src / web-data / handler.js
1 var AbstractContentHandler = Class.create({
2         initialize: function(tpl, target, onFinished){
3                 this.tpl = tpl;
4                 this.target = target;
5                 this.onFinished = onFinished;
6                 if(this.onFinished === undefined || this.onFinished == null){
7                         this.onFinished = [];
8                 }
9                 this.eventsRegistered = false;
10                 this.provider = null;
11                 this.ajaxload = false;
12         },
13         
14         load: function(parms, fnc){
15                 this.requestStarted();
16                 this.provider.load(parms, fnc);
17         },      
18         
19         
20         /**
21          * requestStarted
22          * if this.ajaxload is true setAjaxLoad(this.target) will be called
23          **/
24         requestStarted: function(){
25                 if(this.ajaxload){
26                         setAjaxLoad(this.target);
27                 }
28         },
29         
30         /**
31          *requestFinished
32          * What to do when a request has finished. Does nothing in the Abstract class definition
33          **/
34         requestFinished: function(){
35 //              TODO requestFinished actions
36         },
37         
38         //TODO insert renderTpl, templateEngine.process & Co. here or somewhere else... (maybe a separate class?)
39         
40         /**
41          * show
42          * Show the data that has been fetched by a request (and prepared by renderXML)
43          * in this.target.
44          * Afterwards call this.finished()
45          */
46         show : function(data){
47                 templateEngine.process(this.tpl, data, this.target, this.finished.bind(this));          
48         },
49         
50         /**
51          * notify
52          * fade in to show text in the $('notification') area and fade out afterwards
53          * Parameters:
54          * @text - the text of the notification
55          * @state - false == error (bgcolor red), true == success (bgcolor green)
56          */
57         notify: function(text, state){
58                 notif = $('notification');
59
60                 if(notif !== null){
61                         //clear possibly existing hideNotifier timeout of a previous notfication
62                         clearTimeout(hideNotifierTimeout);
63                         if(state === false){
64                                 notif.style.background = "#C00";
65                         } else {
66                                 notif.style.background = "#85C247";
67                         }                               
68
69                         notif.update("<div>"+text+"</div>");
70                         notif.appear({duration : 0.5, to: 0.9 });
71                         hideNotifierTimeout = setTimeout(hideNotifier, 10000);
72                 }
73         },
74         
75         /**
76          * simpleResultCallback
77          * Callback for @ onSuccess of this.simpleResultQuery()
78          * if this.refresh == true this.reload is being called
79          * Parameters:
80          * @transport - the xmlhttp transport object
81          */
82         simpleResultCallback: function(transport){
83                 this.provider.simpleResultCallback(transport, this.showSimpleResult.bind(this));                
84         },
85         
86         showSimpleResult: function(result){
87                 this.notify(result.getStateText(), result.getState());          
88         },
89         
90         registerEvents : function(){
91                 debug('[AbstractContentHandler] WARNING: registerEvents not implemented in derived class!');
92         },
93         
94         /**
95          * finished
96          * Calls all functions this.onFinished contains this.registerEvents
97          * Is usually called after this.show() has finished
98          */
99         finished : function(){
100                 if(!this.eventsRegistered){
101                         try{
102                                 this.registerEvents();
103                         } catch (e){
104                                 debug(e);
105                         }
106                         this.eventsRegistered = true;
107                 }
108                 
109                 if(this.onFinished !== undefined){
110                         for(var i = 0; i < this.onFinished.length; i++){
111                                 var fnc = this.onFinished[i];
112                                 if(typeof(fnc) === 'function'){
113                                         fnc();
114                                 }
115                         }
116                 }
117         }
118 });
119
120 var BouquetListHandler = Class.create(AbstractContentHandler, {
121         initialize: function($super, target){
122                 $super('tplBouquetList', target);
123                 this.provider = new BouquetListProvider(this.show.bind(this));  
124                 this.ajaxload = false;
125                 this.serviceController = null;
126                 this.initServiceList = false;
127         },
128         
129         init: function(params, controller){
130                 this.serviceController = controller;
131                 this.initServiceList = true;
132                 this.load(params);
133         },
134         
135         show : function(data){
136                 if($(this.target) != null && $(this.target != undefined)){
137                         templateEngine.process(this.tpl, data, this.target,  this.finished.bind(this));
138                         if(this.initServiceList){
139                                 this.serviceController.load(decodeURIComponent(data.bouquets[0].servicereference));
140                                 this.initServiceList = false;
141                         }
142                 } else {
143                         templateEngine.process(                                 
144                                         'tplBouquetsAndServices', 
145                                         null, 
146                                         'contentMain',
147                                         function(){
148                                                 this.show(data);
149                                         }.bind(this)
150                         );
151                 }
152         },
153 });
154
155 var ServiceListHandler = Class.create(AbstractContentHandler, {
156         initialize: function($super, target){
157                 $super('tplServiceList', target, [this.getNowNext.bind(this),this.getSubservices.bind(this)]);
158
159                 this.provider = new ServiceListProvider(this.show.bind(this));
160                 this.epgHandler = new ServiceListEpgHandler();
161                 this.subServiceHandler = new ServiceListSubserviceHandler();
162                 
163                 this.ajaxload = true;
164         },
165                 
166         /**
167          * getNowNext
168          * calls this.epgHandler.getNowNext to show Now/Next epg information
169          * using this.parms.sRef as the servicereference of the bouquet 
170          */
171         getNowNext: function(){
172                 this.epgHandler.provider.getNowNext({bRef : this.provider.parms.sRef});
173         },
174         
175         /**
176          * getSubservices
177          * calls this.subServiceHandler.load() to show Now/Next epg information
178          */
179         getSubservices: function(){
180                 this.subServiceHandler.load({});
181         },
182         
183         /**
184          * call this to switch to a service
185          * Parameters:
186          * @servicereference - the (unescaped) reference to the service that should be shown
187          */
188         zap: function(ref){
189                 this.provider.simpleResultQuery(URL.zap, {sRef : ref}, this.simpleResultCallback.bind(this));
190         
191                 //TODO replace this
192                 setTimeout(updateItemsLazy, 7000); //reload epg and subservices
193                 setTimeout(updateItems, 3000);
194         },
195 });
196
197 var EpgListHandler = Class.create(AbstractContentHandler,{
198         initialize: function($super){
199                 $super('tplEpgList');
200                 this.provider = new ServiceEpgListProvider(this.show.bind(this));
201                 this.window = '';
202                 this.data = '';
203         },
204         
205         show : function(data){
206                 this.data = data;
207                 fetchTpl(this.tpl, this.showEpg.bind(this));            
208         },
209         
210         showEpg: function(){
211                 var html = templates[this.tpl].process(this.data);
212
213                 if (!this.window.closed && this.window.location) {
214                         setWindowContent(this.window, html);
215                 } else {
216                         this.window = openPopup("EPG", html, 900, 500);
217                 }
218         }
219 });
220
221 var ServiceListEpgHandler  = Class.create(AbstractContentHandler, {
222         initialize: function($super){
223                 $super('tplServiceListEPGItem');
224                 this.provider = new ServiceListEpgProvider(this.show.bind(this));
225         },
226         
227         /**
228          * show
229          * calls this.showItem for each item of @list
230          * @list - An array of EPGEvents
231          */     
232         show: function(list, type){
233                 for(var i = 0; i < list.length; i++){
234                         this.showItem(list[i], type);
235                 }
236                 
237                 this.finished();
238         },
239         
240         /**
241          * Shows an EPGEvent item in the DOM
242          * templates.tplServiceListEPGItem needs to be present!
243          * Parameters:
244          * @item - The EPGEvent object
245          */
246         //TODO: move showItem outta here
247         showItem: function(item, type){
248                 if(item.eventid != ''){
249                         var data = { epg : item, nownext: type };
250                         var id = type + item.servicereference;
251         
252                         templateEngine.process('tplServiceListEPGItem', data, id, true);
253
254                         var element = $('tr' + id);
255                         if(element !== null){
256                                 element.show();
257                         }
258                 }
259         }
260 });
261
262 var ServiceListSubserviceHandler  = Class.create(AbstractContentHandler, {
263         //constants
264         PREFIX : 'SUB',
265                 
266         initialize: function($super){
267                 $super('tplSubServices');
268                 this.provider = new ServiceListSubserviceProvider(this.show.bind(this));
269         },
270         
271         /**
272          * show
273          * Show all subervices of a service (if there are any)
274          * Overrides default show
275          */
276         show: function(list){
277                 var id = this.PREFIX + list[0].servicereference;
278                 var parent = $('tr' + id);
279                 
280                 if(parent !== null && list.length > 1){
281                         list.shift();
282                         
283                         var data = { subservices : list };
284                         templateEngine.process(this.tpl, data, id);                     
285                         parent.show();
286                 }
287         }
288 });
289
290 var MovieListHandler  = Class.create(AbstractContentHandler, {
291         initialize: function($super, target){
292                 $super('tplMovieList', target);
293                 this.provider = new MovieListProvider(this.show.bind(this));
294                 
295                 this.ajaxload = true;
296         },
297         
298         /**
299          * del
300          * Deletes a movie
301          * Parameters:
302          * @servicereference - the servicereference of the movie that should be deleted
303          * @servicename - the name of the service the movie was recorded from
304          * @title - the title of the movie
305          * @description - the description of the movie
306          */
307         del: function(servicereference, servicename, title, description){               
308                 var result = confirm( "Are you sure want to delete the Movie?\n" +
309                                 "Servicename: " + servicename + "\n" +
310                                 "Title: " + unescape(title) + "\n" + 
311                                 "Description: " + description + "\n");
312                 
313                 if(result){
314                         debug("[MovieListProvider.del] ok confirm panel"); 
315                         this.provider.simpleResultQuery(URL.moviedelete, {sRef : servicereference}, this.simpleResultCallback.bind(this));                      
316                 }
317                 else{
318                         debug("[MovieListProvider.del] cancel confirm panel");
319                         result = false;
320                 }
321                 
322                 this.refresh = result;
323                 return result;
324         }       
325 });
326
327 var TimerListHandler  = Class.create(AbstractContentHandler, {
328         initialize: function($super, target){
329                 $super('tplTimerList', target);
330                 this.provider = new TimerListProvider(this.show.bind(this));
331                 this.ajaxload = true;
332         }       
333 });
334
335 var TimerHandler = Class.create(AbstractContentHandler, {       
336         ACTIONS: [{value : 0, txt : 'Record'}, 
337                   {value : 1, txt : 'Zap'}],
338         
339         AFTEREVENTS: [{value : 0, txt : 'Nothing'}, 
340                       {value : 1, txt : 'Standby'}, 
341                       {value : 2, txt : 'Deepstandby/Shutdown'}, 
342                       {value : 3, txt : 'Auto'}],
343         
344         SELECTED : "selected",
345         CHECKED: "checked",
346         
347         /**
348          * initialize
349          * See the description in AbstractContentProvider
350          */
351         initialize: function($super, target){
352                 $super('tplTimerEdit', target);
353                 this.t = {};
354                 this.ajaxload = true;
355         },
356         
357         /**
358          * getData
359          * 
360          * Extracts the data of a timer from the .tListItem elements data-* attributes
361          * 
362          * <tr class="tListItem"                                                        
363          *      data-servicereference="${t.servicereference}"
364          *      data-servicename="${t.servicename}"
365          *      data-description="${t.description}"
366          *      data-title="${t.title}"
367          *      data-eventid="${t.eventid}"
368          *      data-begin="${t.begin}"
369          *      data-end="${t.end}"
370          *      data-repeated="${t.repeated}"
371          *      data-justplay="${t.justplay}"
372          *      data-dirname="${t.dirname}"
373          *      data-tags="${t.tags}"
374          *      data-afterevent="${t.afterevent}"
375          *      data-disabled="${t.disabled}"
376          * >
377          * 
378          * Parameters:
379          * @element - the html element calling the load function ( onclick="TimerProvider.load(this)" )
380          */
381         getData: function(tListItem){
382                 var parent = tListItem.up('.tListItem');
383                 
384                 var t = {
385                                 servicereference : parent.readAttribute('data-servicereference'),
386                                 servicename : parent.readAttribute('data-servicename'),
387                                 description : parent.readAttribute('data-description'),
388                                 title : parent.readAttribute('data-title'),
389                                 begin : parent.readAttribute('data-begin'),
390                                 end : parent.readAttribute('data-end'),
391                                 repeated : parent.readAttribute('data-repeated'),
392                                 justplay : parent.readAttribute('data-justplay'),
393                                 dirname : parent.readAttribute('data-dirname'),
394                                 tags : parent.readAttribute('data-tags'),
395                                 afterevent : parent.readAttribute('data-afterevent'),
396                                 disabled : parent.readAttribute('data-disabled')                                
397                 };
398                 
399                 return t;
400         },
401         
402         /**
403          * @override
404          * load
405          * When handling timers the whole loading-sequence is entirely different.
406          * Most of the data is already there or has to be created.
407          * 
408          * Parameters:
409          * @element - the html element calling the load function ( onclick="TimerProvider.load(this)" )
410          */
411         load : function(element){
412                 var t = this.getData(element);
413                         
414                 var begin = new Date(t.begin * 1000);
415                 var end = new Date(t.end * 1000);       
416                 
417                 var bHours = this.numericalOptionList(1, 24, begin.getHours());         
418                 var bMinutes = this.numericalOptionList(1, 60, begin.getMinutes());
419                 var eHours = this.numericalOptionList(1, 24, end.getHours());           
420                 var eMinutes = this.numericalOptionList(1, 60, end.getMinutes());
421                 
422                 var now = new Date();
423                 var years = this.numericalOptionList(now.getFullYear(), now.getFullYear() + 10, begin.getFullYear());
424                 var months = this.numericalOptionList(0, 11, begin.getMonth(), 1);
425                 var days = this.daysOptionList(begin);
426                 
427                 var actions = this.ACTIONS;
428                 actions[t.justplay].selected = this.SELECTED;
429                 
430                 var afterevents = this.AFTEREVENTS;
431                 afterevents[t.afterevent].selected = this.SELECTED;
432                 
433                 var repeated = this.repeatedDaysList(t.repeated);
434                 
435                 var data = { 
436                                 year : years,
437                                 month : months,
438                                 day : days,
439                                 shour : bHours,
440                                 smin : bMinutes,
441                                 ehour : eHours,
442                                 emin : eMinutes,
443                                 action : actions,
444                                 channel : [],
445                                 afterEvent : afterevents,
446                                 repeated : repeated,
447                                 dirname : [],
448                                 tags : [],
449                                 timer : t };
450                 
451                 this.show(data);
452         },
453         
454         toggleDisabled: function(element){
455                 //TODO implement toggleDisabled
456         },
457         
458         /**
459          * repeatedDaysList
460          * 
461          * Parameters:
462          * @num - the decimal value to apply as bitmask
463          * @return - a list of {id : dayid, value : dayvalue, txt : daytext, long : daylong}
464          **/
465         repeatedDaysList: function(num){
466                 var days = [{id : 'mo', value : 1, txt : 'Mo', long : 'Monday'}, 
467                             {id : 'tu', value : 2, txt : 'Tu', long : 'Tuesday'},
468                             {id : 'we', value : 4, txt : 'We', long : 'Wednesday'},
469                             {id : 'th', value : 8, txt : 'Th', long : 'Thursday'},
470                             {id : 'fr', value : 16, txt : 'Fr', long : 'Friday'},
471                             {id : 'sa', value : 32, txt : 'Sa', long : 'Saturday'},
472                             {id : 'su', value : 64, txt : 'Su', long : 'Sunday'},
473                             {id : 'mf', value : 31, txt : 'Mo-Fr', long : 'Monday to Friday'},
474                             {id : 'ms', value : 127, txt : 'Mo-Su', long : 'Monday to Sunday'}
475                             ];
476                 
477                 
478                 //check for special cases (Mo-Fr & Mo-Su)
479                 if(num == 31){
480                         days[7].checked = this.CHECKED;
481                 } else if (num == 127){
482                         days[8].checked == this.CHECKED;
483                 }
484
485                 // num is the decimal value of the bitmask for checked days
486                 for(var i = 0; i < days.length; i++){
487                         days[i].checked = "";
488                         
489                         //set checked when most right bit is 1
490                         if(num &1 == 1){
491                                 days[i].checked = this.CHECKED;
492                         }
493                         
494                         // shift one bit to the right
495                         num = num >> 1;
496                 }
497                 
498                 return days;
499         },
500         
501         /**
502          * numericalOptionList
503          * Create a List of numerical-based options
504          * Entry.value is being extended to at least 2 digits (9 => 09)
505          * 
506          * Parameters:
507          * @lowerBound - Number to start at
508          * @upperBound - Number to stop at
509          * @selectedValue - entry.selected is set to this.SELECTED if number == selectedValue ("" else)
510          **/
511         numericalOptionList: function(lowerBound, upperBound, selectedValue, offset){
512                 var list = [];
513                 var idx = 0;
514                 if(offset == undefined){
515                         offset = 0;
516                 }
517                 
518                 for(var i = lowerBound; i <= upperBound; i++){
519                         var t = i + offset;
520                         var txt = t < 10 ? "0" + t : t;
521                         var selected = "";
522                         if(i == selectedValue){
523                                 selected = this.SELECTED;
524                         }
525                         list[idx] = {value : i, txt : txt, selected : selected};
526                         idx++;
527                 }
528                 return list;
529         },
530         
531         /**
532          * daysOptionList
533          * 
534          * Determines how many Days a month has an builds an 
535          * numericalOptionsList for that number of Days
536          */
537         daysOptionList: function(date){         
538                 var days = 32 - new Date(date.getYear(), date.getMonth(), 32).getDate();
539                 return this.numericalOptionList(1, days, date.getDate());
540         },
541         
542         /**
543          * commitForm
544          * 
545          * Commit the Timer Form by serialing it and doing executing the request
546          * @id - id of the Form
547          */
548         commitForm : function(id){              
549                 var values = $(id).serialize();
550                 debug(values);
551         },
552         
553         /**
554          * renderXML
555          * See the description in AbstractContentProvider
556          */     
557         renderXML: function(xml){
558                 var list = new TimerList(xml).getArray();
559                 return {timer : list};
560         },
561         
562         registerEvents: function(){
563                 $('saveTimer').on('click', function(event, element){
564                                         this.commitForm('timerEditForm');
565                                 }.bind(this)
566                         );
567                 
568                 $('month').on('change', function(event, element){                       
569                                 this.reloadDays();
570                         }.bind(this)
571                 );
572                 
573                 $('year').on('change', function(event, element){                        
574                                 this.reloadDays();
575                         }.bind(this)
576                 );
577                 
578         },
579         
580         reloadDays : function(){
581                 var date = new Date($('year').value, $('month').value, $('day').value);
582                 var days = this.daysOptionList(date);
583                                                 
584                 $('day').update('');
585                 this.createOptions(days, $('day'));
586         },
587         
588         createOptions: function(items, element){                
589                 for(var i = 0; i < items.length; i++){
590                         var item = items[i];
591                         
592                         var attrs = { value : item.value };
593                         if(item.selected == this.SELECTED){
594                                 attrs = { value : item.value, selected : item.selected };
595                         }
596                         var option = new Element('option', attrs).update(item.txt);                             
597                         
598                         element.appendChild(option);
599                 }
600         }
601 });
602
603
604 //create required Instances
605 var serviceListHandler = new ServiceListHandler('contentServices');
606 var epgListHandler = new EpgListHandler();
607 var movieListHandler = new MovieListHandler('contentMain');
608 var timerListHandler = new TimerListHandler('contentMain');
609 var timerHandler = new TimerHandler('contentMain');