// File epgWidget.js
// Pass an variable epgWidgetDefaultChannel to set the default channel, if no variable passed, will use the very default channel

/* if (window.location.hash == "#en")
var _lang = "en";
else var _lang = "zh"; */

var APP = {
	UIEnabled : true,
	VIEW : "program-list",
	LAST_VIEW : null,
	GENRE_OFFSET : null,
	DEFAULT_CHANNEL : 101,
	isIPhone : function(){ return (null != (navigator.userAgent.match(/iPhone/i)) || null != (navigator.userAgent.match(/iPod/i)) || null != (navigator.userAgent.match(/iPad/i))) },
	isIE : function(){ return null != (navigator.userAgent.match(/MSIE/i)) },
	CONFIG : {
		CLEAR_ON_SWITCH : true,			// Clear program list when going to search view
		SCROLL_ACCEL : 1.05,			// Acceleration multipler for scroll bar
		SCROLL_MAX_SPEED : 10,			// Maximum velocity for scroll bar
		SIMUALTE_LAG : true,			// Simulate a Time Lag when firing AJAX requests
		SIMULATE_SERVER_ERROR : true,	// Simulate a Server error
		TEXTURE : true,					// Apply a "drag" texture on the scroll bar
		AJAX_LOADER_DELAY : 500,		// The delay before a loading animation is shown when firing AJAX requests
		AJAX_TIMEOUT : 5000,			// The maximum execution time allowed for AJAX requests
		AJAX_RETRY_DELAY : 5,			// The initial interval between AJAX retries (in seconds)
		AJAX_MAX_RETRY : 3,				// Max number of retries
		AJAX_MAX_RETRY_DELAY : 60		// Upper limit for retry interval
	},
	TRANSLATION : {
		TITLE : {
			zh : "今日節目表",
			en : "Program list"
		},
		ALL_CHANNEL : {
			zh : "所有頻道",
			en : "All channels"
		},
		FAVOURITE : {
			zh : "最愛頻道",
			en : "Favourite channel"
		},
		BACK : {
			zh : "返回",
			en : "Back"
		},
		SEARCH : {
			zh : "搜尋",
			en : "Search"
		},
		EDIT : {
			zh : "編輯",
			en : "Edit"
		},
		SAVE : {
			zh : "儲存",
			en : "Save"
		},
		NOW_PLAYING : {
			zh : "現在播放",
			en : "Now playing"
		},
		GENRE : {
			zh : {
				0 : "所有頻道",
				1 : "電影",
				2 : "旅遊",
				3 : "新聞",
				4 : "兒童",
				5 : "娛樂",
				6 : "體育",
				7 : "外語",
				8 : "無線收費台",
				9 : "成人"
			},
			en : {
				0 : "All channel",
				1 : "Movie",
				2 : "Travel",
				3 : "News",
				4 : "Kids",
				5 : "Entertainment",
				6 : "Sports",
				7 : "Foreign",
				8 : "TVB paid channel",
				9 : "Adult"
			}
		},
		GENRE_SHORT : {
			movie : {
				zh : "電影",
				en : "Movie"
			},
			travel : {
				zh : "旅遊",
				en : "Travel"
			},
			news : {
				zh : "新聞",
				en : "News"
			},
			kids : {
				zh : "兒童",
				en : "Kids"
			},
			sports : {
				zh : "體育",
				en : "Sports"
			},
			entertainment : {
				zh : "娛樂",
				en : "Entertainment"
			},
			foreign : {
				zh : "外語",
				en : "Foreign"
			},
			tvb : {
				zh : "無綫",
				en : "TVB"
			},
			adults : {
				zh : "成人",
				en : "Adults"
			}
		}
	},
	USER : {
		favouriteChannel : []
	}
}

try{
	if (epgWidgetDefaultChannel != null){
		APP.DEFAULT_CHANNEL = epgWidgetDefaultChannel;
	}
}catch(e){};


var PRF = {
	CHANNEL : 1,					// = Program->Channel
	STRING_CHANNEL : 2,				// = Program->StringChannel
	ID : 4,							// = Program->ID
	NAME : 8,						// = Program->Name
	VO_LANGUAGE : 16,				// = Program->VOLanguage
	NAME_LANGUAGE : 32,				// = Program->NameLanguage
	DATE : 64,						// = Program->Date
	START_TIME : 128,				// = Program->StartTime
	END_TIME : 256,					// = Program->EndTime
	DURATION : 512,					// = Program->Duration
	START_TIME_IN_MINUTE : 1024,	// = Program->StartTimeInMinute
	END_TIME_IN_MINUTE : 2048,		// = Program->EndTimeInMinute
	TAGS : 4096,					// = Program->Tags
	IMAGE : 8192					// = Program->Image
}

