Fix movielist escape handling for servicereference (fixes movies with umlauts). Add...
[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                 this.data = {};
13         },
14
15         load: function(parms, fnc){
16                 this.requestStarted();
17                 this.parms = parms;
18                 this.provider.load(parms, fnc);
19         },
20
21         reload: function(){
22                 this.requestStarted();
23                 this.provider.reload();
24         },
25
26         /**
27          * requestStarted
28          * if this.ajaxload is true setAjaxLoad(this.target) will be called
29          **/
30         requestStarted: function(){
31                 if(this.ajaxload){
32                         core.setAjaxLoad(this.target);
33                 }
34         },
35
36         /**
37          *requestFinished
38          * What to do when a request has finished. Does nothing in the Abstract class definition
39          **/
40         requestFinished: function(){
41 //              TODO requestFinished actions
42         },
43
44         /**
45          * show
46          * Show the data that has been fetched by a request (and prepared by renderXML)
47          * in this.target.
48          * Afterwards call this.finished()
49          */
50         show: function(data){
51                 this.data = data;
52                 templateEngine.process(this.tpl, data, this.target, this.finished.bind(this));
53         },
54
55         /**
56          * notify
57          * fade in to show text in the $('notification') area and fade out afterwards
58          * Parameters:
59          * @text - the text of the notification
60          * @state - false == error (bgcolor red), true == success (bgcolor green)
61          */
62         notify: function(text, state){
63                 core.notify(text, state);
64         },
65
66         /**
67          * simpleResultCallback
68          * Callback for @ onSuccess of this.simpleResultQuery()
69          * Parameters:
70          * @transport - the xmlhttp transport object
71          */
72         simpleResultCallback: function(transport){
73                 this.provider.simpleResultCallback(transport, this.showSimpleResult.bind(this));
74         },
75
76         showSimpleResult: function(result){
77                 this.notify(result.getStateText(), result.getState());
78         },
79
80         registerEvents : function(){},
81
82         /**
83          * finished
84          * Calls all functions this.onFinished contains this.registerEvents
85          * Is usually called after this.show() has finished
86          */
87         finished : function(){
88                 if(!this.eventsRegistered){
89                         try{
90                                 this.registerEvents();
91                         } catch (e){
92                                 debug(e);
93                         }
94                         this.eventsRegistered = true;
95                 }
96
97                 if(this.onFinished !== undefined){
98                         for(var i = 0; i < this.onFinished.length; i++){
99                                 var fnc = this.onFinished[i];
100                                 if(typeof(fnc) === 'function'){
101                                         fnc();
102                                 }
103                         }
104                 }
105         }
106 });
107
108 var DeviceInfoHandler = Class.create(AbstractContentHandler,{
109         initialize: function($super, target, cached){
110                 $super('tplDeviceInfo', target);
111                 this.provider = new DeviceInfoProvider(this.show.bind(this));
112                 this.isCached = true;
113                 if(cached === false)
114                         this.isCached = false;
115                 this.data = null;
116         },
117
118         load: function($super, parms, callback){
119                 if(this.data == null)
120                         $super(parms, callback);
121                 else
122                         this.show(this.data);
123         },
124
125         get: function(parms, callback){
126                 this.requestStarted();
127                 if(this.data == null){
128                         this.provider.load(parms,
129                                         function(transport){
130                                                 var data = this.provider.renderXML(this.provider.getXML(transport));
131                                                 this.data = data;
132                                                 callback(data);
133                                         }.bind(this));
134                 } else {
135                         callback(this.data);
136                 }
137         }
138 });
139
140 var ExternalsHandler = Class.create(AbstractContentHandler, {
141         initialize: function($super, target){
142                 $super('tplNavExtrasExternals', target);
143                 this.provider = new ExternalsProvider(this.show.bind(this));
144                 this.ajaxload = false;
145         }
146 });
147
148 var SimplePageHandler = Class.create(AbstractContentHandler,{
149         initialize: function($super, target){
150                 $super(null, target);
151         },
152
153         show: function(tpl, data){
154                 templateEngine.process(tpl, data, this.target, this.finished.bind(this));
155         }
156 });
157
158 var BouquetListHandler = Class.create(AbstractContentHandler, {
159         initialize: function($super, target, targetMain){
160                 $super('tplBouquetList', target);
161                 this.provider = new SimpleServiceListProvider(this.show.bind(this));
162                 this.ajaxload = false;
163                 this.targetMain = targetMain;
164         },
165
166         show : function(data){
167                 this.data = data;
168                 if($(this.target)){
169                         templateEngine.process(this.tpl, data, this.target, this.finished.bind(this));
170                 } else {
171                         templateEngine.process(
172                                         'tplBouquetsAndServices',
173                                         null,
174                                         this.targetMain,
175                                         function(){
176                                                 this.show(data);
177                                         }.bind(this)
178                         );
179                 }
180         }
181 });
182
183 var CurrentHandler  = Class.create(AbstractContentHandler, {
184         initialize: function($super, curTarget, volTarget){
185                 $super('tplCurrent', curTarget);
186                 this.provider = new CurrentProvider(this.show.bind(this));
187                 this.volTpl = 'tplVolume';
188                 this.volTarget = volTarget;
189         },
190
191         show : function(data){
192                 this.data = data;
193                 templateEngine.process(this.volTpl, data, this.volTarget);
194                 templateEngine.process(this.tpl, data, this.target, this.finished.bind(this));
195         }
196 });
197
198 var ServiceListHandler = Class.create(AbstractContentHandler, {
199         initialize: function($super, target){
200                 $super('tplServiceList', target, [this.getSubservices.bind(this)]);
201
202                 this.provider = new ServiceListProvider(this.show.bind(this));
203                 this.epgHandler = new ServiceListEpgHandler();
204                 this.subServiceHandler = new ServiceListSubserviceHandler();
205
206                 this.ajaxload = true;
207         },
208
209         /**
210          * getNowNext
211          * calls this.epgHandler.getNowNext to show Now/Next epg information
212          * using this.parms.sRef as the servicereference of the bouquet
213          */
214         getNowNext: function(){
215                 this.epgHandler.load({bRef : this.provider.parms.bRef});
216         },
217
218         /**
219          * getSubservices
220          * calls this.subServiceHandler.load() to show Now/Next epg information
221          */
222         getSubservices: function(){
223                 this.subServiceHandler.load({});
224         },
225
226         /**
227          * call this to switch to a service
228          * Parameters:
229          * @servicereference - the (unescaped) reference to the service that should be shown
230          */
231         zap: function(parms, callback){
232                 this.provider.simpleResultQuery(
233                                 URL.zap,
234                                 parms,
235                                 function(transport){
236                                         this.simpleResultCallback.bind(this);
237                                         if(callback)
238                                                 callback();
239                                 }.bind(this));
240         },
241
242         showSimpleResult: function($super, result){
243                 if(result.getState()){
244                         core.updateItemsLazy();
245                 }
246                 $super(result);
247         }
248 });
249
250 var EpgListHandler = Class.create(AbstractContentHandler,{
251         initialize: function($super, showEpgFnc){
252                 $super('tplEpgList');
253                 this.provider = new ServiceEpgListProvider(this.show.bind(this));
254                 this.showEpg = showEpgFnc;
255                 this.data = '';
256         },
257
258         search : function(parms, fnc){
259                 this.requestStarted();
260                 this.provider.search(parms, fnc);
261         },
262
263         show : function(data){
264                 this.data = data;
265                 templateEngine.fetch(
266                                 this.tpl,
267                                 function(){
268                                         var html = templateEngine.templates[this.tpl].process(this.data);
269                                         this.showEpg(html);
270                                 }.bind(this)
271                         );
272         }
273 });
274
275 var ServiceListEpgHandler  = Class.create(AbstractContentHandler, {
276         EPG_NOW : 'NOW',
277         EPG_NEXT : 'NEXT',
278         PROGRESS : 'PROGRESS',
279
280         initialize: function($super){
281                 $super('tplServiceListEPGItem');
282                 this.provider = new ServiceListProvider(this.show.bind(this));
283         },
284
285         /**
286          * show
287          * calls this.showItem for each item of @list
288          * @list - An array of EPGEvents
289          */
290         show: function(list){
291                 var len = list.items.length;
292                 for(var i = 0; i < len; i++){
293                         this.updateEpg(list.items[i]);
294                 }
295                 this.finished();
296         },
297
298         /**
299          * Shows an EPGEvent item in the DOM
300          * templates.tplServiceListEPGItem needs to be present!
301          * Parameters:
302          * @item - The EPGEvent object
303          */
304         //TODO: move showItem outta here
305         updateEpg: function(item){
306                 if(item.now.eventid != ''){
307                         var progress = $(this.PROGRESS + item.now.servicereference);
308                         if(progress){
309                                 progress.down('.sListSProgress').title = item.now.progress + "%";
310                                 progress.down('.sListSProgressBar').style.width = item.now.progress + "%";
311                         }
312                 }
313                 this.showItem(this.EPG_NOW, item.now,'.sListEPGNow');
314                 this.showItem(this.EPG_NEXT, item.next,'.sListEPGNext');
315         },
316
317         showItem: function(type, epgItem, parent){
318                 var id = type + epgItem.servicereference;
319                 var epgElement = $(id);
320                 if(epgElement){ //Markers don't have any EPG
321                         var isVisible = false;
322                         var target = epgElement.down('.sListExtEpgLong');
323                         if(target){
324                                 isVisible = target.visible();
325                         }
326
327                         templateEngine.process('tplServiceListEPGItem', {'item' : epgItem, 'isVisible' : isVisible}, id, true);
328                         var element = $(id).up(parent);
329                         if(element){
330                                 element.show();
331                         }
332                 }
333         }
334 });
335
336 var ServiceListSubserviceHandler  = Class.create(AbstractContentHandler, {
337         //constants
338         PREFIX : 'SUB',
339
340         initialize: function($super){
341                 $super('tplSubServices');
342                 this.provider = new ServiceListSubserviceProvider(this.show.bind(this));
343         },
344
345         /**
346          * show
347          * Show all subervices of a service (if there are any)
348          * Overrides default show
349          */
350         show: function(list){
351                 var id = this.PREFIX + list[0].servicereference;
352                 var parent = $('tr' + id);
353
354                 if(parent !== null && list.length > 1){
355                         list.shift();
356
357                         var data = { subservices : list };
358                         templateEngine.process(this.tpl, data, id);
359                         parent.show();
360                 }
361         }
362 });
363
364 var SignalHandler = Class.create(AbstractContentHandler,{
365         initialize: function($super, showSignalFnc){
366                 $super('tplSignalPanel');
367                 this.provider = new SignalProvider(this.show.bind(this));
368                 this.showSignal = showSignalFnc;
369         },
370
371         show : function(data){
372                 this.data = data;
373                 templateEngine.fetch(
374                                 this.tpl,
375                                 function(){
376                                         var html = templateEngine.templates[this.tpl].process(this.data);
377                                         this.showSignal(html);
378                                 }.bind(this)
379                         );
380         }
381 });
382
383 var MediaPlayerHandler = Class.create(AbstractContentHandler, {
384         initialize: function($super, target){
385                 $super('tplMediaPlayer', target);
386                 this.provider = new MediaPlayerProvider(this.show.bind(this));
387         },
388
389         command: function(command){
390                 this.provider.simpleResultQuery(
391                                 URL.mediaplayercmd,
392                                 {'command' : command},
393                                 this.simpleResultCallback.bind(this)
394                         );
395         },
396
397         playFile: function(file){
398                 this.provider.simpleResultQuery(
399                                 URL.mediaplayerplay,
400                                 {'file' : file},
401                                 this.simpleResultCallback.bind(this)
402                         );
403         },
404
405         addFile: function(file){
406                 this.provider.simpleResultQuery(
407                                 URL.mediaplayeradd,
408                                 {'file' : file},
409                                 this.simpleResultCallback.bind(this)
410                         );
411         },
412
413         removeFile: function(file){
414                 this.provider.simpleResultQuery(
415                                 URL.mediaplayerremove,
416                                 {'file' : file},
417                                 function(data){
418                                         this.simpleResultCallback(data);
419                                         this.reload();
420                                 }.bind(this)
421                         );
422         },
423
424         savePlaylist: function(filename){
425                 this.provider.simpleResultQuery(
426                                 URL.mediaplayerwrite,
427                                 {'filename' : filename},
428                                 this.simpleResultCallback.bind(this)
429                         );
430         }
431
432 });
433
434 var MovieListHandler  = Class.create(AbstractContentHandler, {
435         initialize: function($super, target){
436                 $super('tplMovieList', target);
437                 this.provider = new MovieListProvider(this.show.bind(this));
438                 this.ajaxload = true;
439         },
440
441         getData: function(element){
442                 /*<table
443                         class="mListItem"
444                         data-servicereference="${movie.servicereference}"
445                         data-servicename="${movie.servicename}"
446                         data-title="${movie.title}"
447                         data-description="${movie.description}">
448                 */
449                 var parent = element.up('.mListItem');
450                 var m = {
451                                 servicereference : decodeURIComponent(parent.readAttribute('data-servicereference')),
452                                 servicename : unescape(parent.readAttribute('data-servicename')),
453                                 title : unescape(parent.readAttribute('data-title')),
454                                 description : unescape(parent.readAttribute('data-description'))
455                 };
456
457                 return m;
458         },
459
460         /**
461          * del
462          * Deletes a movie
463          * Parameters:
464          * @servicereference - the servicereference of the movie that should be deleted
465          * @servicename - the name of the service the movie was recorded from
466          * @title - the title of the movie
467          * @description - the description of the movie
468          */
469         del: function(element){
470                 var movie = this.getData(element);
471                 var result = confirm( "Are you sure want to delete the Movie?\n" +
472                                 "Servicename: " + movie.servicename + "\n" +
473                                 "Title: " + movie.title + "\n" +
474                                 "Description: " + movie.description + "\n");
475
476                 if(result){
477                         debug("[MovieListProvider.del] ok confirm panel");
478                         this.provider.simpleResultQuery(URL.moviedelete, {sRef : movie.servicereference}, this.onDeleted.bind(this));
479                 }
480                 else{
481                         debug("[MovieListProvider.del] cancel confirm panel");
482                         result = false;
483                 }
484
485                 this.refresh = result;
486                 return result;
487         },
488
489         /**
490          * del
491          * Display the del result and reloads the movielist
492          */
493         onDeleted: function(transport){
494                 this.simpleResultCallback(transport);
495                 this.reload();
496         }
497 });
498
499 var MovieNavHandler = Class.create(AbstractContentHandler,{
500         initialize: function($super, tagTarget, locTarget){
501                 $super('tplMovieTags', tagTarget);
502                 this.targetLocations = locTarget;
503                 this.tplLocations = 'tplMovieLocations';
504         },
505
506         load: function(locations, tags){
507                 var data = { 'locations' : locations, 'tags' : tags};
508                 this.show(data);
509                 this.showLocations(data);
510         },
511
512         showLocations: function(data){
513                 templateEngine.process(this.tplLocations, data, this.targetLocations);
514         }
515 });
516
517 var MultiEpgHandler  = Class.create(AbstractContentHandler, {
518         initialize: function($super, showMultiEpgFnc){
519                 $super('tplMultiEpg', null);
520                 this.provider = new MultiEpgProvider(this.show.bind(this));
521                 this.ajaxload = false;
522                 this.showEpg = showMultiEpgFnc;
523         },
524
525         show : function(data){
526                 this.data = data;
527                 templateEngine.fetch(
528                         this.tpl,
529                         function(){
530                                 var html = templateEngine.templates[this.tpl].process(this.data);
531                                 this.showEpg(html);
532                         }.bind(this)
533                 );
534         }
535 });
536
537 var ScreenshotHandler = Class.create(AbstractContentHandler, {
538         initialize: function($super, target){
539                 $super('tplGrab', target);
540                 this.provider = new ScreenshotProvider(this.show.bind(this));
541                 this.ajaxload = true;
542         }
543 });
544
545 var SimpleRequestHandler = Class.create(AbstractContentHandler,{
546         initialize: function($super){
547                 $super();
548                 this.provider = new SimpleRequestProvider();
549         },
550
551         load: function(url, parms){
552                 this.provider.simpleResultQuery(
553                                 url,
554                                 parms,
555                                 this.simpleResultCallback.bind(this)
556                         );
557         }
558 });
559
560 var RemoteControlHandler = Class.create(SimpleRequestHandler,{
561         sendKey: function(parms){
562                 this.load(URL.remotecontrol, parms);
563         },
564
565         showSimpleResult: function(result){
566                 this.finished();
567                 if(!result.getState())
568                         this.notify(result.getStateText(), result.getState());
569         }
570 });
571
572 var TimerListHandler  = Class.create(AbstractContentHandler, {
573         initialize: function($super, target){
574                 $super('tplTimerList', target);
575                 this.provider = new TimerListProvider(this.show.bind(this));
576                 this.ajaxload = true;
577         },
578
579         cleanup: function(){
580                 this.provider.simpleResultQuery(
581                                 URL.timercleanup, {},
582                                 function(transport, callback){
583                                         this.simpleResultCallback(transport, callback);
584                                         this.reload();
585                                 }.bind(this));
586         }
587 });
588
589 var TimerHandler = Class.create(AbstractContentHandler, {
590         ACTIONS: [{value : 0, txt : strings.record},
591                         {value : 1, txt : strings.zap}],
592
593         AFTEREVENTS: [{value : 0, txt : strings.do_nothing},
594                                 {value : 1, txt : strings.standby},
595                                 {value : 2, txt : strings.shutdown},
596                                 {value : 3, txt : strings.auto}],
597
598         SELECTED : "selected",
599         CHECKED: "checked",
600
601         /**
602          * initialize
603          * See the description in AbstractContentProvider
604          */
605         initialize: function($super, target, reloadCallback, onFinished){
606                 $super('tplTimerEdit', target, onFinished);
607                 this.t = {};
608                 this.provider = new SimpleRequestProvider();
609                 this.bouquetListProvider = new SimpleServiceListProvider(this.onBouquetsReady.bind(this));
610                 this.serviceListProvider = new SimpleServiceListProvider(this.onServicesReady.bind(this));
611                 this.ajaxload = true;
612                 this.reloadCallback = reloadCallback;
613                 this.data = {};
614         },
615
616         simpleResultCallback: function(transport, callback){
617                 this.provider.simpleResultCallback(
618                                 transport,
619                                 function(result){
620                                         this.showSimpleResult(result, callback);
621                                 }.bind(this)
622                         );
623         },
624
625         showSimpleResult: function($super, result, callback){
626                 $super(result);
627                 if(callback){
628                         callback(result);
629                         return;
630                 } else if(this.reloadCallback){
631                         this.reloadCallback();
632                 }
633         },
634
635         toReadableDate: function(date){
636                 var dateString = "";
637                 dateString += date.getFullYear();
638                 dateString += "-" + addLeadingZero(date.getMonth()+1);
639                 dateString += "-" + addLeadingZero(date.getDate());
640
641                 return dateString;
642         },
643
644         /**
645          * getData
646          *
647          * Extracts the data of a timer from the .tListItem elements data-* attributes
648          *
649          * <tr class="tListItem"
650          *      data-servicereference="${t.servicereference}"
651          *      data-servicename="${t.servicename}"
652          *      data-description="${t.description}"
653          *      data-name="${t.name}"
654          *      data-eventid="${t.eventid}"
655          *      data-begin="${t.begin}"
656          *      data-end="${t.end}"
657          *      data-repeated="${t.repeated}"
658          *      data-justplay="${t.justplay}"
659          *      data-dirname="${t.dirname}"
660          *      data-tags="${t.tags}"
661          *      data-afterevent="${t.afterevent}"
662          *      data-disabled="${t.disabled}"
663          * >
664          *
665          * Parameters:
666          * @element - the html element calling the load function ( onclick="TimerProvider.load(this)" )
667          */
668         getData: function(element, setOld){
669                 var parent = element.up('.tListItem');
670                 var t = {};
671
672                 if(parent){
673                         var begin = unescape(parent.readAttribute('data-begin'));
674                         var end = unescape(parent.readAttribute('data-end'));
675                         var beginD = new Date(begin * 1000);
676                         var endD = new Date(end * 1000);
677                         t = {
678                                 servicereference : decodeURIComponent(parent.readAttribute('data-servicereference')),
679                                 servicename : unescape(parent.readAttribute('data-servicename')),
680                                 description : unescape(parent.readAttribute('data-description')),
681                                 name : unescape(parent.readAttribute('data-name')),
682                                 eventid : unescape(parent.readAttribute('data-eventid')),
683                                 begin : begin,
684                                 beginDate : this.toReadableDate(beginD),
685                                 end : end,
686                                 endDate : this.toReadableDate(endD),
687                                 repeated : unescape(parent.readAttribute('data-repeated')),
688                                 justplay : unescape(parent.readAttribute('data-justplay')),
689                                 dirname : unescape(parent.readAttribute('data-dirname')),
690                                 tags : unescape(parent.readAttribute('data-tags')),
691                                 afterevent : unescape(parent.readAttribute('data-afterevent')),
692                                 disabled : unescape(parent.readAttribute('data-disabled'))
693                         };
694
695                         if(setOld){
696                                 t['servicereferenceOld'] = decodeURIComponent(parent.readAttribute('data-servicereference'));
697                                 t['beginOld'] = t.begin;
698                                 t['endOld'] = t.end;
699                                 t['deleteOldOnSave'] = 1;
700                         } else {
701                                 t['deleteOldOnSave'] = 0;
702                         }
703                 }
704                 return t;
705         },
706
707         getDataFromEvent: function(element){
708                 var parent = element.up('.epgListItem');
709                 var t = {};
710
711                 if(parent){
712                         var begin = unescape(parent.readAttribute('data-start'));
713                         var end = unescape(parent.readAttribute('data-end'));
714                         var beginD = new Date(begin * 1000);
715                         var endD = new Date(end * 1000);
716                         t = {
717                                 servicereference : decodeURIComponent(parent.readAttribute('data-servicereference')),
718                                 servicename : unescape(parent.readAttribute('data-servicename')),
719                                 description : unescape(parent.readAttribute('data-description')),
720                                 name : unescape(parent.readAttribute('data-title')),
721                                 eventid : unescape(parent.readAttribute('data-eventid')),
722                                 begin : begin,
723                                 beginDate : this.toReadableDate(beginD),
724                                 end : end,
725                                 endDate : this.toReadableDate(endD),
726                                 repeated : "0",
727                                 justplay : "0",
728                                 dirname : "",
729                                 tags : "",
730                                 afterevent : "3",
731                                 disabled : "0",
732                                 deleteOldOnSave : "0"
733                         };
734                 }
735                 return t;
736         },
737
738
739         /**
740          * @override
741          * load
742          * When handling timers the whole loading-sequence is entirely different.
743          * Most of the data is already there or has to be created.
744          *
745          * Parameters:
746          * @element - the html element calling the load function ( onclick="TimerProvider.load(this)" )
747          */
748         load: function(element, setOld, initial, fromEvent){
749                 var t = {};
750                 var begin = new Date();
751                 var end = new Date();
752
753                 if(initial){
754                         end.setHours(end.getHours() + 1);
755                         t = {
756                                 servicereference : "",
757                                 servicename : "",
758                                 description : "",
759                                 name : "",
760                                 eventid : "0",
761                                 begin : "0",
762                                 beginDate : this.toReadableDate(begin),
763                                 end : "0",
764                                 endDate : this.toReadableDate(end),
765                                 repeated : "0",
766                                 justplay : "0",
767                                 dirname : "",
768                                 tags : "",
769                                 afterevent : "3",
770                                 disabled : "0"
771                         };
772                 } else {
773                         if(fromEvent){
774                                 t = this.getDataFromEvent(element);
775                         } else {
776                                 t = this.getData(element, setOld);
777                         }
778                         begin = new Date(t.begin * 1000);
779                         end = new Date(t.end * 1000);
780                 }
781
782
783                 var bHours = this.numericalOptionList(0, 23, begin.getHours());
784                 var bMinutes = this.numericalOptionList(0, 59, begin.getMinutes());
785                 var eHours = this.numericalOptionList(0, 23, end.getHours());
786                 var eMinutes = this.numericalOptionList(0, 59, end.getMinutes());
787
788                 var actions = this.ACTIONS;
789                 for (var i = 0; i < actions.length; i++) {
790                         delete actions[i].selected;
791                 }
792                 actions[t.justplay].selected = this.SELECTED;
793
794                 var afterevents = this.AFTEREVENTS;
795                 for (var i = 0; i < afterevents.length; i++) {
796                         delete afterevents[i].selected;
797                 }
798                 afterevents[t.afterevent].selected = this.SELECTED;
799
800                 var repeated = this.repeatedDaysList(t.repeated);
801
802                 var data = {
803                                 shour : bHours,
804                                 smin : bMinutes,
805                                 ehour : eHours,
806                                 emin : eMinutes,
807                                 action : actions,
808                                 channel : [],
809                                 afterEvent : afterevents,
810                                 repeated : repeated,
811                                 timer : t };
812                 var _this = this;
813                 core.lt.getLocationsAndTags(function(currentLocation, locations, tags){
814                         _this.onLocationsAndTagsReady(data, currentLocation, locations, tags, initial);
815                 });
816         },
817
818         onLocationsAndTagsReady: function(data, currentLocation, locations, tags, initial){
819                 var dirname = data.timer.dirname;
820                 if(dirname == "")
821                         dirname = currentLocation;
822                 var l = toOptionList(locations, dirname);
823                 var t = toOptionList(tags, data.timer.tags, " ");
824                 t.shift();
825                 l.shift();
826
827                 data['dirname'] = l;
828                 data['tags'] = t;
829                 if(initial){
830                         data.timer.dirname = currentLocation;
831                 }
832                 this.data = data;
833                 this.bouquetListProvider.load({'sRef' : bouquetsTv});
834         },
835
836         onBouquetsReady: function(data){
837                 this.data['bouquets'] = data.services;
838                 this.serviceListProvider.load({'sRef' : unescape(data.services[0].servicereference)});
839         },
840
841         onServicesReady: function(data){
842                 var services = data.services;
843                 var serviceFound = false;
844                 var timer = this.data.timer;
845                 services.each(function(service){
846                         if(decodeURIComponent(service.servicereference) == timer.servicereference){
847                                 service['selected'] = 'selected';
848                                 serviceFound = true;
849                         } else if (decodeURIComponent(service.servicereference)
850                                    .startsWith("1:64:")) {
851                                 service['selected'] = 'disabled';
852                         } else {
853                                 service['selected'] = '';
854                         }
855                 }.bind(this));
856                 if ((timer.servicereference != "") && !serviceFound) {
857                         services.push( {'servicereference' : timer.servicereference, 'servicename' : timer.servicename, 'selected' : 'selected'});
858                 }
859
860                 this.data['services'] = services;
861                 this.show(this.data);
862         },
863
864         onBouquetChanged: function(bRef, callback){
865                 var _this = this;
866                 var fnc = function(data){
867                         callback(data, _this.data.timer);
868                 };
869                 var prov = new SimpleServiceListProvider(fnc);
870                 prov.load({'sRef' : bRef});
871         },
872
873         recordNow: function(type, callback){
874                 this.provider.simpleResultQuery(
875                         URL.recordnow,
876                         {
877                                 'recordnow' : type
878                         },
879                         function(result){
880                                 if(!callback)
881                                         callback = function(){}; //Avoid automatic reload
882                                 this.simpleResultCallback(result, callback);
883                         }.bind(this));
884         },
885
886         addByEventId: function(sRef, id, justplay){
887                 this.provider.simpleResultQuery(
888                         URL.timeraddbyeventid,
889                         {
890                                 'sRef' : sRef,
891                                 'eventid' : id,
892                                 'justplay' : justplay
893                         },
894                         this.simpleResultCallback.bind(this));
895         },
896
897         change: function(t, old){
898                 var parms = {
899                         'sRef' : t.servicereference,
900                         'begin' : t.begin,
901                         'end' : t.end,
902                         'name' : t.name,
903                         'description' : t.description,
904                         'dirname' : t.dirname,
905                         'tags' : t.tags,
906                         'afterevent' : t.afterevent,
907                         'eit' : t.eventid,
908                         'disabled' : t.disabled,
909                         'justplay' : t.justplay,
910                         'repeated' : t.repeated
911                 };
912
913                 if(old){
914                         Object.extend(parms, {
915                                 'channelOld' : old.servicereference,
916                                 'beginOld' : old.begin,
917                                 'endOld' : old.end,
918                                 'deleteOldOnSave' : old.deleteOldOnSave
919                         });
920                 } else {
921                         parms['deleteOldOnSave'] = 0;
922                 }
923
924                 this.provider.simpleResultQuery(
925                         URL.timerchange,
926                         parms,
927                         this.simpleResultCallback.bind(this)
928                 );
929         },
930
931         del: function(element){
932                 var t = this.getData(element);
933                 var result = confirm("Selected timer:\n" + "Channel: " + t.servicename + "\n" +
934                                 "Name: " + t.name + "\n" + "Description: " + t.description + "\n" +
935                                 "Are you sure that you want to delete the Timer?");
936                 if (result) {
937                         debug("[TimerListProvider].del ok confirm panel");
938                         this.refresh = true;
939                         this.provider.simpleResultQuery(
940                                         URL.timerdelete,
941                                         {'sRef' : t.servicereference, 'begin' : t.begin, 'end' : t.end},
942                                         this.simpleResultCallback.bind(this)
943                                 );
944                 }
945                 return result;
946         },
947
948         toggleDisabled: function(element){
949                 var t = this.getData(element, true);
950                 var old = t;
951                 if(t.disabled == '0')
952                         t.disabled = '1';
953                 else
954                         t.disabled = '0';
955                 this.change(t, old);
956         },
957
958         /**
959          * repeatedDaysList
960          *
961          * Parameters:
962          * @num - the decimal value to apply as bitmask
963          * @return - a list of {id : dayid, value : dayvalue, txt : daytext, long : daylong}
964          **/
965         repeatedDaysList: function(num){
966                 var days = [{id : 'mo', value : 1, txt : 'Mo', long : 'Monday'},
967                                         {id : 'tu', value : 2, txt : 'Tu', long : 'Tuesday'},
968                                         {id : 'we', value : 4, txt : 'We', long : 'Wednesday'},
969                                         {id : 'th', value : 8, txt : 'Th', long : 'Thursday'},
970                                         {id : 'fr', value : 16, txt : 'Fr', long : 'Friday'},
971                                         {id : 'sa', value : 32, txt : 'Sa', long : 'Saturday'},
972                                         {id : 'su', value : 64, txt : 'Su', long : 'Sunday'},
973                                         {id : 'mf', value : 31, txt : 'Mo-Fr', long : 'Monday to Friday'},
974                                         {id : 'ms', value : 127, txt : 'Mo-Su', long : 'Monday to Sunday'}
975                                         ];
976                 var orgNum = num;
977                 // num is the decimal value of the bitmask for checked days
978                 for(var i = 0; i < days.length; i++){
979                         days[i].checked = "";
980
981                         //set checked when most right bit is 1
982                         if(num &1 == 1){
983                                 days[i].checked = this.CHECKED;
984                         }
985
986                         // shift one bit to the right
987                         num = num >> 1;
988                 }
989
990                 //check for special cases (Mo-Fr & Mo-Su)
991                 if(orgNum == 31){
992                         days[7].checked = this.CHECKED;
993                 } else if (orgNum == 127){
994                         days[8].checked = this.CHECKED;
995                 }
996
997                 return days;
998         },
999
1000         /**
1001          * numericalOptionList
1002          * Create a List of numerical-based options
1003          * Entry.value is being extended to at least 2 digits (9 => 09)
1004          *
1005          * Parameters:
1006          * @lowerBound - Number to start at
1007          * @upperBound - Number to stop at
1008          * @selectedValue - entry.selected is set to this.SELECTED if number == selectedValue ("" else)
1009          **/
1010         numericalOptionList: function(lowerBound, upperBound, selectedValue, offset){
1011                 var list = [];
1012                 var idx = 0;
1013                 if(offset == undefined){
1014                         offset = 0;
1015                 }
1016
1017                 for(var i = lowerBound; i <= upperBound; i++){
1018                         var t = i + offset;
1019                         var txt = t < 10 ? "0" + t : t;
1020                         var selected = "";
1021                         if(i == selectedValue){
1022                                 selected = this.SELECTED;
1023                         }
1024                         list[idx] = {value : i, txt : txt, selected : selected};
1025                         idx++;
1026                 }
1027                 return list;
1028         },
1029
1030         /**
1031          * daysOptionList
1032          *
1033          * Determines how many Days a month has an builds an
1034          * numericalOptionsList for that number of Days
1035          */
1036         daysOptionList: function(date){
1037                 var days = 32 - new Date(date.getYear(), date.getMonth(), 32).getDate();
1038                 return this.numericalOptionList(1, days, date.getDate());
1039         },
1040
1041         /**
1042          * commitForm
1043          *
1044          * Commit the Timer Form by serializing it, generating the correct paramteters and then executing the change Method
1045          * @id - id of the Form
1046          */
1047         commitForm : function(id){
1048                 debug("TimerHandler.commitForm");
1049                 var values = $(id).serialize(true);
1050
1051                 var tags = [];
1052                 $$('.tEditTag').each(function(element){
1053                         var selected = element.readAttribute('data-selected');
1054                         if(selected == "selected"){
1055                                 var value = element.readAttribute('data-value');
1056                                 tags.push(value);
1057                         }
1058                 });
1059
1060                 var repeated = 0;
1061                 $$('.tEditRepeated').each(function(element){
1062                         if(element.checked){
1063                                 if(element.value != 31 && element.value != 127){
1064                                         repeated += Number(element.value);
1065                                 }
1066                         }
1067                 });
1068
1069                 var begin = 0;
1070                 var end = 0;
1071
1072                 var startDate = $('sdate').value.split('-');
1073                 var sDate = new Date();
1074                 sDate.setFullYear(startDate[0], startDate[1] - 1, startDate[2]);
1075                 sDate.setHours( $('shour').value );
1076                 sDate.setMinutes( $('smin').value );
1077                 sDate.setSeconds(0);
1078                 begin = Math.floor(sDate.getTime() / 1000);
1079
1080                 var endDate = $('edate').value.split('-');
1081                 var eDate = new Date();
1082                 eDate.setFullYear(endDate[0], endDate[1] - 1, endDate[2]);
1083                 eDate.setHours( $('ehour').value );
1084                 eDate.setMinutes( $('emin').value );
1085                 eDate.setSeconds(0);
1086                 end = Math.floor(eDate.getTime() / 1000);
1087
1088                 timer = {
1089                         'servicereference' : decodeURIComponent(values.service),
1090                         'begin' : begin,
1091                         'end' : end,
1092                         'name' : values.name,
1093                         'eventid' : values.eventid,
1094                         'description' : values.description,
1095                         'dirname' : values.dirname,
1096                         'tags' : tags.join(" "),
1097                         'afterevent' : values.afterevent,
1098                         'eit' : 0,
1099                         'disabled' : values.disabled,
1100                         'justplay' : values.justplay,
1101                         'repeated' : repeated
1102                 };
1103                 var old = null;
1104                 if(values.deleteOldOnSave == "1"){
1105                         old = {
1106                                 'servicereference' : decodeURIComponent(values.servicereferenceOld),
1107                                 'begin' : values.beginOld,
1108                                 'end' : values.endOld,
1109                                 'deleteOldOnSave' : values.deleteOldOnSave
1110                         };
1111                 }
1112
1113                 debug(timer);
1114                 debug(old);
1115                 this.change(timer, old);
1116         },
1117
1118         /**
1119          * renderXML
1120          * See the description in AbstractContentProvider
1121          */
1122         renderXML: function(xml){
1123                 var list = new TimerList(xml).getArray();
1124                 return {timer : list};
1125         }
1126 });
1127
1128
1129 var VolumeHandler  = Class.create(AbstractContentHandler, {
1130         initialize: function($super, target){
1131                 $super('tplVolume', target);
1132                 this.provider = new VolumeProvider(this.show.bind(this));
1133                 this.ajaxload = false;
1134         }
1135 });