
jsImport("publish/sportsnet_sked_config.js?r=" + Math.random()); //defeat caching

var isIE  = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;

var printObjectProps = function(whichObj, doRecurse, whichLevel) {
//accepts an object reference and an optional boolean
//traces out all properties/values of whichObj
//if doRecurse is true (default is false), it recurses into nested objects
//usage:
//sape.Utils.printObjectProps(someObject, [true/false]);
	if (whichLevel == undefined) {
		whichLevel = 0;
	}
	var dispPrefix = "";
	for (var i=0; i<whichLevel; i++) {
		dispPrefix = dispPrefix + "----";
	}

	var recursing = false;
	if (doRecurse != undefined) {
		recursing = doRecurse;
	}
	for (var curProp in whichObj) {
		var curVal = whichObj[curProp];
		if (typeof(curVal) == "object") {
			if (recursing) {
				trace(dispPrefix + "recursing into: " + curProp + " (" + typeof(curVal) + ")");
				printObjectProps(curVal, true, whichLevel + 1);
			} else {
				trace(dispPrefix + curProp + ": " + curVal + " (" + typeof(curVal) + ") - not recursing");
			}
		} else {
			trace(dispPrefix + curProp + ": " + curVal + " (" + typeof(curVal) + ")");
		}
	}
}

var trace = function (whichString) {
	if (document.getElementById('tForm') == "undefined" || document.getElementById('tForm') == undefined) {
		var divWidth = 850;
		var divHeight = 326;
		var docHeight = document.documentElement.clientHeight;
		var docWidth = document.documentElement.clientWidth;
		var formHolder = document.createElement('div');
		formHolder.id = "tFormHolder";
		formHolder.style.zIndex = "1000";
		formHolder.style.background = "#666666";
		formHolder.style.overflow = "hidden";
		formHolder.style.position = "absolute";
		formHolder.style.width = divWidth + "px";
		formHolder.style.height = divHeight + "px";
		formHolder.style.left = 20; //(docWidth - divWidth - 20) + "px";
		formHolder.style.top = (docHeight - divHeight - 20) + "px";
		formHolder.style.color = "#FFFFFF";
		formHolder.style.fontFamily = "Verdana";
		formHolder.style.fontSize = "11px";
		formHolder.style.fontWeight = "bold";
		formHolder.style.paddingTop = "2px";
		formHolder.style.textAlign = "center";
		formHolder.innerHTML = "Trace Window (<a href='javascript:toggleWindow();' style='color:#FFFFFF'>toggle</a>)";
		formHolder.onmousedown = function (event) { startDrag(event, this); };
		formHolder.onmouseup = function () { stopDrag(this); };
		
		var form = document.createElement('form');
		formHolder.appendChild(form);
		form.id = "tForm";
		var field = document.createElement('textarea');
		form.appendChild(field);
		field.rows = 5;
		field.cols = 18;
		field.id = "tText";
		field.style.position = "absolute";
		field.style.left = "1px";
		field.style.top = "20px";
		field.style.width = (divWidth-8) + "px";
		field.style.height = (divHeight-26) + "px";
    
		//document.body.appendChild(formHolder);
		$("body").prepend(formHolder); 
	}
		
	var theField = document.getElementById('tText');
	theField.value = theField.value + "\n" + whichString;
	theField.scrollTop = theField.scrollHeight;
}

var toggleWindow = function () {
	var d = document.getElementById('tFormHolder');
	if (d.style.height == "18px") {
		d.style.height = "326px";
	} else {
		d.style.height = "18px";
	}
}

var startDrag = function(e, whichElem) {
	var curMouseLoc = getMouseXY(e);
	var xOffset = curMouseLoc.x - whichElem.offsetLeft;
	var yOffset = curMouseLoc.y - whichElem.offsetTop;
	document.onmousemove = function(e) {
		var curMouseLoc = getMouseXY(e);
		var newX = curMouseLoc.x - xOffset;
		var newY = curMouseLoc.y - yOffset;
		whichElem.style.left = newX + "px";
		whichElem.style.top = newY + "px";
	}
}

var stopDrag = function(whichElem) {
	document.onmousemove = null;
}

var getMouseXY = function(e) {
	//must have an event (e.g. click, mousedown, mousemove, etc.) passed to it
	var curLoc = {};
	if (isIE) {
		curLoc.x = event.clientX + document.body.scrollLeft;
		curLoc.y = event.clientY + document.body.scrollTop;
	} else {
		curLoc.x = e.pageX;
		curLoc.y = e.pageY;
	}
	return curLoc;
}// ========================================================================
//  XML.ObjTree -- XML source code from/to JavaScript object like E4X
// ========================================================================

if ( typeof(XML) == 'undefined' ) XML = function() {};

//  constructor

XML.ObjTree = function () {
    return this;
};

//  class variables

XML.ObjTree.VERSION = "0.24";

//  object prototype

XML.ObjTree.prototype.xmlDecl = '<?xml version="1.0" encoding="UTF-8" ?>\n';
XML.ObjTree.prototype.attr_prefix = '-';
XML.ObjTree.prototype.overrideMimeType = 'text/xml';

//  method: parseXML( xmlsource )

XML.ObjTree.prototype.parseXML = function ( xml ) {
    var root;
    if ( window.DOMParser ) {
        var xmldom = new DOMParser();
//      xmldom.async = false;           // DOMParser is always sync-mode
        var dom = xmldom.parseFromString( xml, "application/xml" );
        if ( ! dom ) return;
        root = dom.documentElement;
    } else if ( window.ActiveXObject ) {
        xmldom = new ActiveXObject('Microsoft.XMLDOM');
        xmldom.async = false;
        xmldom.loadXML( xml );
        root = xmldom.documentElement;
    }
    if ( ! root ) return;
    return this.parseDOM( root );
};

//  method: parseHTTP( url, options, callback )

XML.ObjTree.prototype.parseHTTP = function ( url, options, callback ) {
    var myopt = {};
    for( var key in options ) {
        myopt[key] = options[key];                  // copy object
    }
    if ( ! myopt.method ) {
        if ( typeof(myopt.postBody) == "undefined" &&
             typeof(myopt.postbody) == "undefined" &&
             typeof(myopt.parameters) == "undefined" ) {
            myopt.method = "get";
        } else {
            myopt.method = "post";
        }
    }
    if ( callback ) {
        myopt.asynchronous = true;                  // async-mode
        var __this = this;
        var __func = callback;
        var __save = myopt.onComplete;
        myopt.onComplete = function ( trans ) {
            var tree;
            if ( trans && trans.responseXML && trans.responseXML.documentElement ) {
                tree = __this.parseDOM( trans.responseXML.documentElement );
            } else if ( trans && trans.responseText ) {
                tree = __this.parseXML( trans.responseText );
            }
            __func( tree, trans );
            if ( __save ) __save( trans );
        };
    } else {
        myopt.asynchronous = false;                 // sync-mode
    }
    var trans;
    if ( typeof(HTTP) != "undefined" && HTTP.Request ) {
        myopt.uri = url;
        var req = new HTTP.Request( myopt );        // JSAN
        if ( req ) trans = req.transport;
    } else if ( typeof(Ajax) != "undefined" && Ajax.Request ) {
        var req = new Ajax.Request( url, myopt );   // ptorotype.js
        if ( req ) trans = req.transport;
    }
//  if ( trans && typeof(trans.overrideMimeType) != "undefined" ) {
//      trans.overrideMimeType( this.overrideMimeType );
//  }
    if ( callback ) return trans;
    if ( trans && trans.responseXML && trans.responseXML.documentElement ) {
        return this.parseDOM( trans.responseXML.documentElement );
    } else if ( trans && trans.responseText ) {
        return this.parseXML( trans.responseText );
    }
}

//  method: parseDOM( documentroot )

XML.ObjTree.prototype.parseDOM = function ( root ) {
    if ( ! root ) return;

    this.__force_array = {};
    if ( this.force_array ) {
        for( var i=0; i<this.force_array.length; i++ ) {
            this.__force_array[this.force_array[i]] = 1;
        }
    }

    var json = this.parseElement( root );   // parse root node
    if ( this.__force_array[root.nodeName] ) {
        json = [ json ];
    }
    if ( root.nodeType != 11 ) {            // DOCUMENT_FRAGMENT_NODE
        var tmp = {};
        tmp[root.nodeName] = json;          // root nodeName
        json = tmp;
    }
    return json;
};

//  method: parseElement( element )

XML.ObjTree.prototype.parseElement = function ( elem ) {
    //  COMMENT_NODE
    if ( elem.nodeType == 7 ) {
        return;
    }

    //  TEXT_NODE CDATA_SECTION_NODE
    if ( elem.nodeType == 3 || elem.nodeType == 4 ) {
        var bool = elem.nodeValue.match( /[^\x00-\x20]/ );
        if ( bool == null ) return;     // ignore white spaces
        return elem.nodeValue;
    }

    var retval;
    var cnt = {};

    //  parse attributes
    if ( elem.attributes && elem.attributes.length ) {
        retval = {};
        for ( var i=0; i<elem.attributes.length; i++ ) {
            var key = elem.attributes[i].nodeName;
            if ( typeof(key) != "string" ) continue;
            var val = elem.attributes[i].nodeValue;
            if ( ! val ) continue;
            key = this.attr_prefix + key;
            if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
            cnt[key] ++;
            this.addNode( retval, key, cnt[key], val );
        }
    }

    //  parse child nodes (recursive)
    if ( elem.childNodes && elem.childNodes.length ) {
        var textonly = true;
        if ( retval ) textonly = false;        // some attributes exists
        for ( var i=0; i<elem.childNodes.length && textonly; i++ ) {
            var ntype = elem.childNodes[i].nodeType;
            if ( ntype == 3 || ntype == 4 ) continue;
            textonly = false;
        }
        if ( textonly ) {
            if ( ! retval ) retval = "";
            for ( var i=0; i<elem.childNodes.length; i++ ) {
                retval += elem.childNodes[i].nodeValue;
            }
        } else {
            if ( ! retval ) retval = {};
            for ( var i=0; i<elem.childNodes.length; i++ ) {
                var key = elem.childNodes[i].nodeName;
                if ( typeof(key) != "string" ) continue;
                var val = this.parseElement( elem.childNodes[i] );
                if ( ! val ) continue;
                if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
                cnt[key] ++;
                this.addNode( retval, key, cnt[key], val );
            }
        }
    }
    return retval;
};

//  method: addNode( hash, key, count, value )

XML.ObjTree.prototype.addNode = function ( hash, key, cnts, val ) {
    if ( this.__force_array[key] ) {
        if ( cnts == 1 ) hash[key] = [];
        hash[key][hash[key].length] = val;      // push
    } else if ( cnts == 1 ) {                   // 1st sibling
        hash[key] = val;
    } else if ( cnts == 2 ) {                   // 2nd sibling
        hash[key] = [ hash[key], val ];
    } else {                                    // 3rd sibling and more
        hash[key][hash[key].length] = val;
    }
};