$(document).ready(function(){

	function rLang(){
		if (_lang == "en") return "en_us";
		else return "zh_tw";
	}

	_langUrl = "/gw-epg/epg/channelMapping.zh-TW.js";
	if (_lang == "en") _langUrl = "/gw-epg/epg/channelMapping.en-US.js";
	
	if ($("div.debug").size() == 1){
		$("[function=simulate-time-lag]").click(config);
		$("[function=simulate-server-error]").click(config);
		$("[function=add-texture]").click(config);
		$("[function=scroll-max-speed]").keyup(config);
		$("[function=scroll-accel]").keyup(config);
		$("[function=switch-language]").click(function(){
			if (_lang == "en"){
				_lang = "zh";
				window.location.hash = "#zh";
			} else {
				_lang = "en";
				window.location.hash = "#en";
			}
			target = $("*[binding]", $("#epg-widget"));
			$.each(target, function(idx, obj){
				text = eval("APP.TRANSLATION." + $(this).attr("binding") + "." + _lang);
				$(this).text(text);
			});
			alert('Dynamic resources are updated. For static resources, please refresh this page to see changes');
		});
	}
	
	function config(){
		option = $("div.debug");
		if (option.size() == 0) return;
		
		APP.CONFIG.SIMULATE_LAG = $("[function=simulate-time-lag]:checked").size() == 1;
		APP.CONFIG.SIMULATE_SERVER_ERROR = $("[function=simulate-server-error]:checked").size() == 1;
		
		if ($("[function=add-texture]:checked").size() == 1) $("div.texture").show();
		else $("div.texture").hide();
		
		APP.CONFIG.SCROLL_MAX_SPEED = parseFloat($("[function=scroll-max-speed]").val());
		if (isNaN(APP.CONFIG.SCROLL_MAX_SPEED)) APP.CONFIG.SCROLL_MAX_SPEED = 10;
		
		APP.CONFIG.SCROLL_ACCEL = parseFloat($("[function=scroll-accel]").val());
		if (isNaN(APP.CONFIG.SCROLL_ACCEL)) APP.CONFIG.SCROLL_ACCEL = 1.05;
	}
	config();
	

	$.ajax({
		url: _langUrl,
		method: "get",
		dataType: "script",
		beforeSend : function(){ APP.UIEnabled = false },
		success: function(){
		
			var WidgetProgramFeeder = {
				_container : $("#program-list ul"),
				_callback : null,
				fill : function(programs, channel, callback){
					this._dom = null;
					this._callback = callback;
					var _self = this;
					this._container.empty();

					$.each(programs.data.chProgram, function(idx, program){
						$.each(program, function(i, o){ _self._createEntry(o);});
					});
					this.finalize();
				},
				_createEntry : function(entry){
					// Sample Dom
					/* <li class="first-row"><div class="time">11:00 am</div><div class="name">大話荷里活</div></li>
					   <li><div class="time">13:00 am</div><div class="name">蠻荒奇緣</div></li>
					   <li><div class="time">2:30 pm</div><div class="name">海闊天空 (14:35)</div></li>
						.
						.
						.
						
					   <li class="last-row"><div class="time">5:30 pm</div><div class="name">緣路有你 (17:50)</div></li>
					*/
					var dom = $('<li><div class="time">' + EpgTools.displayEx(entry.start) + '</div><div class="name">' + entry.name + '</div></li>')
						.appendTo(this._container);
				},
				finalize : function(){
					$("li:first", this._container).addClass("first-row");
					$("li:last", this._container).addClass("last-row");
					// this._dom.appendTo($("#program-list"));
					APP.UIEnabled = true;
					this._callback();
				}
			}
			
			var WidgetFavouriteFeeder = {
				_container : $("#favourite-result"),
				
				fill : function(channels, time){

					var _self = this;
					this._container.empty();
					if (channels.length == 0 || !channels) return false;
					tmp = "";
					for (i = 0; i < channels.length; i++){
						tmp += channels[i].toString();
						if ( i != channels.length - 1 ) tmp += ","
					}
					var d = new Date();
					if (typeof(time) == "undefined" || time == "now") time = d.getTime()
					else time = parseInt(time, 10);
					var param = {
						fn : 'list-ch-chs',
						r_serv : 'chToProgram',
						r_lang : rLang(),
						s_ch : tmp,
						s_prf : PRF.NAME | PRF.START_TIME,
						s_start : time,
						s_end : time + 3600000 * 24 - 1,
						s_groupLimit : 3
					}
					// http://10.3.226.92:8080/gw-epg/getData.jsp?fn=list-ch-chs&s_ch=100%2C110&r_lang=zh_tw&s_prf=136&s_start=1286294400000&s_end=1286380740000
					
					AjaxWrapper.Search(param, function(data){
						$.each(data.data.chProgram, function(channel, obj){
							$.each(obj, function(idx, program){
								if (channel != 'keys') _self._createEntry(channel, program);
							});
						});
						_self.finalize();
					});
				},
				
				_createEntry : function(channel, program){
					// console.info(channel, program);
					
					// DOM Sample
					/* <ul class="result">
						<li class="header"><span class="channel-link" channel-link="111"><span class="channel-number">111</span><span class="channel-name">HBO Hits</span></span></li>
						<li><span class="time">8:30 am</span><span class="program-name" lang="zh">伴娘先生</span></li>
						<li><span class="time">10:45 am</span><span class="program-name">News Report</span></li>
						<li><span class="time">11:30 am</span><span class="program-name" lang="zh">狂野時速 4</span></li>
						<li><span class="time">3:00 pm</span><span class="program-name" lang="zh">430 穿梭機</span></li>
					</ul>
					*/
					
					// create header if not yet drawn
					this._createProgram(channel, program, this._createHeader(channel));
				},
				
				_createProgram : function(channel, program, header){
					// console.info(program);
					$('<li><span class="time">' + EpgTools.displayEx(program.start) + '</span><span class="program-name">' + program.name + '</span></li>')
						.appendTo(header);
				},
				
				_createHeader : function(channel){
					target = $("ul.result[channel=" + channel + "]", this._container);
					if (target.index() == -1){
						// target = $('<ul class="result" channel="' + channel + '"><li class="header"><span class="channel-link" channel="' + channel + '"><span class="channel-number">' + channel + '</span><span class="channel-name">' + ChannelMapping[channel].name + '</span></span></ul>')
						target = $('<ul class="result" channel="' + channel + '"><li class="header"><span class="channel-link" channel="' + channel + '"><span class="channel-number">' + channel + '</span><span class="channel-name">' + ChannelMapping[channel].name + '</span></span></ul>')
							.appendTo(this._container);
					}
					return target;
				},
				
				finalize : function(){
					$('<div class="separator"></div>').insertBefore($("ul.result:not(:first)", this._container));
					$("#favourite-result").slideDown("fast", function(){
						APP.UIEnabled = true;
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					});
				}
			}
			
			var WidgetSearchFeeder = {
			
				_container : $("#search-result"),
				_callback : null,
				
				searchChannel : function(keyword){
					var container = $(".channel-result li", this._container).not(".header").remove();
					// channels are searched within local context
					var keyword = new RegExp(keyword, "gi");
					var _self = this;
					$.each(ChannelMapping, function(idx, obj){
						if (obj.name.match(keyword)){
							// create the header if not already here
							if ($(".channel-result li.header").index() == -1)
								$('<li class="header">頻道</li>').appendTo($(".channel-result"));
							// Dom Sample
							/* <li class="first-row"><span class="channel-link" channel-link="332"><div class="channel-number">332</div><div class="channel-name">now 新聞台</div></span></li>
							   <li><span class="channel-link" channel-link="333"><div class="channel-number">333</div><div class="channel-name">now 財經台</div></span></li>
							   <li class="last-row"><span class="channel-link" channel-link="336"><div class="channel-number">336</div><div class="channel-name">now 報價台</div></span></li>
							*/
							idx = $(".channel-result li").size() - 1;
							$('<li><span class="channel-link" channel="' + obj.channel + '"><div class="channel-number">' + obj.channel + '</div><div class="channel-name">' + obj.name + '</div></span></li>')
								.insertAfter($(".channel-result li:eq("+ idx +")"));
						}
					});
					
					$(".channel-result li:eq(1)").addClass("first-row");
					$(".channel-result li:last").addClass("last-row");
					return this;
				},
				
				searchProgram : function(keyword){
					var _self = this;
					// search/?r_lang=en_us&s_name=Life&s_groupLimit=3
					var param = {
						fn : 'search-se',
						r_serv : 'searchProgram',
						s_name : keyword, // !?!?!?!
						s_groupLimit : 3,
						r_lang : rLang(),
						s_prf : PRF.NAME | PRF.START_TIME | PRF.CHANNEL,
						s_start : new Date().getTime(),
						s_end : new Date().getTime() + 3600 * 24 * 1000 - 1
					}

					AjaxWrapper.Search(param, function(data){
						$("ul.program-result li", _self.container).remove();
						if ($(".program-result li.header").index() == -1)
								_self._programPointer = $('<li class="header">節目</li>').appendTo($(".program-result"));
						$.each(data.data.chProgram, function(channel, obj){
							//if (channel != 'keys')
								$.each(obj, function(idx, program){
									_self._createEntry(channel, program)});
									// console.info(program);
							//	});
						});
						_self.finalize();
					});
				},
				
				fill : function(programs, search, callback){
				},
				
				_createEntry : function(channel, entry){
					// console.info(channel, entry);
					this._createProgramHeader(channel);
					// DOM Sample
					/* <li class="first-row" channel="134"><span class="channel-link" channel-link="134"><div class="channel-number">134</div><div class="channel-name">中國電影頻道</div></span></li>
					   <li class="last-row"><div class="time">10:15 am</div><div class="name">Now 所有夢想都開花</div></li>
					   <li class="separator"></li>
					   <li class="first-row"><span class="channel-link" channel-link="208"><div class="channel-number">208</div><div class="channel-name">Discovery World HD</div></span></li>
					   <li class="last-row"><div class="time">10:15 am</div><div class="name">Now 所有夢想都開花</div></li>
					*/
					// this._programPointer = $('<li><div class="time">' + entry.StartTime + '</div><div class="name">' + entry.Name + '</div></li>')
					this._programPointer = $('<li><div class="time">' + EpgTools.displayEx(entry.start) + '</div><div class="name">' + entry.name + '</div></li>')
						.insertAfter(this._programPointer);
				},
				_programPointer : $(".program-result li.header:first"),
				_createProgramHeader : function(channel){
					if ($("li.first-row[channel=" + channel + "]").index() == -1){
						try {
							channelName = ChannelMapping[channel];
							if (channelName != undefined){
								next = $('<li class="first-row" channel="' + channel + '"><span class="channel-link" channel="' + channel + '"><div class="channel-number">' + channel + '</div><div class="channel-name">' + ChannelMapping[channel].name + '</div></span></li>')
									.insertAfter(this._programPointer);
									this._programPointer = next;
							}
						} catch(ex){
							// console.info(ex);
						}
					}
				},
				
				finalize : function(){
				
					$('<li class="separator"></li>').insertBefore($("li.first-row[channel]").not($("li.first-row[channel]:first")))
						.prev("li").addClass("last-row");
					
					$("#search-result").slideDown("fast", function(){
						APP.UIEnabled = true;
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					});
				}
				
			}
			
			var AjaxWrapper = {
				Result : null,
				// Backend : 'demo.php',
				//Backend : '/epg/rpc.php',
				Backend : '/gw-epg/get-data',
				Token : '1234567',
				fnLoader : null,
				retry : 0,
				Search : function(param, callback){
					// param is where cache can be implemented on top of
					
					myUrl = AjaxWrapper.Backend; // + "?token=" + AjaxWrapper.Token;
					// if (APP.CONFIG.SIMULATE_LAG) myUrl += "?lag=1";
					// if (APP.CONFIG.SIMULATE_SERVER_ERROR) myUrl += "&error=1";
					
					$.ajax({
						url : myUrl,
						data : param,
						dataType : 'json',
						timeout : APP.CONFIG.AJAX_TIMEOUT,
						beforeSend : function(){
							APP.UIEnabled = false;
							/* clearTimeout(AjaxWrapper.fnLoader);
							AjaxWrapper.fnLoader = null;
							AjaxWrapper.fnLoader = setTimeout(function(){ Loader.play() }, APP.CONFIG.AJAX_LOADER_DELAY); */
						},
						error : function(xhr, msg, err){
							// check error
							// if (msg == "timeout"){
								// Retry
								/* Loader.stop().error(function(){
									AjaxWrapper.Search(param, callback);
								});
								clearTimeout(AjaxWrapper.fnLoader);
								AjaxWrapper.fnLoader = null; */
							// }
						},
						success : function(result){
							/* AjaxWrapper.retry = 0;
							Loader.stop();
							clearTimeout(AjaxWrapper.fnLoader);
							AjaxWrapper.fnLoader = null; */
							AjaxWrapper.Result = result;
							if (callback) callback(result);
						}
					});
				}
			}
			
			var FavouriteChannel = {
				save : function(){
					tmp = [];
					$.each($("span.edit-entry div.tick"), function(idx, obj){
						ch = $(this).closest("span.edit-entry").attr("channel");
						tmp.push(ch);
					});
					Storage.save('favouriteChannel', tmp);
					// change to array and save to localStorage here
					APP.USER.favouriteChannel = tmp;
				}
			}
						
			var ChannelScroll = {
				container : null,
				scroller : null,
				scrollUp : null,
				scrollDown : null,
				fnScroll : null,
				lastActivecategory : 1,
				fnScrollSpd : 0,
				_ms : 0,
				_sc : null,
				_el : null,
				_elUl : null,
				_elWrapper : null,
				_channelUl : null,
				_mr : null,
				init : function(){
					
					this.container = $(".channel-data-container-wrapper", epgRoot);
					this.scroller = $(".genre-scroller", epgRoot);
					
					var _self = this;
					
					// this._ms = _self.container.get(0).scrollHeight;
					
					this._sc = _self.scroller.closest("div.genre-scroller-container")
					this._el = $("#channel-list div.channel-data-container-wrapper ul.channel-ul", epgRoot);
					this._elUl = $("#channel-list div.genre-container div.genre-list ul", epgRoot);
					this._elWrapper = this._el.closest("div.channel-data-container-wrapper");
					this._channelUl = $("#channel-list div.genre-list ul", epgRoot);
					this.container.scroll(function(){
						
						// maxScroll = _self._ms;
						maxScroll = _self.container.get(0).scrollHeight;
						scrollPercent = _self.container.scrollTop() / (maxScroll - _self.container.height());
						// scrollPercent = $(this).scrollTop() / (maxScroll - _self.container.height());
						
						// Now move the scroll bar position according
						movableRange = _self._sc.height() - _self.scroller.height();
						offset = movableRange * scrollPercent;
						// _self.scroller.get(0).style.webkitTransitionDuration = "0";
						// _self.scroller.get(0).style.webkitTransitionDuration = "10ms";
						_self.scroller.css("top", offset);
						
						// Sync the category indicator
						scrolled = $(this).scrollTop();
						target = 0;

						// Find the top scrolled category
						el = _self._el;
							
						// Special case, if scrollTop = max, it is adults category
						
						if (APP.GENRE_OFFSET == null){
							APP.GENRE_OFFSET = Array();
							// What's the first entry in the view?
							// check the top offset for each genre
							ul = _self._elUl;
							noOfGenre = $("li", ul).size();
							for (i=1; i<noOfGenre+1; i++){
								el = $("li[category=" + i + "]:first");
								offset = el.position().top;
								
								if (i==1){
									baseOffset = el.offset().top - 138;
								}

								APP.GENRE_OFFSET[i-1] = offset - baseOffset;
								
							}
						}
							
						if (el.height() - scrolled == _self._elWrapper.height()){							
							target = APP.GENRE_OFFSET.length - 1;
						} else for (i = 0 ; i < APP.GENRE_OFFSET.length; i++){
							if (scrolled <= APP.GENRE_OFFSET[i]){
								target = Math.max(i - 1, 0);
								break;
							}
						}
						
						if (target != _self.lastActivecategory){
							_self.lastActivecategory = target;
							// el = $("#channel-list div.genre-list ul", epgRoot);
							el = _self._channelUl;
							$("li.active", el).removeClass("active");
							nextActive = $("li:eq(" + target + ")", el);
							nextActive.addClass("active");
						}
							
					});
					
					if (!APP.isIPhone()){							
						_self.scroller.draggable("destroy").draggable({
							containment: 'parent',
							axis : 'y',
							start : function(evt, ui){
								_self.mr = $(evt.target).closest("div.genre-scroller-container").height() - $(evt.target).height();
							},
							drag : function(evt, ui){
								// movableRange = $(evt.target).closest("div.genre-scroller-container").height() - $(evt.target).height();
								movableRange = _self.mr;
								draggedPercent = ui.position.top / movableRange;
								shouldScroll = draggedPercent * (_self.container.get(0).scrollHeight - _self.container.height());
								_self.container.scrollTop(shouldScroll);
							}
						});
						
						clearInterval(ChannelScroll.fnScroll);
						ChannelScroll.fnScroll = null;
						ChannelScroll.fnScrollSpd = 1;
						
						scrollUp = $("div.scroll-up", _self.scroller.closest("div.genre-scroller-container"));
						scrollDown = scrollUp.next("div.scroll-down");
									
						scrollUp.mousedown(function(){
							ChannelScroll.fnScrollSpd = 1.01;
							clearInterval(ChannelScroll.fnScroll);
							ChannelScroll.fnScroll = null;
							ChannelScroll.fnScroll = setInterval(function(){
								ChannelScroll.fnScrollSpd = Math.min(ChannelScroll.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								ChannelScroll.container.scrollTop(ChannelScroll.container.scrollTop()-ChannelScroll.fnScrollSpd);
							}, 10);
						}).mouseup(function(){
							clearInterval(ChannelScroll.fnScroll);
							ChannelScroll.fnScroll = null;
						}).mouseout(function(){
							clearInterval(ChannelScroll.fnScroll);
							ChannelScroll.fnScroll = null;
						});
						
						scrollDown.mousedown(function(){
							ChannelScroll.fnScrollSpd = 1.01;
							clearInterval(ChannelScroll.fnScroll);
							ChannelScroll.fnScroll = null;
							ChannelScroll.fnScroll = setInterval(function(){
								ChannelScroll.fnScrollSpd = Math.min(ChannelScroll.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								ChannelScroll.container.scrollTop(ChannelScroll.container.scrollTop()+ChannelScroll.fnScrollSpd);
							}, 10);
						}).mouseup(function(){
							clearInterval(ChannelScroll.fnScroll);
							ChannelScroll.fnScroll = null;
						}).mouseout(function(){
							clearInterval(ChannelScroll.fnScroll);
							ChannelScroll.fnScroll = null;
						});

					}
					
					else {
						
						
						clearInterval(ChannelScroll.fnScroll);
						ChannelScroll.fnScroll = null;
						ChannelScroll.fnScrollSpd = 1;
						
						scrollUp = $("div.scroll-up", _self.scroller.closest("div.genre-scroller-container"));
						scrollDown = scrollUp.next("div.scroll-down");
						
						//alert("ipad");	
						scrollUp.get(0).addEventListener("touchstart", function(e){
							//console.log("ipad up");													
							_self.fnScrollSpd = 1.01;
							clearInterval(_self.fnScroll);
							_self.fnScroll = null;
							_self.fnScroll = setInterval(function(){
								_self.fnScrollSpd = Math.min(_self.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								_self.container.scrollTop(_self.container.scrollTop() - _self.fnScrollSpd);
							}, 10);
							
							this.addEventListener("touchend", function(e){
								this.removeEventListener("touchend");
								clearInterval(_self.fnScroll);
								_self.fnScroll = null;
							}, false);
						}, false);
						
						scrollDown.get(0).addEventListener("touchstart", function(e){
							_self.fnScrollSpd = 1.01;
							clearInterval(_self.fnScroll);
							_self.fnScroll = null;
							_self.fnScroll = setInterval(function(){
								_self.fnScrollSpd = Math.min(_self.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								_self.container.scrollTop(_self.container.scrollTop()+_self.fnScrollSpd);
							}, 10);
							
							this.addEventListener("touchend", function(e){
								this.removeEventListener("touchend");
								clearInterval(_self.fnScroll);
								_self.fnScroll = null;
							}, false);
						}, false);




					}
				}
			};
					
			var ScrollBar = {
				fnScroll : null,
				fnScrollSpd : 0,
				maxScroll : 0,
				parentHeight : 0,
				barHeight : 0,
				scroller : null,
				contentContainer : null,
				hide : function(){
					this.scroller.closest(".data-scroller-container").stop(true, false).delay(1).fadeOut();
				},
				show : function(myHeight){
					if (myHeight >= ScrollBar.scroller.closest(".data-scroller-container").height()){
						this.hide();
						return;
					}
					
					ScrollBar.contentContainer.scrollTop(0);
					
					this.scroller.closest(".data-scroller-container").stop(true, true).delay(1).fadeIn();
					
					this.scroller.stop().animate({
						height : myHeight
					}, function(){
						
					});
				},
				_sContainer : null,
				init : function(contentContainer, scroller){
					// calculate the scrollbar height
					var _self = this;
					
					
					this.maxScroll = contentContainer.get(0).scrollHeight;
					this.parentHeight = contentContainer.parent().height();
					this.barHeight = contentContainer.height() / this.maxScroll * scroller.closest("div.data-scroller-container").height();
					this.contentContainer = contentContainer;
					this.scroller = scroller;
					this.show(this.barHeight);
					
					this._sContainer = ScrollBar.scroller.closest("div.data-scroller-container");
					
					// clear the scroll event of the content container, and rebind
					contentContainer.unbind("scroll").scroll(function(){
						// Scroll percent
						// scrollPercent = $(this).scrollTop() / (ScrollBar.maxScroll - ScrollBar.contentContainer.height());
						scrollPercent = ScrollBar.contentContainer.scrollTop() / (ScrollBar.maxScroll - ScrollBar.contentContainer.height());
						
						// Now move the scroll bar position according
						movableRange = _self._sContainer.height() - ScrollBar.scroller.height();
						offset = movableRange * scrollPercent;
						// ScrollBar.scroller.get(0).style.webkitTransitionDuration = "0";
						// ScrollBar.scroller.get(0).style.webkitTransitionDuration = "10ms";
						ScrollBar.scroller.css("top", offset);
		
					}).scrollTop(0);
					
					scrollUp = scroller.next(".scroll-up");
					scrollDown = scrollUp.next(".scroll-down");
					
					/* if (APP.isIPhone()){
						
						clearInterval(ScrollBar.fnScroll);
						ScrollBar.fnScroll = null;
						ScrollBar.fnScrollSpd = 1;
						
						scrollUp.get(0).addEventListener("touchstart", function(e){
						
					
						
							ScrollBar.fnScrollSpd = 1.01;
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
							ScrollBar.fnScroll = setInterval(function(){
								ScrollBar.fnScrollSpd = Math.min(ScrollBar.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								ScrollBar.contentContainer.scrollTop(ScrollBar.contentContainer.scrollTop() - ScrollBar.fnScrollSpd);
							}, 10);
							
							this.addEventListener("touchend", function(e){
								this.removeEventListener("touchend");
								clearInterval(ScrollBar.fnScroll);
								ScrollBar.fnScroll = null;
							}, false);
						}, false);
						
						scrollDown.get(0).addEventListener("touchstart", function(e){
							ScrollBar.fnScrollSpd = 1.01;
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
							ScrollBar.fnScroll = setInterval(function(){
							ScrollBar.fnScrollSpd = Math.min(ScrollBar.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
							ScrollBar.contentContainer.scrollTop(ScrollBar.contentContainer.scrollTop()+ScrollBar.fnScrollSpd);
							}, 10);
							
							this.addEventListener("touchend", function(e){
								this.removeEventListener("touchend");
								clearInterval(ScrollBar.fnScroll);
								ScrollBar.fnScroll = null;
							}, false);
						}, false);
						
						
					} */
					
					if (!APP.isIPhone()){
					
						scroller.draggable("destroy").draggable({
							containment: 'parent',
							axis : 'y',
							drag : function(evt, ui){
								movableRange = $(evt.target).closest("div.data-scroller-container").height() - $(evt.target).height();
								draggedPercent = ui.position.top / movableRange;
								shouldScroll = draggedPercent * (ScrollBar.contentContainer.get(0).scrollHeight - ScrollBar.contentContainer.height());
								ScrollBar.contentContainer.scrollTop(shouldScroll);
								
								// ... Everyone knows IE is buggy...
								if (true){ // (APP.isIE()){
									_limit = ScrollBar.scroller.closest("div.data-scroller-container").height() - ScrollBar.scroller.height();
									if (ui.position.top > _limit) return false;
								}
							}
						});
						
						clearInterval(ScrollBar.fnScroll);
						ScrollBar.fnScroll = null;
						ScrollBar.fnScrollSpd = 1;
									
						scrollUp.mousedown(function(){
							ScrollBar.fnScrollSpd = 1.01;
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
							ScrollBar.fnScroll = setInterval(function(){
								ScrollBar.fnScrollSpd = Math.min(ScrollBar.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								ScrollBar.contentContainer.scrollTop(ScrollBar.contentContainer.scrollTop()-ScrollBar.fnScrollSpd);
							}, 10);
						}).mouseup(function(){
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
						}).mouseout(function(){
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
						});
						
						scrollDown.mousedown(function(){
							ScrollBar.fnScrollSpd = 1.01;
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
							ScrollBar.fnScroll = setInterval(function(){
								ScrollBar.fnScrollSpd = Math.min(ScrollBar.fnScrollSpd * APP.CONFIG.SCROLL_ACCEL, APP.CONFIG.SCROLL_MAX_SPEED);
								ScrollBar.contentContainer.scrollTop(ScrollBar.contentContainer.scrollTop()+ScrollBar.fnScrollSpd);
							}, 10);
						}).mouseup(function(){
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
						}).mouseout(function(){
							clearInterval(ScrollBar.fnScroll);
							ScrollBar.fnScroll = null;
						});
					}
				}
			}
			
			
			
			// Overlay some stupid thing on top to indicating a search
			var Loader = {
				el : null,
				fnAnimate : [],
				lastView : null,
				init : function(){
					if (this.el == null){
						this.el = $("#loader", epgRoot);
					}
					return this;
				},
				play : function(){
					this.el.show();
					$(".loader-retry:visible", this.el).hide();
					$(".loader-container:hidden", this.el).show();
					this.lastView = $("#search-result:visible")
									.add($("#program-list:visible"))
									.add($("#favourite-result:visible"))
									.add($(".data-scroller-container:visible"));
					this.lastView.fadeOut();
					timeout = 0;
					$.each($(".loader-box", this.el), function(idx, obj){
						timeout += 200;
						Loader.fnAnimate[idx] = setTimeout(function(){
							$(obj).animate({
								opacity: 0.01
							}, function(){
								if (idx == 7){
									$(".loader-box", this.el).css("opacity", 1);
									if ($(":visible", Loader.el).size() > 0) Loader.play();
								}
							});
						}, timeout);
					});
					return this;
				},
				stop : function(){
					for (i=0; i<Loader.fnAnimate.length; i++){
						clearTimeout(Loader.fnAnimate[i]);
						Loader.fnAnimate[i] = null;
					}
					this.el.hide();
					$(".loader-box", this.el).stop().css("opacity", 1);
					return this;
				},
				error : function(callback){
					this.el.show();
					$(".loader-container", this.el).hide();
					$(".loader-retry", this.el).show();
					countdown = $(".loader-retry span.count-down", this.el);
					countdown.text(Math.min(APP.CONFIG.AJAX_RETRY_DELAY * ++AjaxWrapper.retry, APP.CONFIG.AJAX_MAX_RETRY_DELAY));
					fnCountdown = setInterval(function(){
						countdown.text(parseInt(countdown.text()) - 1);
						if (parseInt(countdown.text()) == 0) {
							this.el.hide();
							clearInterval(fnCountdown);
							if (callback) callback();
						}
					}, 1000);
					return this;
				}
			}
			
			
			
			// Clicking the channel list drop box
			$(".input-bar div.channel-select").click(function(evt){
				if ($(this).hasClass("searching")) return;
				// channelControl.update();
				if ($("#channel-list:visible").size() == 0){
					// Not shown, slide it out
					$("#channel-list").slideDown("fast", function(){
						
						if (APP.GENRE_OFFSET == null){
							APP.GENRE_OFFSET = Array();
							// What's the first entry in the view?
							// check the top offset for each genre
							target = $("#channel-list div.genre-container div.genre-list ul", epgRoot);
							noOfGenre = $("li", target).size();
							for (i=1; i<noOfGenre+1; i++){
								el = $("li[category=" + i + "]:first");
								offset = el.position().top;
								APP.GENRE_OFFSET[i-1] = offset;
							}
						}
						
					});
					$(".input-bar.channel-view div.text").addClass("selecting");
				} else {
					// Shown, slide it out
					$("#channel-list").slideUp("fast");
					$(".input-bar.channel-view div.text").removeClass("selecting");
				}
			});
			
			// Clicking on a channel genre in the channel select drop box
			target = $("div.genre-container div.genre-list", epgRoot);
			$.each($("li", target), function(idx, obj){
				$(this).click(function(){
					container = $("div.channel-data-container-wrapper", epgRoot);
					// container.scrollTop(APP.GENRE_OFFSET[idx]+1);
					// Animate the scroll
					container.animate({
						scrollTop : APP.GENRE_OFFSET[idx]+1
					});
				});
			});
			
			APP.USER.favouriteChannel = Storage.load('favouriteChannel');
			
			// Fill data first
			var epgRoot = $("#epg-widget");
			
			// Disable UI when 1. animating, 2. flagged for UI disbale, 3. loading ajax
			$("*", epgRoot).click(function(evt){
				if ($("*:animated", epgRoot).size() > 0) return false;
				// if (!APP.UIEnabled) return false;
				if ($("#loader:visible", epgRoot).size() >0) return false;
			});
			
			// Do not allow horizontal scroll for IE / Safari
			$("*", epgRoot).scroll(function(){
				$(this).scrollLeft(0);
				return true;
			});
			
			// Kill IE fixes if not IE
			if (!APP.isIE()) $("*[fix~=ie7]").hide();
			
			// Fill the channel list in the Channel Select Container
			// Sample DOM: <li category="1" channel="110"><div class="channel-code">110</div><div class="channel-name">HBO HD</div></li>
			var target = $("#channel-list ul.channel-ul:first", epgRoot).empty();
			
			$.each(GenreToChannelMapping, function(idx, ch){
				for (i = 0 ; i < ch.length; i++){
					channel = ch[i];
					obj = ChannelMapping[channel];
					cat = null;
					$.each(CategoryToGenreKeys, function(c, g){
						if (g == idx) {
							cat = c;
							return true;
						}
					});
					$('<li class="channel-link" category="' + cat + '" channel-link="' + channel + '" title="' + channel + ' ' + obj.name + '"><div class="channel-code">' + channel + '</div><div class="channel-name">' + obj.name + '</div></li>').appendTo(target);
				}
			});
			
			// Fill the channel list in the Favourite Edit Container
			// Sample DOM - ticked: <li><span class="edit-entry" channel="100"><span class="sprite-sheet checkbox"><div class="sprite-sheet tick"></div></span><span class="channel-number">100</span><span class="channel-name" lang="zh">now 香港台</span></span></li>
			//			  - non-ticked: <li><span class="edit-entry" channel="332"><span class="sprite-sheet checkbox"></span><span class="channel-number">332</span><span class="channel-name" lang="zh">now 新聞台</span></span></li>
			target = $("#favourite-edit ul.edit:first", epgRoot).empty();
			$.each(ChannelMapping, function(idx, obj){
				tickedTag = ($.inArray(obj.channel.toString(), APP.USER.favouriteChannel) != -1 )? '<div class="sprite-sheet tick" style="width: 9px; display: block"></div>':'';
				$('<li channel="'+ obj.channel +'" category="'+ obj.category +'"><span class="edit-entry" channel="' + obj.channel + '"><span class="sprite-sheet checkbox">' + tickedTag + '</span><span class="channel-number">' + obj.channel + '</span><span class="channel-name" lang="zh">' + obj.name + '</span></span></li>').appendTo(target);
			});
			
			// Fill the search text
			$("#widget-search-field", epgRoot).val(eval("APP.TRANSLATION.SEARCH."+ _lang));
			
			// Select a default Channel
			if (typeof(_defaultChannel == "undefined")){
				// no master config from root page, direct it to first channel
				var _defaultChannel = ChannelMapping[APP.DEFAULT_CHANNEL];
			}
			
			// Fill the genre category select drop box
			target = $("ul.genre-select", epgRoot);
			$.each(eval("APP.TRANSLATION.GENRE."+ _lang), function(idx, obj){
				$('<li genre="' + idx + '">' + obj + '</li>').appendTo(target);
			});
			
			// Evenly spaces the genre list in the ul
			target = $("#channel-list div.genre-container div.genre-list ul", epgRoot);
			noOfGenre = $("li", target).size();
			fontHeight = 12;
			parentHeight = parseInt($("#channel-list div.genre-container").css("height")) - 6;
			space = (parentHeight - noOfGenre * fontHeight) / (noOfGenre -1);
			initY = 2;
			for (i=0; i<noOfGenre; i++){
				$($("li", target).get(i)).css("top", initY);
				initY += space + fontHeight;
			}
			
			// Fill the time select box, wrap in a function so if in case there need to update...
			function fillTimeSelectBox(count){
				// Desired DOM
				/*<li time="now"><span lang="zh" class="time">現正播放</span></li>
				<li time="540"><span class="time">9:00 am</span></li>
				<li time="600"><span class="time">10:00 am</span></li>
				<li time="660"><span class="time">11:00 am</span></li>
				<li time="720"><span class="time">12:00 pm</span></li>
				<li time="780"><span class="time">1:00 pm</span></li>
				<li time="840"><span class="time">2:00 pm</span></li>
				<li time="900"><span class="time">3:00 pm</span></li>
				<li time="960"><span class="time">4:00 pm</span></li>
				<li time="1020"><span class="time">5:00 pm</span></li>
				<li time="1080"><span class="time">6:00 pm</span></li>*/
				el = $("#time-select-drop-box ul.time-select");
				el.empty(); // Kill any exisiting entry
				
				// First row, always equals to NOW
				$('<li time="now"><span lang="' + _lang + '" class="time">' + eval("APP.TRANSLATION.NOW_PLAYING."+ _lang) + '</span></li>').appendTo(el);
				
				// Now fill other entries
				if (!count) count = 8;
				d = new Date();
				now = d.getHours();
				
				_d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() + 1, 0, 0, 0);
				
				for (i = 0; i< count ; i++){
					time = _d.getTime();
					displayTime = EpgTools.displayEx(time);
					$('<li time="' + time + '"><span lang="' + _lang +'" class="time">' + displayTime + '</span></li>').appendTo(el);
					_d = new Date(_d.getFullYear(), _d.getMonth(), _d.getDate(), _d.getHours() + 1, 0, 0, 0)
				}
				
				// unixtimestamp = Math.round(Math.round(new Date().getTime()) / 3600) * 3600;
				
				/* for (i = 0; i<count; i++){
					hour = now + i;
					if (hour > 24) hour %= 24;
					
					ampm = "am";
					if ((Math.floor(hour/12)) & 1 == 1) ampm = "pm";
					
					
					// time = hour%24 *60;
					time = unixtimestamp + i * 3600;
					
					$('<li time="' + time + '"><span lang="' + _lang +'" class="time">' + hour%12 + ':00 ' + ampm + '</span></li>').appendTo(el);
				} */
			}
			fillTimeSelectBox(12);
			
			
			// Fill binding text
			target = $("*[binding]", epgRoot);
			$.each(target, function(idx, obj){
				text = eval("APP.TRANSLATION." + $(this).attr("binding") + "." + _lang);
				$(this).text(text);
			});
			
			// Sub the default channel name into the drop box
			target = $("div.input-bar div.channel-select div.text:first", epgRoot);
			target.text(_defaultChannel.channel + " " + _defaultChannel.name);
			
			// var channelControl = new DataControl(".channel-data-container-wrapper");
			
			// Store the channel name in the channel-select text
			$(".input-bar div.channel-select div.text", epgRoot).attr("_text", $(".input-bar.channel-view div.text").text());
			
			// Store the time selected in the time-select text
			$(".input-bar .time-select div.text", epgRoot).attr("timeSelected", eval("APP.TRANSLATION.NOW_PLAYING."+ _lang));
			
			// END OF DATA FILLING
			
			
			// Initialize
			ChannelScroll.init();
			ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
			Loader.init();
			
			
			
			// Clicking on a channel in the list box
			$(".channel-ul li.channel-link").click(function(evt){
			
				// Slide up the channel list box
				$("#channel-list").slideUp("fast");
				$(".input-bar.channel-view div.text").removeClass("selecting");
				channelCode = $(this).attr("channel-link");
				// Put the text into the text box
				channelName = channelCode + " " + ChannelMapping[channelCode].name;
				//console.info(ChannelMapping[channelCode]);
				$(".input-bar div.channel-select div.text").attr("_text", channelName).text(channelName);
				
				var now = new Date();
				var end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 5, -1, 0, 0).getTime();
				
				// console.info(now.getTime(), end);

				var param = {
					fn : 'list-ch',
					r_lang : rLang(),
					s_ch : channelCode,
					s_prf : PRF.NAME | PRF.START_TIME,
					s_start : new Date().getTime(),
					s_end : new Date().getTime() + 3600 * 24 * 1000 - 1
				}
				AjaxWrapper.Search(param, function(data){
					$("#program-list").slideUp("fast", function(){
						WidgetProgramFeeder.fill(data, channelCode, function(){
							$("#program-list").slideDown("fast", function(){
								ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
							});
						});
					});
				});
				
				// demo, DISABLED
				if (false){ // APP.CONFIG.SIMULATE_LAG){
					Loader.play();
					setTimeout(function(){
						Loader.stop();
						$("#program-list").slideDown("fast", function(){
							ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
						});
					}, parseInt(Math.random()*4500 + 500));
				} else {
					$("#program-list").slideDown("fast", function(){
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					});
				}
				
			});
			
			// When the channel list drop box becomes the back button
			$(".input-bar").delegate("div.channel-select.searching", "click", function(evt){
				// Bring back the view if it is hidden
				// $("#search-result:visible").stop().slideUp("fast", function(){ $("#program-list:hidden").stop().slideDown("fast", function(){
				$("#search-result").stop().slideUp("fast", function(){ $("#program-list").stop().slideDown("fast", function(){
					ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
				}) });
				
				$(".input-bar.channel-view > div").removeClass("searching");
				$(".input-bar.channel-view div.text").text($(".input-bar.channel-view div.text").attr("_text"));
			});
			
			// Clicking the search field	
			$(".input-bar.channel-view > .program-search").click(function(evt){
				
				if ($("#loader:visible").size()>0) return false; // do nothing, it's loading
				if ($(this).hasClass("searching")) return; // do nothing, already in search mode
				
				// $("#channel-list:visible").stop().slideUp("fast");
				$("#channel-list").stop().slideUp("fast");
				
				// Switch to search panel
				$(".input-bar.channel-view div.channel-select").addClass("searching");
				$(".input-bar.channel-view div.channel-select.searching div.text").text(eval("APP.TRANSLATION.BACK."+ _lang));
				$(".input-bar.channel-view div.program-search").toggleClass("searching");
				$("#widget-search-field").focus().select();
				
				// Should the view be cleared?
				if (APP.CONFIG.CLEAR_ON_SWITCH) {
					$("#program-list").stop().slideUp("fast", function(){ $("#search-result").stop().slideDown("fast", function(){
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					}) });
				}
			});
			
			// Search field reset button
			$(".program-search .reset-search").click(function(evt){
				$("#widget-search-field").val("").focus();
			});
			
			// Search field enter key
			$("#widget-search-field").keyup(function(evt){
				switch(evt.keyCode){
					case 13 : // ENTER
						if ($(this).val().length == 0) return false;
						if (!APP.UIEnabled) return false;
						APP.UIEnabled = false;
						var _self = $(this);
						$("#search-result").slideUp("fast", function(){
							ScrollBar.hide();
							WidgetSearchFeeder.searchChannel(_self.val()).searchProgram(_self.val());							
						});
						
						break;
					case 27 : // ESC
						if ($("#epg-widget *:animated").size() > 0) break;
						$("div.channel-select.searching").click();
						this.blur();
						break;
					case 38 : // UP
						break;
					case 40 : // DOWN
						break;
					default: break;
				}
			});
			
			// Panel Switch
			$(".tab-bar li:first").click(function(){
				// Go to channel-view
				if ($(this).hasClass("active")) return; // already in channel-view
				
				// Kill any drop-box if visible
				$("div.drop-box:visible").hide();
				
				APP.LAST_VIEW.slideDown("fast", function(){
					ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
				});
				
				$("#favourite-result").hide();
				$("#favourite-edit").hide();
				
				$(".input-bar.channel-view").show();
				$(".input-bar.favourite-view").hide();
				
				$(".tab-bar li").toggleClass("active");
			});
			
			$(".tab-bar li:eq(1)").click(function(){
				// Go to favourite-view
				if ($(this).hasClass("active")) return; // already in channel-view
				
				APP.LAST_VIEW = $("#program-list:visible").add($("#search-result:visible"));
				$("#channel-list:visible").hide();
				
				$(".tab-bar li").toggleClass("active");
				
				// Go favourite view or favourite edit?
				// if fav-channel.length == 0 go edit; else go view
				$("#program-list").hide();
				$("#search-result").hide();
				$(".input-bar.channel-view").hide();
				$(".input-bar.favourite-view").show();
				
				if ( !APP.USER.favouriteChannel || APP.USER.favouriteChannel.length == 0) {
					
					$(".input-bar .edit-or-save div.text").text(eval("APP.TRANSLATION.SAVE."+ _lang));
					$(".input-bar .edit-or-save").attr("role", "edit");
					$("#favourite-edit").show();
					
					// Fill the select box text
					$(".input-bar .time-select").attr("view", "favEdit");
					currentCat = $(".input-bar .time-select div.text").attr("favcategory");
					$(".input-bar .time-select div.text").text(eval("APP.TRANSLATION.GENRE."+ _lang+"[currentCat]"));
					
				} else {
					$("#favourite-result").show();
					$(".input-bar .edit-or-save").attr("role", "save");
					$(".input-bar .edit-or-save div.text").text(eval("APP.TRANSLATION.EDIT."+ _lang));
					$(".input-bar .time-select").attr("view", "favResult");
					$(".input-bar .time-select div.text").text($(".input-bar .time-select div.text").attr("timeSelected"));
					WidgetFavouriteFeeder.fill(APP.USER.favouriteChannel);
				}
				
				ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
			});
			
			// Clicking on EDIT / SAVE button of favourite view
			$(".input-bar div.edit-or-save").click(function(){
				// kill any drop-box if visible
				$(".drop-box:visible").slideUp();
				
				if ($(this).attr("role") == "save"){
					// Go to edit mode
					$(".input-bar .edit-or-save div.text").text(eval("APP.TRANSLATION.SAVE."+ _lang));
					$(".input-bar .edit-or-save").attr("role", "save");
					$("#favourite-result").hide();
					$("#favourite-edit").show("fast", function(){
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"))
					});
					
					// Fill the select box text
					$(".input-bar .time-select").attr("view", "favEdit");
					currentCat = $(".input-bar .time-select div.text").attr("favcategory");
					$(".input-bar .time-select div.text").text(eval("APP.TRANSLATION.GENRE."+ _lang + "[currentCat]"));
					$(this).attr("role", "edit");
				} else if ($(this).attr("role") == "edit"){
					// save the channels
					FavouriteChannel.save();
					// Detect if any channels are selected
					var noOfTicks = $("span.edit-entry div.tick").size();
					if (noOfTicks){
						// Go to result view
						$("#favourite-edit").hide();
						$("#favourite-result").show();
						$(".input-bar .edit-or-save div.text").text(eval("APP.TRANSLATION.EDIT."+ _lang));
						$(".input-bar .time-select").attr("view", "favResult");
						$(".input-bar .time-select div.text").text($(".input-bar .time-select div.text").attr("timeSelected"));
						$(this).attr("role", "save");
						
						// ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
						WidgetFavouriteFeeder.fill(APP.USER.favouriteChannel);
					}
				}
			});
			
			// Clicking on favourite genre category select box
			$(".input-bar .time-select").click(function(){
				if ($(this).attr("view") == "favEdit"){
					if ($("#genre-select-drop-box:visible").size() == 0){
						$("#genre-select-drop-box").slideDown("fast");
						$(".input-bar .time-select div.text").addClass("selecting");
					} else {
						$("#genre-select-drop-box").slideUp("fast");
						$(".input-bar .time-select div.text").removeClass("selecting");
					}
				} else if ($(this).attr("view") == "favResult") {
					if ($("#time-select-drop-box:visible").size() == 0){
						$("#time-select-drop-box").slideDown("fast");
						$(".input-bar .time-select div.text").addClass("selecting");
					} else {
						$("#time-select-drop-box").slideUp("fast");
						$(".input-bar .time-select div.text").removeClass("selecting");
					}
				}
			});
			
			// Clicking on a genre in the genre select box
			$("#genre-select-drop-box ul.genre-select li").click(function(){
				// hide the drop box
				$("#genre-select-drop-box:visible").slideUp("fast");
				$(".input-bar .time-select div.text").removeClass("selecting");
				$(".input-bar .time-select div.text").text($(this).text());
				$(".input-bar .time-select div.text").attr("favcategory", $(this).attr("genre"));
				// replace the channels
				targetcategory = parseInt($(this).attr("genre"));
				
				$("#favourite-edit").slideUp("fast", function(){
					if (targetcategory == 0){
						$("#favourite-edit ul.edit li[category]").show();
					} else {
					
						// $("#favourite-edit ul.edit li[category!="+targetcategory+"]").hide();
						// $("#favourite-edit ul.edit li[category="+targetcategory+"]").show();
						$("#favourite-edit ul.edit li[category]").hide();
						targetGenre = CategoryToGenreMapping[targetcategory];
						$.each(GenreToChannelMapping[targetGenre], function(idx, obj){
							$("#favourite-edit ul.edit li[channel=" + obj + "]").show();
						});
						
					}
					
					$("#favourite-edit").slideDown("fast", function(){
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					});
				});
				
			});
			
			// Clicking on a time slot in the time select box
			$("#time-select-drop-box ul.time-select li").click(function(){
				// hide the drop box
				$("#time-select-drop-box:visible").slideUp("fast");
				$(".input-bar .time-select div.text").removeClass("selecting");
				$(".input-bar .time-select div.text").text($(this).text()).attr("timeSelected", $(this).text());
				
				WidgetFavouriteFeeder.fill(APP.USER.favouriteChannel, $(this).attr("time"));
				// demo, DISABLED
				if (false){ //(APP.CONFIG.SIMULATE_LAG){
					Loader.play();
					setTimeout(function(){
						Loader.stop();
						$("#favourite-result").slideDown("fast", function(){
							ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
						});
					}, parseInt(Math.random()*4500 + 500));
				} else {
					$("#favourite-result").slideDown("fast", function(){
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					});
				}
				
			});
			
			// Ticking on a channel in the favourite edit view
			$("#favourite-edit ul.edit").delegate("span.edit-entry", "click", function(evt){
				var ticked = ($("div.tick", $(this)).size() == 1);
				
				if (ticked) {
					// Remove the tick
					$("div.tick", $(this)).remove();
				} else {
					// Add the tick
					tick = $('<div class="sprite-sheet tick"></div>').css("width", 1).appendTo($(".checkbox", $(this))).animate({
						width: 9
					}, 100);
				}
				
			});
			
			// Drop box up / down functions
			var dropBoxScroll = null;
			var pos = 0;
			$(".drop-box .scroll-up").add($(".drop-box .scroll-down")).mouseover(function(){
				el = $(".scroll-wrapper", $(this).closest(".content"));
				if ($(this).hasClass("scroll-up")) pos = -1;
				else if ($(this).hasClass("scroll-down")) pos = 1;
				dropBoxScroll = setInterval(function(){
					offset = el.scrollTop();
					el.scrollTop(offset+pos);
				}, 10);
			}).mouseout(function(){
				clearTimeout(dropBoxScroll);
				dropBoxScroll = null;
			});
			
			// Various channel links
			$("ul").add("#favourite-result").not(".channel-ul").delegate(".channel-link", "click", function(){
			
				ScrollBar.hide();

				// switch back to channel view
				if ($(".tab-bar li:eq(1)", epgRoot).hasClass("active")){
					// favourite view
					$("div.drop-box:visible").hide();
					$(".input-bar.channel-view > div").removeClass("searching");
				
					/* APP.LAST_VIEW.slideDown("fast", function(){
						ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
					}); */
					
					$("#favourite-result").hide();
					$("#favourite-edit").hide();
					
					$(".input-bar.channel-view").show();
					$(".input-bar.favourite-view").hide();
					
					$(".tab-bar li").toggleClass("active");
					
					// search here
					
				} else if ($(".channel-select.searching").size() == 1) {
					// search results
					
					//$(".channel-select.searching").click();
					
					$("#search-result").stop().slideUp("fast", function(){
						// do the search here
					});
					
					$(".input-bar.channel-view > div").removeClass("searching");
					$(".input-bar.channel-view div.text").text($(".input-bar.channel-view div.text").attr("_text"));
				}
				
				$(".channel-ul li.channel-link[channel-link=" + $(this).attr("channel") + "]").click();
			});
			
			// Fix English version category pane
			if (_lang == "en"){
				$("div.genre-container").css("background", "white").css("overflow", "hidden");
				$("div.genre-container").mousemove(function(evt){
					if (evt.clientX > 290)
					
						if ($("div.genre-container").width() != 130) $("div.genre-container").not(":animated").stop().animate({
							width: 130
						});
					
				}).mouseleave(function(){
					$("div.genre-container").stop().animate({
						width: 70
					});
				});
			} 
			
			APP.UIEnabled = true; // Start to accept interaction now
			// WidgetSearchFeeder.searchProgram("now");
			// unixtimestamp = Math.round(Math.round(new Date().getTime() / 1000) / 3600) * 3600;
			// while (new Date(unixtimestamp * 1000).getHours() != 4) unixtimestamp += 3600;
			
			var param = {
				fn : 'list-ch',
				s_prf : PRF.NAME | PRF.START_TIME,
				s_ch : APP.DEFAULT_CHANNEL,
				r_lang : rLang(),
				s_start : new Date().getTime(),
				s_end : new Date().getTime() + 3600000 * 24 - 1
			}
			AjaxWrapper.Search(param, function(data){
				$("#program-list").slideUp("fast", function(){
					WidgetProgramFeeder.fill(data, APP.DEFAULT_CHANNEL, function(){
						$("#program-list").slideDown("fast", function(){
							ScrollBar.init($(".data-container-wrapper"), $("#epg-scroller"));
						});
					});
				});
			});
		}
		
	});
});
				
				