//  method: writeXML( tree )

XML.ObjTree.prototype.writeXML = function ( tree ) {
    var xml = this.hash_to_xml( null, tree );
    return this.xmlDecl + xml;
};

//  method: hash_to_xml( tagName, tree )

XML.ObjTree.prototype.hash_to_xml = function ( name, tree ) {
    var elem = [];
    var attr = [];
    for( var key in tree ) {
        if ( ! tree.hasOwnProperty(key) ) continue;
        var val = tree[key];
        if ( key.charAt(0) != this.attr_prefix ) {
            if ( typeof(val) == "undefined" || val == null ) {
                elem[elem.length] = "<"+key+" />";
            } else if ( typeof(val) == "object" && val.constructor == Array ) {
                elem[elem.length] = this.array_to_xml( key, val );
            } else if ( typeof(val) == "object" ) {
                elem[elem.length] = this.hash_to_xml( key, val );
            } else {
                elem[elem.length] = this.scalar_to_xml( key, val );
            }
        } else {
            attr[attr.length] = " "+(key.substring(1))+'="'+(this.xml_escape( val ))+'"';
        }
    }
    var jattr = attr.join("");
    var jelem = elem.join("");
    if ( typeof(name) == "undefined" || name == null ) {
        // no tag
    } else if ( elem.length > 0 ) {
        if ( jelem.match( /\n/ )) {
            jelem = "<"+name+jattr+">\n"+jelem+"</"+name+">\n";
        } else {
            jelem = "<"+name+jattr+">"  +jelem+"</"+name+">\n";
        }
    } else {
        jelem = "<"+name+jattr+" />\n";
    }
    return jelem;
};

//  method: array_to_xml( tagName, array )

XML.ObjTree.prototype.array_to_xml = function ( name, array ) {
    var out = [];
    for( var i=0; i<array.length; i++ ) {
        var val = array[i];
        if ( typeof(val) == "undefined" || val == null ) {
            out[out.length] = "<"+name+" />";
        } else if ( typeof(val) == "object" && val.constructor == Array ) {
            out[out.length] = this.array_to_xml( name, val );
        } else if ( typeof(val) == "object" ) {
            out[out.length] = this.hash_to_xml( name, val );
        } else {
            out[out.length] = this.scalar_to_xml( name, val );
        }
    }
    return out.join("");
};

//  method: scalar_to_xml( tagName, text )

XML.ObjTree.prototype.scalar_to_xml = function ( name, text ) {
    if ( name == "#text" ) {
        return this.xml_escape(text);
    } else {
        return "<"+name+">"+this.xml_escape(text)+"</"+name+">\n";
    }
};

//  method: xml_escape( text )

XML.ObjTree.prototype.xml_escape = function ( text ) {
    return String(text).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
};/*
 * Yuichi Tateno. <hotchpotch@N0!spam@gmail.com>
 * http://rails2u.com/
 * 
 * The MIT License
 * --------
 * Copyright (c) 2007 Yuichi Tateno.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

var JSTweener = {
    looping: false,
    frameRate: 60,
    objects: [],
    defaultOptions: {
        time: 1,
        transition: 'easeoutexpo',
        delay: 0,
        prefix: {},
        suffix: {},
        onStart: undefined,
        onStartParams: undefined,
        onUpdate: undefined,
        onUpdateParams: undefined,
        onComplete: undefined,
        onCompleteParams: undefined
    },
    inited: false,
    easingFunctionsLowerCase: {},
    init: function() {
        this.inited = true;
        for (var key in JSTweener.easingFunctions) {
            this.easingFunctionsLowerCase[key.toLowerCase()] = JSTweener.easingFunctions[key];
        }
    },
    toNumber: function(value, prefix, suffix) {
        // for style
        if (!suffix) suffix = 'px';

        return value.toString().match(/[0-9]/) ? Number(value.toString().replace(
                                                        new RegExp(suffix + '$'), ''
                                                       ).replace(
                                                        new RegExp('^' + (prefix ? prefix : '')), ''
                                                       ))
                                               : 0;
    },
    addTween: function(obj, options) {
        var self = this;
        if (!this.inited) this.init();
        var o = {};
        o.target = obj;
        o.targetPropeties = {};
        
        for (var key in this.defaultOptions) {
            if (options[key]) {
                o[key] = options[key];
                delete options[key];
            } else {
                o[key] = this.defaultOptions[key];
            }
        }

        if (typeof o.transition == 'function') {
            o.easing = o.transition;
        } else {
            o.easing = this.easingFunctionsLowerCase[o.transition.toLowerCase()];
        }

        for (var key in options) {
            if (!o.prefix[key]) o.prefix[key] = '';
            if (!o.suffix[key]) o.suffix[key] = '';
            var sB = this.toNumber(obj[key], o.prefix[key],  o.suffix[key]);
            o.targetPropeties[key] = {
                b: sB,
                c: options[key] - sB
            };
        }

        setTimeout(function() {
            o.startTime = (new Date() - 0);
            o.endTime = o.time * 1000 + o.startTime;

            if (typeof o.onStart == 'function') {
                if (o.onStartParams) {
                    o.onStart.apply(o, o.onStartParams);
                } else {
                    o.onStart();
                }
            }

            self.objects.push(o);
            if (!self.looping) { 
                self.looping = true;
                self.eventLoop.call(self);
            }
        }, o.delay * 1000);
    },
    eventLoop: function() {
        var now = (new Date() - 0);
        for (var i = 0; i < this.objects.length; i++) {
            var o = this.objects[i];
            var t = now - o.startTime;
            var d = o.endTime - o.startTime;

            if (t >= d) {
                for (var property in o.targetPropeties) {
                    var tP = o.targetPropeties[property];
                    try {
                        o.target[property] = o.prefix[property] + (tP.b + tP.c) + o.suffix[property];
                    } catch(e) {}
                }
                this.objects.splice(i, 1);

                if (typeof o.onUpdate == 'function') {
                    if (o.onUpdateParams) {
                        o.onUpdate.apply(o, o.onUpdateParams);
                    } else {
                        o.onUpdate();
                    }
                }

                if (typeof o.onComplete == 'function') {
                    if (o.onCompleteParams) {
                        o.onComplete.apply(o, o.onCompleteParams);
                    } else {
                        o.onComplete();
                    }
                }
            } else {
                for (var property in o.targetPropeties) {
                    var tP = o.targetPropeties[property];
                    var val = o.easing(t, tP.b, tP.c, d);
                    try {
                        // FIXME:For IE. A Few times IE (style.width||style.height) = value is throw error...
                        o.target[property] = o.prefix[property] + val + o.suffix[property];
                    } catch(e) {}
                }

                if (typeof o.onUpdate == 'function') {
                    if (o.onUpdateParams) {
                        o.onUpdate.apply(o, o.onUpdateParams);
                    } else {
                        o.onUpdate();
                    }
                }
            }
        }

        if (this.objects.length > 0) {
            var self = this;
            setTimeout(function() { self.eventLoop() }, 1000/self.frameRate);
        } else {
            this.looping = false;
        }
    }
};

JSTweener.Utils = {
    bezier2: function(t, p0, p1, p2) {
         return (1-t) * (1-t) * p0 + 2 * t * (1-t) * p1 + t * t * p2;
    },
    bezier3: function(t, p0, p1, p2, p3) {
         return Math.pow(1-t, 3) * p0 + 3 * t * Math.pow(1-t, 2) * p1 + 3 * t * t * (1-t) * p2 + t * t * t * p3;
    },
    allSetStyleProperties: function(element) {
         var css;
         if (document.defaultView && document.defaultView.getComputedStyle) {
             css = document.defaultView.getComputedStyle(element, null);
         } else {
             css = element.currentStyle
         }
         for (var key in css) {
             if (!key.match(/^\d+$/)) {
                 try {
                 element.style[key] = css[key];
                 } catch(e) {};
             }
         }
    }
}

/*
 * JSTweener.easingFunctions is
 * Tweener's easing functions (Penner's Easing Equations) porting to JavaScript.
 * http://code.google.com/p/tweener/
 */

JSTweener.easingFunctions = {
    easeNone: function(t, b, c, d) {
        return c*t/d + b;
    },    
    easeInQuad: function(t, b, c, d) {
        return c*(t/=d)*t + b;
    },    
    easeOutQuad: function(t, b, c, d) {
        return -c *(t/=d)*(t-2) + b;
    },    
    easeInOutQuad: function(t, b, c, d) {
        if((t/=d/2) < 1) return c/2*t*t + b;
        return -c/2 *((--t)*(t-2) - 1) + b;
    },    
    easeInCubic: function(t, b, c, d) {
        return c*(t/=d)*t*t + b;
    },    
    easeOutCubic: function(t, b, c, d) {
        return c*((t=t/d-1)*t*t + 1) + b;
    },    
    easeInOutCubic: function(t, b, c, d) {
        if((t/=d/2) < 1) return c/2*t*t*t + b;
        return c/2*((t-=2)*t*t + 2) + b;
    },    
    easeOutInCubic: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutCubic(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInCubic((t*2)-d, b+c/2, c/2, d);
    },    
    easeInQuart: function(t, b, c, d) {
        return c*(t/=d)*t*t*t + b;
    },    
    easeOutQuart: function(t, b, c, d) {
        return -c *((t=t/d-1)*t*t*t - 1) + b;
    },    
    easeInOutQuart: function(t, b, c, d) {
        if((t/=d/2) < 1) return c/2*t*t*t*t + b;
        return -c/2 *((t-=2)*t*t*t - 2) + b;
    },    
    easeOutInQuart: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutQuart(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInQuart((t*2)-d, b+c/2, c/2, d);
    },    
    easeInQuint: function(t, b, c, d) {
        return c*(t/=d)*t*t*t*t + b;
    },    
    easeOutQuint: function(t, b, c, d) {
        return c*((t=t/d-1)*t*t*t*t + 1) + b;
    },    
    easeInOutQuint: function(t, b, c, d) {
        if((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
        return c/2*((t-=2)*t*t*t*t + 2) + b;
    },    
    easeOutInQuint: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutQuint(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInQuint((t*2)-d, b+c/2, c/2, d);
    },    
    easeInSine: function(t, b, c, d) {
        return -c * Math.cos(t/d *(Math.PI/2)) + c + b;
    },    
    easeOutSine: function(t, b, c, d) {
        return c * Math.sin(t/d *(Math.PI/2)) + b;
    },    
    easeInOutSine: function(t, b, c, d) {
        return -c/2 *(Math.cos(Math.PI*t/d) - 1) + b;
    },    
    easeOutInSine: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutSine(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInSine((t*2)-d, b+c/2, c/2, d);
    },    
    easeInExpo: function(t, b, c, d) {
        return(t==0) ? b : c * Math.pow(2, 10 *(t/d - 1)) + b - c * 0.001;
    },    
    easeOutExpo: function(t, b, c, d) {
        return(t==d) ? b+c : c * 1.001 *(-Math.pow(2, -10 * t/d) + 1) + b;
    },    
    easeInOutExpo: function(t, b, c, d) {
        if(t==0) return b;
        if(t==d) return b+c;
        if((t/=d/2) < 1) return c/2 * Math.pow(2, 10 *(t - 1)) + b - c * 0.0005;
        return c/2 * 1.0005 *(-Math.pow(2, -10 * --t) + 2) + b;
    },    
    easeOutInExpo: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutExpo(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInExpo((t*2)-d, b+c/2, c/2, d);
    },    
    easeInCirc: function(t, b, c, d) {
        return -c *(Math.sqrt(1 -(t/=d)*t) - 1) + b;
    },    
    easeOutCirc: function(t, b, c, d) {
        return c * Math.sqrt(1 -(t=t/d-1)*t) + b;
    },    
    easeInOutCirc: function(t, b, c, d) {
        if((t/=d/2) < 1) return -c/2 *(Math.sqrt(1 - t*t) - 1) + b;
        return c/2 *(Math.sqrt(1 -(t-=2)*t) + 1) + b;
    },    
    easeOutInCirc: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutCirc(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInCirc((t*2)-d, b+c/2, c/2, d);
    },    
    easeInElastic: function(t, b, c, d, a, p) {
        var s;
        if(t==0) return b;  if((t/=d)==1) return b+c;  if(!p) p=d*.3;
        if(!a || a < Math.abs(c)) { a=c; s=p/4; } else s = p/(2*Math.PI) * Math.asin(c/a);
        return -(a*Math.pow(2,10*(t-=1)) * Math.sin((t*d-s)*(2*Math.PI)/p )) + b;
    },    
    easeOutElastic: function(t, b, c, d, a, p) {
        var s;
        if(t==0) return b;  if((t/=d)==1) return b+c;  if(!p) p=d*.3;
        if(!a || a < Math.abs(c)) { a=c; s=p/4; } else s = p/(2*Math.PI) * Math.asin(c/a);
        return(a*Math.pow(2,-10*t) * Math.sin((t*d-s)*(2*Math.PI)/p ) + c + b);
    },    
    easeInOutElastic: function(t, b, c, d, a, p) {
        var s;
        if(t==0) return b;  if((t/=d/2)==2) return b+c;  if(!p) p=d*(.3*1.5);
        if(!a || a < Math.abs(c)) { a=c; s=p/4; }       else s = p/(2*Math.PI) * Math.asin(c/a);
        if(t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin((t*d-s)*(2*Math.PI)/p )) + b;
        return a*Math.pow(2,-10*(t-=1)) * Math.sin((t*d-s)*(2*Math.PI)/p )*.5 + c + b;
    },    
    easeOutInElastic: function(t, b, c, d, a, p) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutElastic(t*2, b, c/2, d, a, p);
        return JSTweener.easingFunctions.easeInElastic((t*2)-d, b+c/2, c/2, d, a, p);
    },    
    easeInBack: function(t, b, c, d, s) {
        if(s == undefined) s = 1.70158;
        return c*(t/=d)*t*((s+1)*t - s) + b;
    },    
    easeOutBack: function(t, b, c, d, s) {
        if(s == undefined) s = 1.70158;
        return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
    },    
    easeInOutBack: function(t, b, c, d, s) {
        if(s == undefined) s = 1.70158;
        if((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
        return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
    },    
    easeOutInBack: function(t, b, c, d, s) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutBack(t*2, b, c/2, d, s);
        return JSTweener.easingFunctions.easeInBack((t*2)-d, b+c/2, c/2, d, s);
    },    
    easeInBounce: function(t, b, c, d) {
        return c - JSTweener.easingFunctions.easeOutBounce(d-t, 0, c, d) + b;
    },    
    easeOutBounce: function(t, b, c, d) {
        if((t/=d) <(1/2.75)) {
            return c*(7.5625*t*t) + b;
        } else if(t <(2/2.75)) {
            return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
        } else if(t <(2.5/2.75)) {
            return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
        } else {
            return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
        }
    },    
    easeInOutBounce: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeInBounce(t*2, 0, c, d) * .5 + b;
        else return JSTweener.easingFunctions.easeOutBounce(t*2-d, 0, c, d) * .5 + c*.5 + b;
    },    
    easeOutInBounce: function(t, b, c, d) {
        if(t < d/2) return JSTweener.easingFunctions.easeOutBounce(t*2, b, c/2, d);
        return JSTweener.easingFunctions.easeInBounce((t*2)-d, b+c/2, c/2, d);
    }
};
JSTweener.easingFunctions.linear = JSTweener.easingFunctions.easeNone;

var SkedManager = Class.create({
	initialize: function() {
		this._parent = parent;
		this._element = $prototype('daily-sked-options');
		
		this.setDeepLinkProps();
		this.initLinks();
		this.initView();
	},
	setDeepLinkProps: function() {
		//the defaults below are overridden if a valid time/date is passed in the query string
		//format is http://rsndev.pairsite.com/tvschedule/?date=20090905&time=2030
		//date: YYYYMMDD
		//time: [H]HMM
		window.deepLinkDate = -1;
		window.deepLinkTime = -1;
		
		var qParams = this.parseQuery();
		
		if (qParams.date != undefined) {
			for (var i = 0; i < skedDates.length; i++) {
				if (qParams.date == skedDates[i].xmlFileDate) {
					window.deepLinkDate = qParams.date;
					break;
				}
			}
		}
		if (qParams.time != undefined && (qParams.time.length == 3 || qParams.time.length == 4)) {
			var hour, min;
			if (qParams.time.length == 3) {
				hour = parseInt(qParams.time.substr(0, 1));
				min = parseInt(qParams.time.substr(1, 2));
			}
			else {
				hour = parseInt(unpadValue(qParams.time.substr(0, 2)));
				min = parseInt(qParams.time.substr(2, 2));
			}
			if (!isNaN(hour) && !isNaN(min)) {
				hour = Math.min(Math.max(hour,0), 24); //make sure it's between 0 and 24
				if (hour == 24) hour = 0; //we're 0-based here folks
				hour = padValue(hour); //turn something like 0 into 00
				min = Math.min(Math.max(min,0), 59); //make sure it's between 0 and 59
				min = padValue(Math.round(min / 30) % 2 * 30); //round to :00 or :30
				
				window.deepLinkTime = hour + min;
			}
		}
		//trace("deepLinkDate: " + deepLinkDate);
		//trace("deepLinkTime: " + deepLinkTime);
	},
	parseQuery: function(qs,options) {
		var q = (typeof qs === 'string'?qs:window.location.search), o = {'f':function(v){return unescape(v).replace(/\+/g,' ');}}, options = (typeof qs === 'object' && typeof options === 'undefined')?qs:options, o = jQuery.extend({}, o, options), params = {};
		jQuery.each(q.match(/^\??(.*)$/)[1].split('&'),function(i,p){
			p = p.split('=');
			p[1] = o.f(p[1]);
			params[p[0]] = params[p[0]]?((params[p[0]] instanceof Array)?(params[p[0]].push(p[1]),params[p[0]]):[params[p[0]],p[1]]):p[1];
		});
		return params;
	},
	initView: function() {
		this.curView = "grid"; //fullDay
		
		//get cookie - jquery here
		var c = $.cookie('sn_skedView');
		if ((c != null) && (c == "fullDay"))
			this.curView = "fullDay";
		
		if (window.skedMgr == undefined) {
			window.skedMgr = new GridView();
			window.skedMgr._element.setOpacity(0);
			
			document.observe("skedMgr:onSkedDataReady", delegate.create(this, "onSkedDataReady"));
			
			dataMgr.loadSkedXML();
		}
	},
	onSkedDataReady: function() {
//		if (this._skedFullDay != undefined) {
//			this._skedFullDay.die();
//			this._skedFullDay = undefined;
//		}
		this.updateDisplay();
	},
	setView: function(w) {
		this.curView = w;
		$.cookie('sn_skedView', this.curView);
		
		this.updateDisplay();
	},
	updateDisplay: function(str) {
		this.setButtonStates();
		this.setSkedView();
	},
	setSkedView: function(){
		if (this.curView == "fullDay") {
			window.skedMgr.hide();
			if (this._skedFullDay == undefined) {
				var initObj = {
					id: "skedFullDay"
				};
				this._skedFullDay = new DailyView(initObj);
			}
			else {
				this._skedFullDay._element.show();
			}
		}
		else {
			if (this._skedFullDay != undefined) this._skedFullDay._element.hide();
			window.skedMgr.show();
			window.skedMgr._element.setOpacity(1);
		}
	},
	onTimeLinkClick: function(event){
		var targetTime = "2000"; //default to primetime (8PM)
		if (event.target.id == "ctLink")
			targetTime = this._ctLinkTimeID;

		if (this.curView == "fullDay")
			this._skedFullDay._element.select("[id='"+targetTime+"']")[0].scrollTo();
		else
			skedMgr._timeTable.snapToTime(targetTime);
		
		return false;
	},
	initLinks: function(){
		var timeLinkDlgt = delegate.create(this, "onTimeLinkClick");
		//prime/current time shortcuts
		this._ptLink = $('#ptLink');
		this._ptLink.click(timeLinkDlgt);
		
		this._ctLink = $('#ctLink');
		this.initCurrentTimeLink();
		this._ctLink.click(timeLinkDlgt);
		
		//views
		//we use jquery here because prototype's observe doesn't return false back thru stack (so # links still fire and make the page scroll to top)
		this._gridLink = $('#option-grid');
		this._fullDayLink = $('#option-full-day');
		this._filterLeague = $('#filter-league');
		this._filterTeam = $('#filter-team');
		this._gridLink.clickFunc = this._fullDayLink.clickFunc = delegate.create(this, "onLinkClick");
	},
	initCurrentTimeLink: function(){
		var curTime = new Date();
		var curHours = padValue(curTime.getHours().toString());
		var curMinutes = curTime.getMinutes();
		var roundMinutes = padValue((curMinutes - (curMinutes % 30)).toString());
		
		var amPm = curHours > 11 ? "PM" : "AM";
		var dTime = curHours + ":" + roundMinutes + " " + amPm;
		
		var tZone = dataMgr.getTimeZone("short");
		
		var displayDateTemplate = new Template('Current Time (#{time} #{zone})');
		var dText = displayDateTemplate.evaluate({time:dTime, zone:tZone});
		
		this._ctLinkTimeID = curHours + roundMinutes; //e.g. - 1100 for 11AM
		this._ctLink.html(dText); //jquery
	},
	onLinkClick: function(evt){
		var newView = (evt.target.id == "option-full-day") ? "fullDay" : "grid";
		this.setView(newView);
		return false;
	},
	setButtonStates: function() {
		this.deactivateBtn(this._gridLink);
		this.deactivateBtn(this._fullDayLink);
		
		if (this.curView == "fullDay") {
			this.activateBtn(this._gridLink);
			this._filterLeague.show();
		}
		else {
			this.activateBtn(this._fullDayLink);
			this._filterLeague.hide();
			this._filterTeam.hide();
		}
	},
	activateBtn: function(b) {
		b.click(b.clickFunc);
		b.removeClass("on");
	},
	deactivateBtn: function(b) {
		b.addClass("on");
		b.unbind();
	},
	debug: function(str) {
		var _debug = false;
		if (_debug == true) {
			try {
				trace(str);
			} 
			catch (e) {
				alert(str);
			}
		}
	}
});var DataManager = Class.create({
	initialize: function() {
		var uR = $.cookie('sn_region');//this.getCookie("region");
		if (uR != null && uR != undefined) {
			this._REGION = uR != "" ? uR.toLowerCase() : "ontario";
		} else {
			this._REGION = "ontario";
		}
		
		this.setTimeZone();

		this.isIE6 = Prototype.Browser.IE && (navigator.appVersion.indexOf("MSIE 6.") != -1);
		
		this.initted = false; //used to ignore first date XML load so it's not tracked as user action
		
		this._XML_PATH = "publish/xml/";
		
		this._MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
		this._MONTHS_TRUNCATED = ["Jan.","Feb.","Mar.","Apr.","May","June","July","Aug.","Sept.","Oct.","Nov.","Dec."];
		this._DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
	},
	getTimeZone: function(type) {
		if (type == "short") 
			return this.timeZoneAbbr;
		else
			return this.timeZone;
	},
	setTimeZone: function() {
		this.timeZone = "Eastern Time"; //ontario and east both use this, so no cases are used
		switch (this._REGION.toLowerCase()) {
			case "pacific" : {
				this.timeZone = "Pacific Time";
				break;
			}
			case "west" : {
				this.timeZone = "Mountain Time";
				break;
			}
		}
		
		//set short labels - need to know if we're in DST for this
		var dstChar = this.getDST() == 1 ? "D" : "S";
		this.timeZoneAbbr = this.timeZone.substr(0,1) + dstChar + "T"; //ontario and east both use this, so no cases are used
	},
	getDST:function() {
		//original getDST() script (calculate_time_zone()) by Josh Fraser (http://www.onlineaspect.com)
		var rightNow = new Date();
		var jan1 = new Date(rightNow.getFullYear(), 0, 1, 0, 0, 0, 0);  // jan 1st
		var june1 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0); // june 1st
		var temp = jan1.toGMTString();
		var jan2 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
		temp = june1.toGMTString();
		var june2 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
		var std_time_offset = (jan1 - jan2) / (1000 * 60 * 60);
		var daylight_time_offset = (june1 - june2) / (1000 * 60 * 60);
		var dst;
		if (std_time_offset == daylight_time_offset) {
			dst = "0"; // daylight savings time is NOT observed
		} else {
			// positive is southern, negative is northern hemisphere
			var hemisphere = std_time_offset - daylight_time_offset;
			if (hemisphere >= 0)
				std_time_offset = daylight_time_offset;
			dst = "1"; // daylight savings time is observed
		}
		return dst;
	},
	getRegion: function() {
		return this._REGION;
	},
	getTruncatedMonth: function(m) {
		return this._MONTHS_TRUNCATED[this._MONTHS.indexOf(m)];
	},
	loadSkedXML: function(fName) {
		var casedName = this._REGION.toUpperCase(); //in case the format of region changes
		//accepts a string like "ONTARIO_20081231" and loads the XML file of that name (e.g, "ONTARIO_20081231.xml")
		if (fName == undefined) {
			//if none is passed, it gets the ID for hilited date
			fName = casedName + "_" + skedMgr.getSelectedDateID();
		} else if (fName.indexOf("_") == -1) {
			//if region's not included, prepend it
			fName = casedName + "_" + fName;
		}
		
		//need to load every time since updating the elements is dependent on them being visible, for measurements
		//if (fName != this.curFileID) {
			this.curFileID = fName;
			
			this.curDate = this.getDateFromFileName(fName);
			this.curDisplayDate = this.getDisplayDate(this.curDate);
			statusMgr._setStatus('Loading schedule data for ' + this.curDisplayDate + '...');
			
			var url = this._XML_PATH + fName + ".xml?r=" + Math.random(); //defeat caching;
			var aRequest = new Ajax.Request(url, {method:"get", onSuccess:delegate.create(this, "doXMLLoadSuccess"), onFailure:delegate.create(this, "initXMLLoadError")});
		
			var eventsUrl = this._XML_PATH + "events_" + fName + ".html?r=" + Math.random(); //defeat caching;
			var eventsRequest = new Ajax.Request(eventsUrl, {method:"get", onSuccess:delegate.create(this, "doEventsLoadSuccess"), onFailure:delegate.create(this, "doEventsLoadError")});
		
			if (this.initted == true) {
				skedMain.doOmnitureCall('TVSchedDateSelect');
			} else {
				this.initted = true;
			}
		//}// else {
		//	trace("already loaded");
		//}
	},
	doEventsLoadSuccess: function(aResponse){
		$("#live_events_table_holder").html(aResponse.responseText);
		$("#live_events_holder").show();
	},
	doEventsLoadError: function(aResponse){
		//alert("doEventsLoadError; aResponse.statusText: " + aResponse.statusText);
		$("#live_events_holder").hide();
	},
	doXMLLoadSuccess: function(aResponse) {
		//parse XML to object
		var xotree = new XML.ObjTree();
		xotree.attr_prefix = '@'; //XML attributes will be prepended with this character
		var tree = xotree.parseXML(aResponse.responseText);
		var c = dataMgr.sortChannelDataByRegion(tree.channels.channel);
		document.fire("dataMgr:onDataComplete", { channelsData: c });
	},
	initXMLLoadError: function() {
		var d = setTimeout(delegate.create(this, "doXMLLoadError"), 500);
	},
	doXMLLoadError: function() {
		var lM = 'Sorry. We seem to be having some technical difficulties.<br/><br/>';
		lM += 'You can <a href="#" onclick="closeSked(); return false;">close the schedule</a>, <a href="#" onclick="reloadSked(); return false;">try again</a>';
		lM += ' or ';
		lM += '<a href="#" onclick="openVideoPopup(); closeSked(); return false;">go watch some videos</a>.';
		
		statusMgr._setStatus(lM, false);
	},
	getDisplayDate: function(wDate) {
		//turns a date object into a formatted string like "Tuesday July 8"
		return (this._DAYS[wDate.getDay()] + " " + this._MONTHS[wDate.getMonth()] + " " + wDate.getDate());
	},
	getXMLFileName: function(dt) {
		//turns a date object into a formatted string like "20080607"
		var y = dt.getFullYear();
		var m = padValue((dt.getMonth() + 1).toString()); //add 1 since month is zero-based
		var d = padValue(dt.getDate().toString());
		var _d = y + m + d;
		return _d;
	},
	getDateFromFileName: function(wID) {
		//turns something like "ONTARIO_20080607" or "20080607" into a Date object
		if (wID.indexOf("_") != -1) {
			wID = wID.split("_")[1];
		}
		var y = wID.substr(0,4);
		var m = wID.substr(4,2);
		if (m.substr(0,1) == "0") m = m.substr(1,1);
		var d = wID.substr(6,2);
		if (d.substr(0,1) == "0") d = d.substr(1,1);
		return new Date(parseInt(y), (parseInt(m) - 1), parseInt(d));
	},
	sortChannelDataByRegion: function(channelsData) {
		//alert("sortChannelDataByRegion");
		//sort the sort order so the user's region goes to the top
		var userRegion = this._REGION.toLowerCase();
		var sortOrder = ["pacific", "west", "ontario", "east", "one", "world"];
		//alert("b4 sortOrder: " + sortOrder);
		var regIdx = 0;
		for (var x=0; x<sortOrder.length; x++) {
			var curReg = sortOrder[x];
			if (curReg == userRegion) {
				//alert("found region: " + curReg);
				regIdx = x;
				break;
			}
		}
		//if it's in the first slot, no changes necessary
		if (regIdx != 0) {
			var firstReg = sortOrder.splice(regIdx, 1)[0];
			sortOrder.unshift(firstReg);
		}
		//alert("aft sortOrder: " + sortOrder);
		
		//sorts the data object to match the region order
		var chanIdx = 0;
		var sortedChannelsData = new Array();
		for (var x=0; x<sortOrder.length; x++) {
			var curReg = sortOrder[x];
			for (var y=0; y<channelsData.length; y++) {
				var curChanID = channelsData[y]["@id"].toLowerCase();
				if (curChanID == curReg) {
					sortedChannelsData.push(channelsData.splice(y, 1)[0]);
					break;
				}
			}
		}
		return sortedChannelsData;
	},
	setCookieArray: function(name, arr){
		var cString = arr.join(',');
		return this.setCookie(name, cString);
	},
	getCookieArray: function(name){
		//returns empty array if no cookie
		var c = this.getCookie(name);
		var arr = new Array();
		if (c != null && c != undefined) {
			arr = c.split(",");
		}
		return arr;
	},
	setCookieObject: function(name, obj){
		return this.setCookie(name, this.objToString(obj));
	},
	getCookieObject: function(name){
		//returns empty object if no cookie
		var c = this.getCookie(name);
		var obj = new Object();
		if (c != null && c!= undefined) {
			obj = this.stringToObj(c);
		}
		return obj;
	},
	getCookie: function(name){
		//returns null if unable to get cookie
		var cString;
		try {
			var iStart = document.cookie.indexOf(name + "=");
			var iLength = iStart + name.length + 1;
			if ((iStart == -1) || (!iStart && (name == document.cookie.substring(0)))) {
				cString = null;
			} else {
				var iEnd = document.cookie.indexOf(";", iLength);
				if( iEnd == -1 ) {
					iEnd = document.cookie.length;
				}
				cString = unescape(document.cookie.substring(iLength, iEnd));
			}
		} catch(e) {
			cString = null;
		}
		return cString;
	},
	setCookie: function(name, value, secure){
		var status;
		try {
			//allow for testing on other servers
			var eD = new Date();
			eD.setTime(eD.getTime()+(365*24*60*60*1000));
			var expires = eD.toGMTString();
			var domain;
			if (location.host.indexOf("sportsnet.ca") != -1) {
				domain = ".sportsnet.ca;";
			}
			//if domain/secure aren't set, we don't write them
			var cString = name + '=' + escape(value) + ';expires=' + expires + ';path=/' + ((domain)?';domain='+domain:'') + ((secure)?';secure':'');
			document.cookie = cString;
			//check to make sure it was written (turning off cookies doesn't throw any errors when you try to write them)
			var c = this.getCookie(name);
			var status = (c!=null && c!=undefined) ? "success" : "error";
		} catch(e) {
			status = "error";
		}
		return status;
	},
	deleteCookie: function(name, path, domain){
		var status;
		var domain;
		if (location.host.indexOf("sportsnet.ca") != -1) {
			domain = ".sportsnet.ca;";
		}
		var c = this.getCookie(name);
		if (c != undefined && c!= null) {
			document.cookie = name + '=' + ';expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/' + ((domain)?';domain='+domain:'');
			status = "success";
		}
		return status;
	},
	objToString: function(obj){
		var str = "";
		for(var prop in obj) {
			if (str.length > 0) {
				str += "&";
			}
			str += prop + ":" + escape(obj[prop]); 
		}
		return str;
	},
	stringToObj: function(str){
		var obj = new Object();
		var a = str.split("&");
		for( var i=0; i < a.length; i++ ) a[i] = a[i].split(":");
		for( var i=0; i < a.length; i++ ) obj[a[i][0]] = unescape(a[i][1]);
		return obj;
	}
});var GridView = Class.create({
	initialize: function() {
		this.setDisplayProps();
		
		this.pgBtnKillWait = 500; //milliseconds to wait before closing on mouseout
		this.pgBtnTimer = null;
		
		this.skedRows = new Array();
		this.skedCells = new Array();
		
		this._container = $prototype('skedGridHolder');
		this._element = $prototype('skedMaster');
		this._element.show();
		
		this._dateDropdown = new DateDropdown();
		this._timeTable = new TimeTable(this);
		this._skedRowsHolder = $prototype('skedRowsHolder');
		this._skedRowsHolder.START_X = this._skedRowsHolder.cumulativeOffset().left;
		
		document.observe("dataMgr:onDataComplete", delegate.create(this, "onDataComplete"));
		document.observe("dateDropdown:onDateSelect", delegate.create(this, "onDateSelect"));
		document.observe("timeTable:onTimeScroll", delegate.create(this, "onTimeScroll"));
	},
	hide: function() {
		this._container.hide();
	},
	show: function() {
		this._container.show();
		
		statusMgr._setStatus('Drawing schedule grid for ' + dataMgr.curDisplayDate + '...');
		var d = setTimeout(delegate.create(this, "doDateUpdate"), 100);
	},
	onDateSelect: function(event) {
		//used for both dropdown and date table cell selection
		dataMgr.loadSkedXML(event.memo.id);
	},
	getSelectedDateID: function() {
		return this._dateDropdown.getSelectedDateID();
	},
	onDataComplete: function(event) {
		statusMgr._setStatus('Drawing schedule grid for ' + dataMgr.curDisplayDate + '...');
		this.channelsData = event.memo.channelsData;
		document.fire("skedMgr:onSkedDataReady");
		var d = setTimeout(delegate.create(this, "doDateUpdate"), 100);
	},
	setDisplayProps: function() {
		this.TIME_INCREMENT = 30;
		this.ROW_HEIGHT = 45;
		this.NUM_COLUMNS = 9;
		this.COLUMN_WIDTH = 94;
		this.VISIBLE_WIDTH = this.NUM_COLUMNS * this.COLUMN_WIDTH;
		this.DO_SLIDE = false; //use transition effects?
		this.SLIDE_DUR = .2;
		this.FIRST_CELL_DEFAULT_PADDING = '5px'
		this.FIRST_CELL_BUTTON_PADDING = '19px';
		this.LAST_CELL_OFFSET = 7; //0-based (because it's used to access an element in siblings array) count of cells from first to last (used to determine the last time showing for clipping show cells)
		
		this.PREPEND_TEXT = '<span class="pre_appendText" onclick="document.fire(\'cell:scrollArrowClicked\', {dir:\'left\'}); return false;">&laquo;&nbsp;</span>';
		this.APPEND_TEXT = '<span class="pre_appendText" onclick="document.fire(\'cell:scrollArrowClicked\', {dir:\'right\'}); return false;">&nbsp;&raquo;</span>';
	},
	reorderLogos: function() {
		if (this.logosInOrder == undefined) {
			this.logosInOrder = true;
			for (var x=0; x<this.channelsData.length; x++) {
				var curChanID = this.channelsData[x]["@id"].toLowerCase();
				var curLogo = $prototype('logoHolder').select('[id="' + curChanID + 'Logo"]')[0].remove();
				curLogo.firstDescendant().style.display = "block";
				$prototype('logoHolder').appendChild(curLogo);
			}
		}
	},
	doDateUpdate: function() {
		this.clearCurrentData();
		this.reorderLogos();
		this.drawSkedGrid();
		this.onTimeScroll();
		
		statusMgr._hideStatus();
		document.fire("skedMgr:onSkedReady");
	},
	clearCurrentData: function() {
		while (this.skedRows.length > 0) {
			this._skedRowsHolder.removeChild(this.skedRows.pop());
		}
	},
	onTimeScroll: function() {
		//sets the widths of the cells so they scale/wrap content
		//shows that are "clipped" on either end of the schedule have their titles pre-/appended with "<<" or ">>"
		var curDispLeft = Math.abs(this._skedRowsHolder.offsetLeft);
		var curDispRight = curDispLeft + this.VISIBLE_WIDTH;
		for (var i=0; i<this.skedCells.length; i++) {
			//check to see if the current cell is in the display bounds
			var curCell = this.skedCells[i];
			var curContent = curCell.firstDescendant();
			var firstTimeIntersects = (curDispLeft > curCell.START_X) && (curDispLeft < curCell.END_X);
			var lastTimeIntersects = (curDispRight > curCell.START_X) && (curDispRight < curCell.END_X);
			
			var xOffset;
			var tWidth;
			//set the x/width of the child content divs to keep it in visible bounds
			if (firstTimeIntersects) {
				//trace("found first intersect; curCell: " + curCell.displayText);
				curContent.innerHTML = this.PREPEND_TEXT + curCell.displayText;
				xOffset = this._timeTable.firstCell.START_X - curCell.START_X;
				tWidth = curCell.DEF_WIDTH - xOffset;
			} else if (lastTimeIntersects) {
				//trace("found last intersect; curCell: " + curCell.displayText);
				curContent.innerHTML = curCell.displayText + this.APPEND_TEXT;
				xOffset = 0;
				var wDiff = curCell.END_X - this._timeTable.lastCell.END_X;
				tWidth = curCell.DEF_WIDTH - wDiff;
			} else {
				//if there's no intersect, just "reset" the width/position
				curContent.innerHTML = curCell.displayText;
				xOffset = 0;
				tWidth = curCell.DEF_WIDTH;
			}
			//subtract the left/right padding and left border of the content div
			tWidth -= 11;
			//trace(curCell.displayText + "; xOffset: " + xOffset + "; tWidth: " + tWidth);
			curContent.setStyle({
				left: xOffset + 'px',
				width: tWidth + 'px'
			});
		}
	},
	drawSkedGrid: function() {
		//shift container DIV to align to position of time row (so shows line up with their times)
		this._skedRowsHolder.style.left = this._timeTable._element.style.left;
		this.skedRows = new Array();
		this.skedCells = new Array();
		var chData = this.channelsData;
		for (var i=0; i<chData.length; i++) {
			var curChan = chData[i];
			var curRegion = curChan["@id"];
			var programs = curChan.program;
			
			var chRowHolder = document.createElement('div');
			chRowHolder.id = curChan["@id"] + "RowHolder";
			chRowHolder.className = "skedRow";
			this._skedRowsHolder.appendChild(chRowHolder);
			this.skedRows.push(chRowHolder);
			
			var chTable = chRowHolder.appendChild(document.createElement('table'));
			chTable.border = 0;
			chTable.cellSpacing = 0;
			chTable.cellPadding = 0;
			var chBody = chTable.appendChild(document.createElement('tbody'));
			var chRow = chBody.appendChild(document.createElement('tr'));
			var skedHolderOffsetLeft = this._skedRowsHolder.cumulativeOffset().left;//pL = this._skedRowsHolder.cumulativeOffset().left;
			var curOffsetLeft = skedHolderOffsetLeft;
			for (var j=0; j<programs.length; j++) {
				var curProg = programs[j];
				var isNull = curProg["@isNull"]; //for slots that returned no show info
				var titleID = curProg["@titleID"];
				var imgSrc = curProg["@img"] != undefined ? curProg["@img"] : "";
				var startTime = curProg["@startTime"];
				var endTime = curProg["@endTime"];
				var SeriesName = curProg["SeriesName"];
				var Title = curProg["Title"] || "";
				var SchdPlot = curProg["SchdPlot"] || "";
				var duration = this.getDuration(startTime, endTime);
				if (duration < 0) {
					//sometimes shows with no info have end times after their start times... yeah
					duration = 30;
				}
				var blockSize = duration / this.TIME_INCREMENT;
				//round blocksize to the nearest 2 minute increment (ensures we end up on 30 minute blocks when we have odd times like 5/15 minute shows)
				blockSize = (.02 * Math.ceil(blockSize/.02));
				
				var progCell = chRow.appendChild(document.createElement('td'));
				
				var progDivID = i.toString() + j.toString();
				var progDiv = progCell.appendChild(new Element('div', {'class': 'sked-container', id: progDivID}));
				progDiv._index = j;
				progDiv.isNull = isNull;
				progDiv.displayText = SeriesName;
				//generic
				var curWidth = parseInt(this.COLUMN_WIDTH * blockSize);
				progDiv.DEF_WIDTH = curWidth;
				progDiv.START_X = curOffsetLeft - skedHolderOffsetLeft;
				progDiv.END_X = progDiv.START_X + curWidth;
				//trace("curWidth: " + curWidth);
				progDiv.style.width = curWidth + "px";
				curOffsetLeft += curWidth;
				if (progDiv.isNull) {
					progDiv.addClassName('noInfo');
				} else {
					progDiv.region = curRegion;
					progDiv.id = curRegion + "|" + titleID + "|" + startTime;
					progDiv.titleID = titleID;
					progDiv.imgSrc = imgSrc;
					progDiv.startTime = startTime;
					progDiv.endTime = endTime;
					progDiv.SeriesName = SeriesName;
					progDiv.Title = Title;
					progDiv.SchdPlot = SchdPlot;
					progDiv.duration = duration;
					progDiv.blockSize = blockSize;
					if (Title != "") progDiv.displayText += ": " + Title;
				}
				
				var contentDiv = $prototype(progDiv.appendChild(document.createElement('div')));
				contentDiv.className = "content";
				//we explicitly set the style to defeat some weirdness in IE6
				var widthAdjustedForBorderAndPadding = curWidth - 11;
				contentDiv.setStyle({
					width: widthAdjustedForBorderAndPadding + 'px'
				});
				contentDiv.appendChild(document.createTextNode(SeriesName));

				this.skedCells.push(progDiv);
			}
		}
	},
	getDuration: function(start, end) {
		//basically subtracts the start time from the end time, accounting for pre/post-midnight, etc.
		var sA = start.split(":");
		sH = parseFloat(sA[0]);
		sM = parseFloat(sA[1]);
		var eA = end.split(":");
		eH = parseFloat(eA[0]);
		eM = parseFloat(eA[1]);
		
		var tDate = 1;
		if (eH < sH) {
			tDate = 2;
		}
		var sDate = new Date(2008, 0, 1, sH, sM);
		var eDate = new Date(2008, 0, tDate, eH, eM);
		
		var diff = ((eDate - sDate) / 1000) / 60;
		return diff;
	},
	initKillPgBtn: function() {
		clearTimeout(this.pgBtnTimer);
		this.pgBtnTimer = setTimeout(delegate.create(this, "hidePgBtn"), this.pgBtnKillWait);
	},
	showPgBtn: function(whichBtn) {
		clearTimeout(this.pgBtnTimer);
		if (this.activePgBtn != undefined) {
			if (this.activePgBtn != whichBtn) this.activePgBtn.hide();
		}
		this.activePgBtn = whichBtn;
		
		
		var lPos = this.activePgBtn._targetBtn.cumulativeOffset();
		var lTop = lPos.top;
		var lLeft;
		if (this.activePgBtn._targetBtnSide == "left") {
			lLeft = lPos.left - 16; //16 is width of page scroll button
		}
		else {
			lLeft = lPos.left + 12; //12 is width of scroll button
		}
		this.activePgBtn.style.top = lTop + "px";
		this.activePgBtn.style.left = lLeft + "px";
		
		this.activePgBtn.show();
	},
	hidePgBtn: function() {
		clearTimeout(this.pgBtnTimer);
		this.activePgBtn.hide();
	},
	enableScrollButton: function(which) {
		var b = which;
		b.removeClassName("off");
		b.addClassName("on");
		b.onclick = b.doClick;
		b.onmouseover = b.doMouseover;
		b.onmouseout = b.doMouseout;
	},
	disableScrollButton: function(which) {
		var b = which;//$prototype(which);
		b.removeClassName("on");
		b.addClassName("off");
		b.onclick = null;
		b.onmouseover = null;
		b.onmouseout = null;
	}
});var DateDropdown = Class.create({
	initialize: function() {
		this._dropdown = this.initDropdown();
		this.initButtons();
		
		this.curDate;
		this.timeoutID = -1;
		
		if (deepLinkDate == -1) {
			this.snapToCurrentDate();
		}
		else {
			this.snapToDate(dataMgr.getDateFromFileName(deepLinkDate));
		}
	},
	debug: function(str) {
		var _debug = false;
		if (_debug == true) {
			try {
				trace(str);
			} 
			catch (e) {
				alert(str);
			}
		}
	},
	getSelectedDateID: function() {
		return $F(this._dropdown);
	},
	snapToCurrentDate: function() {
		this.snapToDate(new Date());
		this.setButtonStates();
	},
	snapToDate: function(wDate) {
		this.curDate = wDate;
		
		var targetDateVal = dataMgr.getXMLFileName(wDate);
		for (var i=0; i<this._dropdown.options.length; i++){
			if (this._dropdown.options[i].value == targetDateVal) {
				this._dropdown.selectedIndex = i;
				break;
			}
		}
	},
	changeDate: function(increment) {
		this._dropdown.selectedIndex = this._dropdown.selectedIndex + increment;
		this.setButtonStates();
		clearTimeout(this.timeoutID);
		this.timeoutID = setTimeout( delegate.create(this._dropdown, "onchange"), 350);
	},
	gotoPrevDate: function() {
		if (this._dropdown.selectedIndex > 0) {
			this.changeDate(-1);
		}
		return false;
	},
	gotoNextDate: function() {
		if (this._dropdown.selectedIndex < this._dropdown.options.length-1) {
			this.changeDate(1);
		}
		return false;
	},
	onTodayClick: function() {
		this.snapToCurrentDate();
		this.timeoutID = setTimeout( delegate.create(this._dropdown, "onchange"), 350);
		return false;
	},
	initButtons: function() {
		//we use jquery here because prototype's observe doesn't return false back thru stack (so # links still fire and make the page scroll to top)
		this.prevBtn = $('#dateselect-wrapper .prev');
		this.prevBtn.clickFunc = delegate.create(this, "gotoPrevDate");
		this.activateBtn(this.prevBtn);
		
		this.nextBtn = $('#dateselect-wrapper .next');
		this.nextBtn.clickFunc = delegate.create(this, "gotoNextDate");
		this.activateBtn(this.nextBtn);
	},
	setButtonStates: function() {
		this.activateBtn(this.prevBtn);
		this.activateBtn(this.nextBtn);
		if (this._dropdown.selectedIndex == 0)
			this.deactivateBtn(this.prevBtn);
		if (this._dropdown.selectedIndex == this._dropdown.options.length-1)
			this.deactivateBtn(this.nextBtn);
	},
	activateBtn: function(b) {
		b.unbind();
		
		b.click(b.clickFunc);
		b.removeClass('inactive')
		b.hover(
			function(){
				$(this).addClass("hover");
			},
			function(){
				$(this).removeClass("hover");
			}
		);
	},
	deactivateBtn: function(b) {
		b.removeClass("hover").addClass('inactive');
		b.unbind();
	},
	initDropdown: function() {
		var d = $prototype('sked-datepicker');
		d.owner = this;
		
		//skedDates is defined in "sportsnet_sked_config.js" (generated by publish_schedules.php)
		var numDates = skedDates.length;
		for (var i=0; i<numDates; i++) {
			var curElem = skedDates[i];
			var xD = curElem.xmlFileDate;
			var dD = curElem.displayDate;
			
			var opt = document.createElement('option');
			opt.text = dD;
			opt.value = xD;
			d.options.add(opt);
			
			d.onchange = function() {
				this.owner.debug("onchange: " + $F(this));
				this.owner.setButtonStates();
				document.fire("dateDropdown:onDateSelect", {id: $F(this)});
			};
		}
		
		return d;
	}
});var TimeTable = Class.create({
	initialize: function(parent) {
		this._parent = parent;
		this._element = $prototype('timeTable');
		this.items = this._element.select("td");
		this._scrlL_btn = $prototype('timeLeft');
		this._pageL_btn = $prototype('timePageLeft');
		this._scrlR_btn = $prototype('timeRight');
		this._pageR_btn = $prototype('timePageRight');
		
		if (this._parent.DO_SLIDE) {
			this.doTimeScrollCompleteDlgt = delegate.create(this, "doTimeScrollComplete");
		}
		
		//subtracted from offset when shifting to time column position
		this.START_X = this._element.cumulativeOffset().left;
		//caps how much to shift the time row (makes sure there's no whitespace to the right of current time)
		this.MAX_X_OFFSET = -((this.items.length * this._parent.COLUMN_WIDTH) - this._parent.VISIBLE_WIDTH);
		
		$prototype('timeZone').innerHTML = dataMgr.getTimeZone();
		
		this.initButtons();
		this.initTimeCells();
		
		if (deepLinkTime == -1) {
			this.snapToCurrentTime();
		}
		else {
			this.snapToTime(deepLinkTime);
		}
		
		document.observe("cell:scrollArrowClicked", delegate.create(this, "onCellArrowClicked"));
		var dlgt = delegate.create(this, "onDateChange");
		document.observe("dateCell:onDateSelect", dlgt);
		document.observe("dateDropdown:onDateSelect", dlgt);
	},
	snapToCurrentTime: function() {
		var curTime = new Date();
		var curHours = padValue(curTime.getHours().toString());
		var curMinutes = curTime.getMinutes();
		var roundMinutes = padValue((curMinutes - (curMinutes % 30)).toString());
		//trace("curHours: " + curHours + "; curMinutes: " + curMinutes + "; roundMinutes: " + roundMinutes);
		
		//determine the cell that contains the current time and set its display
		var targetTime = curHours + roundMinutes;
		var currentTimeCell = $prototype(targetTime);
		currentTimeCell.firstDescendant().addClassName("current");
		
		this.snapToTime(targetTime);
	},
	snapToTime: function(whichTime) {
		//whichTime is in this format: HHMM (e.g., "1100") - maps to time cell IDs
		//reset the old one
		if (this.firstCell != undefined) {
			this.resetOldFirstCell();
		}
		
		//if that cell is too close to the end, determine the cell that should be first (else, the current one is first)
		var numTimeCells = this.items.length;
		var targetTimeCell = $prototype(whichTime);
		var targetCellIndex = this.getTimeCellIndex(targetTimeCell);
		
		var curTimeFitsFirst = targetCellIndex <= (numTimeCells - this._parent.NUM_COLUMNS);
		if (curTimeFitsFirst) {
			this.firstCell = targetTimeCell;
		} else {
			this.firstCell = this.items[(numTimeCells - this._parent.NUM_COLUMNS)];
		}
		
		this.setFirstCell();
		//set the last cell based on which cell is first
		this.lastCell = this.firstCell.nextSiblings()[this._parent.LAST_CELL_OFFSET];
		
		//original
		this.doTimeScroll(false);
	},
	snapToX: function(x) {
		this.resetOldFirstCell();
		
		this.firstCell = this.getCellAtX(x);
		this.lastCell = this.firstCell.nextSiblings()[this._parent.NUM_COLUMNS-2];
		
		this.setFirstCell();
		this.doTimeScroll();
	},
	getCellAtX: function(x) {
		var c;
		for (var i=0; i<this.items.length; i++) {
			var curCell = this.items[i];
			if (x == curCell.START_X) {
				c = curCell;
				break;
			}
		}
		return c
	},
	onCellArrowClicked: function(event) {
		var d = event.memo.dir;
		if (d == "left") {
			this.scrollTimeLeft();
		} else if (d == "right") {
			this.scrollTimeRight();
		}
	},
	initTimeCells: function() {
		for (var i=0; i<this.items.length; i++) {
			var curCell = this.items[i];
			curCell.START_X = curCell.cumulativeOffset().left - this.START_X;
			curCell.END_X = curCell.START_X + this._parent.COLUMN_WIDTH;
		}
	},
	onDateChange: function() {
		this.snapToCurrentTime();
	},
	getTimeCellIndex: function(whichCell) {
		var cI = 0;
		for (var i=0; i<this.items.length; i++) {
			var curCell = this.items[i];
			if (curCell == whichCell) {
				cI = i;
			}
		}
		return cI;
	},
	initButtons: function() {
		//set the actions on scroll buttons
		var tL = this._scrlL_btn;
		tL._parent = this;
		tL.pgBtn = this._pageL_btn;
		tL.doClick = new Function("this.blur(); this._parent.scrollTimeLeft(); return false;");
		tL.doMouseover = function() {
			skedMgr.showPgBtn(this.pgBtn);
		}
		tL.doMouseout = function() {
			skedMgr.initKillPgBtn();
		}
		
		var tR = this._scrlR_btn;
		tR._parent = this;
		tR.pgBtn = this._pageR_btn;
		tR.doClick = new Function("this.blur(); this._parent.scrollTimeRight(); return false;");
		tR.doMouseover = function() {
			skedMgr.showPgBtn(this.pgBtn);
		}
		tR.doMouseout = function() {
			skedMgr.initKillPgBtn();
		}
		
		//page scroll buttons
		var lP = this._pageL_btn;
		lP._parent = this;
		lP._targetBtn = this._scrlL_btn;
		lP._targetBtnSide = "left";
		lP.onclick = new Function("this.blur(); this._parent.pageTimeLeft(); return false;");
		lP.onmouseover = function() {
			skedMgr.showPgBtn(this);
		};
		lP.onmouseout = function() {
			skedMgr.initKillPgBtn();
		};
		
		var rP = this._pageR_btn;
		rP._parent = this;
		rP._targetBtn = this._scrlR_btn;
		rP._targetBtnSide = "right";
		rP.onclick = new Function("this.blur(); this._parent.pageTimeRight(); return false;");
		rP.onmouseover = function() {
			skedMgr.showPgBtn(this);
		};
		rP.onmouseout = function() {
			skedMgr.initKillPgBtn();
		};
	},
	updateButtonActions: function() {
		this._parent.enableScrollButton(this._scrlL_btn);
		this._parent.enableScrollButton(this._scrlR_btn);
			
		var isAtLeftEnd = this.firstCell.previousSiblings()[0] == undefined;
		var isAtRightEnd = this.lastCell.nextSiblings()[0] == undefined;
		if (isAtLeftEnd) {
			this._parent.disableScrollButton(this._scrlL_btn);
			this._pageL_btn.hide();
		} else if (isAtRightEnd) {
			this._parent.disableScrollButton(this._scrlR_btn);
			this._pageR_btn.hide();
		}
	},
	scrollTimeLeft: function() {
		skedMain.doOmnitureCall('TVSchedOther', {type: "time nav"});
		
		var prevFirstCell = this.firstCell.previousSiblings()[0];
		if (prevFirstCell != undefined) {
			this.resetOldFirstCell();
			
			this.firstCell = prevFirstCell;
			this.lastCell = this.lastCell.previousSiblings()[0];
			
			this.setFirstCell();
			this.doTimeScroll();
		}
	},
	scrollTimeRight: function() {
		skedMain.doOmnitureCall('TVSchedOther', {type: "time nav"});
		
		var nextLastCell = this.lastCell.nextSiblings()[0];
		if (nextLastCell != undefined) {
			this.resetOldFirstCell();
			
			this.lastCell = nextLastCell;
			this.firstCell = this.firstCell.nextSiblings()[0];
			
			this.setFirstCell();
			this.doTimeScroll();
		}
	},
	pageTimeLeft: function() {
		skedMain.doOmnitureCall('TVSchedOther', {type: "time nav"});
		
		 //this.items is only used if the current last cell is too close to the end to simply shift the index
		 //(i.e., if we're on the 45th cell, then we can't just shift over by 8 as it puts us "past" the last cell)
		var prevFirstCell;
		var prevSibs = this.firstCell.previousSiblings();
		if (prevSibs.length >= this._parent.NUM_COLUMNS) {
			prevFirstCell = prevSibs[this._parent.NUM_COLUMNS-1];
		} else {
			prevFirstCell = this.items[0];
		}
		if (prevFirstCell != this.firstCell) {
			this.resetOldFirstCell();
			
			this.firstCell = prevFirstCell; ;
			this.lastCell = this.firstCell.nextSiblings()[this._parent.NUM_COLUMNS-2];
			
			this.setFirstCell();
			this.doTimeScroll();
		}
	},
	pageTimeRight: function() {
		skedMain.doOmnitureCall('TVSchedOther', {type: "time nav"});
		
		 //this.items is only used if the current last cell is too close to the end to simply shift the index
		 //(i.e., if we're on the 45th cell, then we can't just shift over by 8 as it puts us "past" the last cell)
		var nextLastCell;
		var nextSibs = this.lastCell.nextSiblings();
		if (nextSibs.length >= this._parent.NUM_COLUMNS) {
			nextLastCell = nextSibs[this._parent.NUM_COLUMNS-1];
		} else {
			nextLastCell = this.items[this.items.length - 1];
		}
		if (nextLastCell != this.lastCell) {
			this.resetOldFirstCell();
			
			this.lastCell = nextLastCell;
			this.firstCell = this.lastCell.previousSiblings()[this._parent.NUM_COLUMNS-2];
			
			this.setFirstCell();
			this.doTimeScroll();
		}
	},
	doTimeScroll: function(withSlide) {
		var x = Math.max(-this.firstCell.START_X, this.MAX_X_OFFSET);
		if (this._parent.DO_SLIDE && withSlide != false) {
			var m = new Effect.Move(this._element, { x:x, duration:this._parent.SLIDE_DUR, mode:'absolute' });
			var n = new Effect.Move($prototype('skedRowsHolder'), { x:x, duration:this._parent.SLIDE_DUR, mode:'absolute', afterFinish: this.doTimeScrollCompleteDlgt});
		} else {
			this._element.style.left = x + "px";
			$prototype('skedRowsHolder').style.left = x + "px";
			this.doTimeScrollComplete();
		}
	},
	doTimeScrollComplete: function() {
		this.updateButtonActions();
		document.fire("timeTable:onTimeScroll");
	},
	resetOldFirstCell: function() {
		this.firstCell.select('.content')[0].style.paddingLeft = this._parent.FIRST_CELL_DEFAULT_PADDING;
	},
	setFirstCell: function() {
		this.firstCell.select('.content')[0].style.paddingLeft = this._parent.FIRST_CELL_BUTTON_PADDING;
	}
});var StatusManager = Class.create({
	initialize: function() {
		this._element = $prototype('statusDiv');
		this._msgDiv = this._element.select('[id="message"]')[0];
	},
	_setStatus: function(whichMessage, showProgress) {
		this._msgDiv.innerHTML = whichMessage;
		if (showProgress != false) {
			var a = $prototype('progressAnim').cloneNode(true);
			a.show();
			this._msgDiv.appendChild(a);
		}
		this._element.show();
		
	},
	_hideStatus: function () {
		this._element.hide();
	}
});var DailyView = Class.create({
	initialize: function() {
		this.setDisplayProps();
		this.channelsData = skedMgr.channelsData;
		
		this._element = $prototype('printTemplate');
		this.$channelColumnsWrapper = $('.channelColumnsWrapper:first');
		this._headerTable = this._element.select('.pr_headerTable')[0];
		
		this.reorderLogos();
		this.setTimeZone();
		this.drawSkedGrid();
		
		this._element.show();
		
		document.observe("dataMgr:onDataComplete", delegate.create(this, "onSkedDataReady"));
		document.observe("dateDropdown:onDateSelect", delegate.create(this, "onDateSelect"));
	},
	onSkedDataReady: function(event) {
		this.channelsData = event.memo.channelsData;
		this.drawSkedGrid();
	},
	onDateSelect: function() {
		this.$channelColumnsWrapper.empty();
	},
	setDisplayProps: function() {
		this.TIME_INCREMENT = 30;
		this.BLOCK_HEIGHT = 60; //height of a half-hour block (not including bottom border)
		
		var totalHeight = 60 * 48; //60 is height per time cell; 48 half hour increments
		var totalMinutes = 1440; //24 hours in minutes
		this.MINUTES_PER_PIXEL = totalHeight / totalMinutes;
	},
	die: function() {
		this._element.remove();
	},
	setHeaderImage: function() {
		var img = this._element.select('.printRegLogo')[0];
		img.src = "img/print_header_logo_" + dataMgr.getRegion().toLowerCase() + ".jpg";
	},
	setTimeZone: function() {
		this._headerTable.select('.pr_timeZone')[0].innerHTML = dataMgr.getTimeZone('short');
	},
	reorderLogos: function() {
		if (this.logosInOrder == undefined) {
			this.logosInOrder = true;
			var channelRow = this._headerTable.select('.channelRow')[0];
			for (var x=0; x<this.channelsData.length; x++) {
				var curChanID = this.channelsData[x]["@id"].toLowerCase();
				var curLogo = channelRow.select('[id="' + curChanID + 'Logo"]')[0].remove();
				channelRow.appendChild(curLogo);
			}
		}
	},
	getTimeID: function(wDate) {
		var curHours = padValue(wDate.getHours().toString());
		var curMinutes = wDate.getMinutes();
		var roundMinutes = padValue((curMinutes - (curMinutes % 30)).toString());
		var timeID = curHours + ":" + roundMinutes;
		return timeID;
	},
	drawSkedGrid: function(){
		var chData = this.channelsData;
		for (var i = 0; i < chData.length; i++) {
			var curChan = chData[i];
			var curChanID = curChan["@id"].toLowerCase();
			var curRegion = curChan["@id"];
			var programs = curChan.program;
			
			var $channelTable = $('<table class="pr_columnTable channelColumn ' + curChanID + 'Column" border="0" cellpadding="0" cellspacing="0"><tbody class="pr_skedBody"></tbody></table>');
			var curTotal = 0;
			var roundedTotal = 0;
			for (var j = 0; j < programs.length; j++) {
				var curProg = programs[j];
				var isNull = curProg["@isNull"]; //for slots that returned no show info
				var titleID = curProg["@titleID"];
				var imgSrc = curProg["@img"] != undefined ? curProg["@img"] : "";
				var startTime = curProg["@startTime"];
				var endTime = curProg["@endTime"];
				var SeriesName = curProg["SeriesName"];
				var Title = curProg["Title"] || "";
				var SchdPlot = curProg["SchdPlot"] || "";
				var duration = this.getDuration(startTime, endTime);

				var displayText = SeriesName; 
				if (Title != "") displayText += ": " + Title;
				
				var $progCell = $('<td></td>');
				$progCell.attr({
					id: curRegion + "|" + titleID + "|" + startTime
				});
				var targetHeight = (duration * this.MINUTES_PER_PIXEL);// - 1;
				$progCell.css('height', targetHeight + 'px');
				//we set the height of the div since it actually holds the bottom border
				var cellDivHeightAdjust = 10 + 1; //total padding/border; see "table.channelColumn td div" class in CSS
				var targetDivHeight = targetHeight-cellDivHeightAdjust;
				var divTbPadding = 5; //default padding - we override this if the cell is very short
				var lineHeightStyle = '';
				if (targetDivHeight < 11) {
					divTbPadding = 0;
					targetDivHeight = targetHeight-1;
					lineHeightStyle = ' line-height:10px;';
				}
				$progCell.html('<div style="height:' + targetDivHeight + 'px; padding-top:' + divTbPadding + 'px; padding-bottom:' + divTbPadding + 'px;'+lineHeightStyle+'">' + displayText + '</div>');

				$('<tr></tr>').appendTo($channelTable).append($progCell);
			}
			this.$channelColumnsWrapper.append($channelTable);
		}
	},
	getEndTime: function(start, duration) {
		var sA = start.split(":");
		sH = parseFloat(sA[0]);
		sM = parseFloat(sA[1]);
		
		var sDate = new Date(2008, 0, 1, sH, sM);
		
		//trace("sDate: " + sDate);
		var endDate = new Date();
		endDate.setTime(sDate.getTime() + (duration * (60*1000)));
		//trace("endDate: " + endDate);
		
		var endTime = this.getTimeID(endDate);
		return endTime;
	},
	getDuration: function(start, end) {
		//basically subtracts the start time from the end time, accounting for pre/post-midnight, etc.
		var sA = start.split(":");
		sH = parseFloat(sA[0]);
		sM = parseFloat(sA[1]);
		var eA = end.split(":");
		eH = parseFloat(eA[0]);
		eM = parseFloat(eA[1]);
		
		var tDate = 1;
		if (eH < sH) {
			tDate = 2;
		}
		var sDate = new Date(2008, 0, 1, sH, sM);
		var eDate = new Date(2008, 0, tDate, eH, eM);
		
		var diff = ((eDate - sDate) / 1000) / 60;
		
		if (diff < 0) {
			//sometimes shows with no info have end times after their start times... yeah
			diff = 30;
		}
		
		return diff;
	}
});

function jsImport(url) {
	document.write('<script src="', url, '" type="text/javascript"><\/script>');
}

function initSked() {
	setUiBlockerProps();

	window.delegate = new Delegate();
	window.skedMain = new Main();
	skedMain.buildSked();
}

function setUiBlockerProps(){
	$.blockUI.defaults.css = {
	        padding:        0,
	        margin:         0,
	        width:          'auto',
	        top:            'auto',
	        left:           'auto',
	        textAlign:      'left',
	        color:          '#000',
	        border:         'none',
	        backgroundColor:'#fff',
	        cursor:         'default'
	    }
}

var Main = Class.create({
	initialize: function() {
		//strings with #{vars} are Template strings - values are replaced by getTemplateString below
		this.strings = {
			noFilterMatch: "There are no matches for your current filter(s).\nTry reducing the number of filters or check another day.",
			liveFilterDefault: "What are you looking for?"
		}
		
		this.setWindowFuncs();
	},
	//omniture tracking calls
	doOmnitureCall: function(type, paramsObj) {
		return; //kill the tracking in dev
		
		var dispMonths = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
		//the type is based on the event name (we "redundantly" set it in each case below just in case they decide to change the event names - we can just set them below and don't have to go to each function to update them)
		//the trkParam varies based on what gets tracked with each type (notes in each case are from the tagging strategy document)
		var type, trkParam;
		switch (type) {
			case "TVSchedDnld" : {
				//trkCustLnk(this,'TVSchedDnld',%%Schedule Name%%);
				//For Schedule name, use Region:Name
				//called by onMenuSelect() in ToolBar
				type = "TVSchedDnld";
				trkParam = dataMgr._REGION + ":" + paramsObj.skedName;
				break;
			}
			case "TVSchedPrnt" : {
				//trkCustLnk(this,'TVSchedPrnt',%%Schedule Date%%);
				//For schedule date use Region:Date(yyyy-mmm-dd)
				//called by printSked() in ToolBar
				type = "TVSchedPrnt";
				var dateParam = dataMgr.curDate.getFullYear() + "-" + dispMonths[dataMgr.curDate.getMonth()] + "-" + dataMgr.curDate.getDate();
				trkParam = dataMgr._REGION + ":Date(" + dateParam + ")";
				break;
			}
			case "TVSchedProg" : {
				//trkCustLnk(this,'TVSchedProg',%%Program Name%%);
				//For program name use Region:Channel(channel region; e.g., west):program name:Date(YYYY-mmm-dd):start time(hh:mm -24 hour format)
				//called by showDetails() in ScheduleManager
				type = "TVSchedProg";
				
				var cell = paramsObj.curCell;
				
				//get the program name
				var progName = cell.SeriesName;
				//in cases where the show has an episode name, append it (e.g., "OHL: Sarnia @ Guelph")
				if (cell.Title != "") progName += (" | " + cell.Title);
				
				var dateParam = dataMgr.curDate.getFullYear() + "-" + dispMonths[dataMgr.curDate.getMonth()] + "-" + dataMgr.curDate.getDate();
				
				trkParam = dataMgr._REGION + ":Channel(" + cell.region + "):" + progName + ":Date(" + dateParam + "):start time(" + this.getDisplayTime(cell.startTime, cell.region) + ")";
				break;
			}
			case "TVSchedDateSelect" : {
				//trkCustLnk(this,'TVSchedDateSelect',%%Date%%);
				//For date use Region:Date(yyyy-mmm-dd)
				//called by loadSkedXML() in DataManager
				type = "TVSchedDateSelect";
				var dateParam = dataMgr.curDate.getFullYear() + "-" + dispMonths[dataMgr.curDate.getMonth()] + "-" + dataMgr.curDate.getDate();
				trkParam = dataMgr._REGION + ":Date(" + dateParam + ")";
				break;
			}
			case "TVSchedOther" : {
				//trkCustLnk(this,'TVSchedOther',%%Event Name%%);
				//For event name, select from:
				//-	Apply Filter - called by fireFilterChangeEvent() in FilterManager
				//-	time nav - called by user scroll functions in TimeTable
				//-	What's hot Click - 
				//-	Stay Connected Click
				//-	Columnist click
				type = "TVSchedOther";
				trkParam = paramsObj.type;
				break;
			}
		}
		try {
			//trace("type: " + type + "; trkParam: " + trkParam);
			trkCustLnk(this, type, trkParam);
		} catch (e) {
			//alert("doOmnitureCall error: " + e);
		}
	},
	getDisplayTime: function (time, region) {
		var hourInMillis = 60 * 60 * 1000;
		
		//used for omniture tracking functions
		var tA = time.split(":");
		tH = parseFloat(tA[0]);
		tM = parseFloat(tA[1]);
		
		//adjust times for cells in the west/pacific rows to bring them into eastern time
		var curTime = new Date();
		curTime.setHours(tH);
		curTime.setMinutes(tM);
		if (region == "west") {
			curTime = new Date(curTime.getTime() + (2 * hourInMillis));
		} else if (region == "pacific") {
			curTime = new Date(curTime.getTime() + (3 * hourInMillis));
		}
		
		//now adjust for the current time zone if we're in pacific or west
		var tZ = dataMgr.getTimeZone();
		//mountain (west region) is 2 hours behind
		//pacific is 3 hours behind
		if (tZ == "Mountain Time") {
			curTime = new Date(curTime.getTime() - (2 * hourInMillis));
		} else if (tZ == "Pacific Time") {
			curTime = new Date(curTime.getTime() - (3 * hourInMillis));
		}
		
		var timeString = padValue(curTime.getHours()) + ':' + padValue(curTime.getMinutes());
		return timeString;
	},
	getTemplateString: function(tString, obj) {
		tString = this.strings[tString];
		var t = new Template(tString);
		return t.evaluate(obj);
	},
	buildSked: function() {
		window.dataMgr = new DataManager();
		window.statusMgr = new StatusManager();
		window.dailySkedMgr = new SkedManager(this); //dailySkedMgr creates/displays the applicable view on load/request
		
		//window.featureAdHolder = new FeatureAdHolder();
		//window.storyScroller = new StoryScroller();
		//window.featureColumn = new FeatureColumn();
	},
	getTotalOffset: function(element) {
		var totalOffset = {top:0, left:0};
		var curElem = element;
		//var curParent = element.offsetParent;
		while (curElem.offsetParent != undefined) {
			//trace("curElem: " + curElem + "; offsetTop: " + curElem.offsetTop);
			totalOffset.top += curElem.offsetTop;
			totalOffset.left += curElem.offsetLeft;
			curElem = curElem.offsetParent;
		}
		return totalOffset;
	},
	getTotalOffsetTop: function(element) {
		var totalOffset = 0;
		var curElem = element;
		//var curParent = element.offsetParent;
		while (curElem.offsetParent != undefined) {
			//trace("curElem: " + curElem + "; offsetTop: " + curElem.offsetTop);
			totalOffset += curElem.offsetTop;
			curElem = curElem.offsetParent;
		}
		return totalOffset;
	},
	getTotalOffsetLeft: function(element) {
		var totalOffset = 0;
		var curElem = element;
		//var curParent = element.offsetParent;
		while (curElem.offsetParent != undefined) {
			//trace("curElem: " + curElem + "; offsetLeft: " + curElem.offsetLeft);
			totalOffset += curElem.offsetLeft;
			curElem = curElem.offsetParent;
		}
		return totalOffset;
	},
	setWindowFuncs: function() {
		window.reloadSked = function() {
			document.location.reload();
		}
		window.closeSked = function() {
			var isTop = window == top;
			if (!isTop) {
				window.parent.closeSked();
			} else {
				window.close();
			}
		}
		window.padValue = function(whichVal) {
			//turns something like "5" into "05"
			whichVal = whichVal.toString();
			if (whichVal.length == 1) whichVal = "0" + whichVal;
			return whichVal;
		}
		window.unpadValue = function(whichVal) {
			//turns something like "05" into "5"
			whichVal = whichVal.toString();
			if (whichVal == "0") {
				whichVal = "00"; //for minutes (so we don't see stuff like "6:0 PM")
			} else {
				if (whichVal.charAt(0) == "0") whichVal = whichVal.substr(1,1);
			}
			return whichVal;
		}
	},
	doAlert: function(msg) {
		alert(msg);
	}
});

var Delegate = Class.create({
	initialize: function() {
	},
	//Delegate.create is a wrapper for defining bound event handlers (i.e., runs the function in the scope of a desired Element)
	//it accepts a Prototype Element and a function name (string)
	create: function(_scope, _func) {
		_scope = $prototype(_scope);
		//trace("_scope: " + _scope);
		//trace("_func: " + _func);
		var f = _scope[_func];
		//trace("f: " + f);
		var d = _scope[_func].bindAsEventListener(_scope);
		return d;
	}
});
