/* <copyright>
 This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
 No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
 (c) Copyright 2011 Motorola Mobility, Inc.  All Rights Reserved.
 </copyright> */

var Montage = require("montage/core/core").Montage;
var Component = require("montage/ui/component").Component;
var nj = require("js/lib/NJUtils").NJUtils;

var TimelinePanel = exports.TimelinePanel = Montage.create(Component, {

    hasTemplate:{
        value:true
    },

    /* === BEGIN: Models === */
    _arrLayers:{
        value:[]
    },

    arrLayers:{
        serializable:true,
        get:function () {
            return this._arrLayers;
        },
        set:function (newVal) {
            this._arrLayers = newVal;
            this.needsDraw = true;
            this.cacheTimeline();
        }
    },

    _temparrLayers:{
        value:[]
    },

    temparrLayers:{
        get:function () {
            return this._temparrLayers;
        },
        set:function (newVal) {
            this._temparrLayers = newVal;
        }
    },


    _layerRepetition:{
        value:null
    },

    layerRepetition:{
        get:function () {
            return this._layerRepetition;
        },
        set:function (newVal) {
            this._layerRepetition = newVal;
        }
    },
    
    _areTracksScrolling: {
    	value: false
    },

    // Set to false to skip array caching array sets in currentDocument
    _boolCacheArrays:{
        value:true
    },

	// Current layer number: iterated and used to assign layer IDs.
    _currentLayerNumber:{
        value:0
    },

    currentLayerNumber:{
        get:function () {
            return this._currentLayerNumber;
        },
        set:function (newVal) {
            if (newVal !== this._currentLayerNumber) {
                this._currentLayerNumber = newVal;
                this.cacheTimeline();
            }
        }
    },

    _currentLayerSelected:{
        value: false
    },
    currentLayerSelected:{
        get:function () {
            return this._currentLayerSelected;
        },
        set:function (newVal) {
            this._currentLayerSelected = newVal;
            this.cacheTimeline();
        }
    },
    _currentElementsSelected: {
    	value: []
    },
    currentElementsSelected: {
    	get: function() {
    		return this._currentElementsSelected;
    	},
    	set: function(newVal) {
    		this._currentElementsSelected = newVal;
    		this.cacheTimeline();
    	}
    },

    _currentLayersSelected:{
        value:[]
    },
    currentLayersSelected:{
        get:function () {
            return this._currentLayersSelected;
        },
        set:function (newVal) {
            this._currentLayersSelected = newVal;
            this.cacheTimeline();
        }
    },
    
    // The index of the last layer that was clicked on
    // (used for shift-click multiselect)
    _lastLayerClicked : {
    	value: 0
    }, 
    lastLayerClicked: {
    	serializable: true,
    	get: function() {
    		return this._lastLayerClicked;
    	},
    	set: function(newVal) {
    		this._lastLayerClicked = newVal
    	}
    },
    
    _currentSelectedContainer: {
    	value: null
    },
    currentSelectedContainer: {
    	get: function() {
    		return this._currentSelectedContainer;
    	},
    	set: function(newVal) {
    		this._currentSelectedContainer = newVal;
    		this.handleDocumentChange();
    	}
    },

    _millisecondsOffset:{
        value:1000
    },

    millisecondsOffset:{
        get:function () {
            return this._millisecondsOffset;
    },
        set:function (newVal) {
            if (newVal !== this._millisecondsOffset) {
                this._millisecondsOffset= newVal;
                this.drawTimeMarkers();
                NJevent('tlZoomSlider',this);
            }
        }
    },

    _masterDuration:{
        value:0
    },

    masterDuration:{
        serializable:true,
        get:function () {
            return this._masterDuration;
        },
        set:function (val) {
            this._masterDuration = val;
            var intDur = Math.round(val/12),
           		strWidth = intDur + "px";
            this.timebar.style.width = strWidth;
        }
    },

    _trackRepetition:{
        value:null
    },

    trackRepetition:{
        get:function () {
            return this._trackRepetition;
        },
        set:function (newVal) {
            this._trackRepetition = newVal;
        }
    },

    _selectedKeyframes:{
        value:[]
    },

    selectedKeyframes:{
        serializable:true,
        get:function () {
            return this._selectedKeyframes;
        },
        set:function (newVal) {
            this._selectedKeyframes = newVal;
        }
    },

    _selectedTweens:{
        value:[]
    },

    selectedTweens:{
        serializable:true,
        get:function () {
            return this._selectedTweens;
        },
        set:function (newVal) {
            this._selectedTweens = newVal;
        }
    },

    _breadCrumbContainer:{
            value:null
    },

    breadCrumbContainer:{
        get:function () {
            return this._breadCrumbContainer;
        },
        set:function (value) {
            if (this._breadCrumbContainer !== value) {
                this._breadCrumbContainer = value;
            }
        }
    },

    _firstTimeLoaded:{
        value:true
    },

    _captureSelection:{
        value:false
    },

    _openDoc:{
        value:false
    },

    timeMarkerHolder:{
        value:null
    },
    
    // Drag and Drop properties
    _dragAndDropHelper : {
    	value: false
    },
    _dragAndDropHelperCoords: {
    	value: false
    },
    _dragAndDropHelperOffset : {
    	value: false
    },
    _dragLayerID : {
    	value: null
    },
    _draggingType: {
    	value: false
    },
    draggingType: {
    	get: function() {
    		return this._draggingType;
    	},
    	set: function(newVal) {
    		this._draggingType = newVal;
    	}
    },

    layersDragged:{
           value:[],
           writable:true
    },

    dragLayerID : {
    	get: function() {
    		return this._dragLayerID;
    	},
    	set: function(newVal) {
    		if (newVal !== this._dragLayerID) {
    			this._dragLayerID = newVal;
    		}
    	}
    },
    _dragLayerIndexes: {
    	value: []
    },
    _dropLayerID : {
    	value: null
    },
    dropLayerID : {
    	get: function() {
    		return this._dropLayerID;
    	},
    	set: function(newVal) {
    		if (newVal !== this._dropLayerID) {
    			this._dropLayerID = newVal;
    			
    			var dropLayerIndex = this.getLayerIndexByID(this.dropLayerID),
    				arrDragLayers = [], 
    				i = 0, 
    				dragLayerIndexesLength = this._dragLayerIndexes.length;
    			
    			// TODO: possibly we'll need to sort dragLayerIndexes so things don't get out of order?
    			
    			for (i = 0; i < dragLayerIndexesLength; i++) {
    				var myDraggingLayer = this.arrLayers[this._dragLayerIndexes[i]];
    				arrDragLayers.push(myDraggingLayer);
    				// Splice arrLayers
    				this.arrLayers.splice(this._dragLayerIndexes[i], 1);
    				this.arrLayers.splice(dropLayerIndex, 0, myDraggingLayer);
    			}
    			this.layersDragged = arrDragLayers;
    			this._layerDroppedInPlace = this.arrLayers[dropLayerIndex];
    			
    			// Cache the new info
    			this.cacheTimeline();
    			
    			// Clear drag and drop variables for future re-use
    			this._dropLayerID = null;
    			this.dragLayerIndexes = [];
    			this._dragLayerIndexes = [];
    			this.lastLayerClicked = 0;

    			// Sometimes, just to be fun, the drop and dragend events don't fire.
    			// So just in case, set the draw routine to delete the helper.
    			this._deleteHelper = true;
    			this.needsDraw = true;
    		}
    	}
    },
    _appendHelper: {
    	value: false
    },
    _deleteHelper: {
    	value: false
    },
    _scrollTracks: {
    	value: false
    },
    
    // Keyframe drag and drop properties
    _draggingTrackId: {
    	value: false
    },
    draggingTrackId: {
    	get: function() {
    		return this._draggingTrackId;
    	},
    	set: function(newVal) {
    		this._draggingTrackId = newVal;
    	}
    },
    
    
    useAbsolutePosition:{
        value:true
    },
    _currentDocumentUuid: {
    	value: false
    },
    _ignoreSelectionChanges: {
    	value: false
    },
	// is the control key currently being pressed (used for multiselect)
	_isControlPressed: {
		value: false
	},

	// is the shift key currently being pressed (used for multiselect) 
	_isShiftPressed: {
		value: false
	},
    /* === END: Models === */
   
    /* === BEGIN: Draw cycle === */
    prepareForDraw:{
        value:function () {
            this.initTimeline();
        }
    },
    
    draw:{
    	value: function() {
    		
    		// Drag and Drop:
    		if (this.draggingType === "layer") {
	    		
	    		// Do we have a helper to append?
	            if (this._appendHelper === true) {
	            	this.container_layers.appendChild(this._dragAndDropHelper);
	            	this._appendHelper = false;
	            }
	            // Do we need to move the helper?
	    		if (this._dragAndDropHelperCoords !== false) {
	    			if (this._dragAndDropHelper !== null) {
	    				this._dragAndDropHelper.style.top = this._dragAndDropHelperCoords;
	    			}
	    			this._dragAndDropHelperCoords = false;
	    		}
	    		// Do we need to scroll the tracks?
	    		if (this._scrollTracks !== false) {
	    			this.layout_tracks.scrollTop = this._scrollTracks;
	    			this._scrollTracks = false;
	    		}
	    		// Do we have a helper to delete?
	    		if (this._deleteHelper === true) {
	    			if (this._dragAndDropHelper === null) {
	    				// Problem....maybe a helper didn't get appended, or maybe it didn't get stored.
	    				// Try and recover the helper so we can delete it.
	    				var myHelper = this.container_layers.querySelector(".timeline-dnd-helper");
	    				if (myHelper != null) {
	    					this._dragAndDropHelper = myHelper;
	    				}
	    			}
		            if (this._dragAndDropHelper !== null) {
		            	// We need to delete the helper.  Can we delete it from container_layers?
		            	if (this._dragAndDropHelper && this._dragAndDropHelper.parentNode === this.container_layers) {
		            		this.container_layers.removeChild(this._dragAndDropHelper);
		            		this._dragAndDropHelper = null;
		            		this._deleteHelper = false;
		            	}
		            }
	                this.application.ninja.elementMediator.reArrangeDOM(this.layersDragged , this._layerDroppedInPlace);
	                this.layersDragged =[];
	    		}
    		} else if (this.draggingType === "keyframe") {
	    		// Do we need to scroll the tracks?
	    		if (this._scrollTracks !== false) {
	    			this.layout_tracks.scrollLeft = this._scrollTracks;
	    			this._scrollTracks = false;
	    		}
    		}
    		
    		// Do we need to scroll the layers?
    		if (this._areTracksScrolling) {
    			this._areTracksScrolling = false;
	            this.user_layers.scrollTop = this.layout_tracks.scrollTop;
	            this.layout_markers.scrollLeft = this.layout_tracks.scrollLeft;
	         	this.playheadmarker.style.top = this.layout_tracks.scrollTop + "px";
    		}

    	}
    },

    /* === END: Draw cycle === */
    /* === BEGIN: Controllers === */
    // Create an empty layer template object with minimal defaults and return it for use
    createLayerTemplate:{
        value:function () {
            var returnObj = {};
            
            returnObj.layerData = {};
            returnObj.layerData.layerName = null;
            returnObj.layerData.layerID = null;
            returnObj.layerData.stageElement = null;
            returnObj.layerData.isMainCollapsed = true;
            returnObj.layerData.isPositionCollapsed = true;
            returnObj.layerData.isTransformCollapsed = true;
            returnObj.layerData.isStyleCollapsed = true;
            returnObj.layerData.arrLayerStyles = [];
            returnObj.layerData.arrLayerStyles = [];
            returnObj.layerData.elementsList = [];
            returnObj.layerData.deleted = false;
            returnObj.layerData.isSelected = false;
            returnObj.layerData.layerPosition = null;
            returnObj.layerData.created = false;
            returnObj.layerData.isTrackAnimated = false;
            returnObj.layerData.currentKeyframeRule = null;
            returnObj.layerData.trackPosition = 0;
            returnObj.layerData.arrStyleTracks = [];
            returnObj.layerData.tweens = [];
            returnObj.layerData.layerTag = "";
            returnObj.layerData.isVisible = true;
            returnObj.layerData.docUUID = this.application.ninja.currentDocument._uuid;
            returnObj.layerData.isTrackAnimated = false;
            returnObj.parentElementUUID = null;
            returnObj.parentElement = null;
            
            return returnObj;
        }
    },
    
    // cache Timeline data in currentDocument.
    cacheTimeline: {
    	value: function() {
			// Store the timeline data in currentDocument...
			if (this._boolCacheArrays) {
				// ... but only if we're supposed to.
	    		this.application.ninja.currentDocument.tlArrLayers = this.arrLayers;
	    		this.application.ninja.currentDocument.tlCurrentSelectedContainer = this.application.ninja.currentSelectedContainer;
	    		this.application.ninja.currentDocument.tllayerNumber = this.currentLayerNumber;
	    		this.application.ninja.currentDocument.tlCurrentLayerSelected = this.currentLayerSelected;
	    		this.application.ninja.currentDocument.tlCurrentLayersSelected = this.currentLayersSelected;
	    		this.application.ninja.currentDocument.tlCurrentElementsSelected = this.currentElementsSelected;
			}
    	}
    },
    // Initialize Timeline cache in currentDocument.
    initTimelineCache: {
    	value: function() {
			// Initialize the currentDocument for a new set of timeline data.
			this.application.ninja.currentDocument.isTimelineInitialized = true;
			this.application.ninja.currentDocument.tlArrLayers = [];
    		this.application.ninja.currentDocument.tlCurrentSelectedContainer = this.application.ninja.currentSelectedContainer;
    		this.application.ninja.currentDocument.tllayerNumber = this.currentLayerNumber;
    		this.application.ninja.currentDocument.tlCurrentLayerSelected = false;
    		this.application.ninja.currentDocument.tlCurrentLayersSelected = false;
    		this.application.ninja.currentDocument.tlCurrentElementsSelected = [];
    	}
    },
    
    // Create an array of style objects for an element, for use
    // in creating a new layer
    createLayerStyles : {
    	value: function(ptrElement) {
    		// TODO: Create logic to loop through 
    		// CSS properties on element and build 
    		// array of layer styles for return.
    		// Right now this method just returns an array of one bogus style.
    		
    		var returnArray = [],
    			newStyle = {}, 
    			styleID = "1@0"; // format: layerID + "@" + style counter
    			
    		/* Example new style 	
            newStyle.styleID = styleID;
			newStyle.whichView = "propval";		// Which view do we want to show, usually property/value view (see Style)
			newStyle.editorProperty = "top";	// the style property
			newStyle.editorValue = 0;			// The current value
			newStyle.ruleTweener = false; 
			newStyle.isSelected = false;
			
			returnArray.push(newStyle);
			*/
			
			return returnArray;
    		
    	}
    },
    
    // Create an array of style track objects for an element, for use
    // in creating a new layer
    createStyleTracks : {
    	value: function(ptrElement) {
    		// TODO: Create logic to loop through 
    		// CSS properties on element and build 
    		// array of layer styles for return.
    		// Right now this method just returns an array of one bogus style.
    		
    		var returnArray = [];
						
			return returnArray;
    		
    	}
    },

    // Bind all document-specific events (pass in true to unbind)
    _bindDocumentEvents : {
        value: function(boolUnbind) {
        	/*
			var arrEvents = [ "stageElement",
                             "deleteLayer",
                             "elementAdded",
                             "elementsRemoved",
                             "elementReplaced",
                             "selectionChange"],
        	 */
			var arrEvents = ["elementAdded",
                             "elementsRemoved",
                             "selectionChange"],
                i,
                arrEventsLength = arrEvents.length;

            if (boolUnbind) {
                for (i = 0; i < arrEventsLength; i++) {
                    this.eventManager.removeEventListener(arrEvents[i], this, false);
                }
            } else {
                for (i = 0; i < arrEventsLength; i++) {
                    this.eventManager.addEventListener(arrEvents[i], this, false);
                }
            }
        }
    },

    // Initialize the timeline, runs only once when the timeline component is first loaded
    initTimeline:{
        value:function () {
        	
        	// Get some selectors
            this.layout_tracks = this.element.querySelector(".layout-tracks");
            this.layout_markers = this.element.querySelector(".layout_markers");
            
            
            // Bind the event handler for the document change events
            //this.eventManager.addEventListener("onOpenDocument", this.handleDocumentChange.bind(this), false);
            this.eventManager.addEventListener("closeDocument", this.handleDocumentChange.bind(this), false);
            //this.eventManager.addEventListener("switchDocument", this.handleDocumentChange.bind(this), false);
            //this.eventManager.addEventListener("breadCrumbBinding",this,false);
            
            // Bind drag and drop event handlers
            this.container_layers.addEventListener("dragstart", this.handleLayerDragStart.bind(this), false);
            this.container_layers.addEventListener("dragend", this.handleLayerDragEnd.bind(this), false);
            this.container_layers.addEventListener("dragover", this.handleLayerDragover.bind(this), false);
            this.container_layers.addEventListener("drop", this.handleLayerDrop.bind(this), false);
            this.container_tracks.addEventListener("dragover", this.handleKeyframeDragover.bind(this), false);
            this.container_tracks.addEventListener("drop", this.handleKeyframeDrop.bind(this), false);
            
            // Bind the handlers for the config menu
            this.checkable_animated.addEventListener("click", this.handleAnimatedClick.bind(this), false);
            this.checkable_relative.addEventListener("click", this.handleRelativeClick.bind(this), false);
            this.checkable_absolute.addEventListener("click", this.handleAbsoluteClick.bind(this), false);
            this.tl_configbutton.addEventListener("click", this.handleConfigButtonClick.bind(this), false);
            document.addEventListener("click", this.handleDocumentClick.bind(this), false);
            
            // Add some event handlers
            this.timeline_leftpane.addEventListener("click", this.timelineLeftPanelMousedown.bind(this), false);
            //this.timeline_leftpane.addEventListener("click", this.timelineLeftPanelMousedown.bind(this), false);
            //this.timeline_leftpane.addEventListener("mouseup", this.timelineLeftPaneMouseup.bind(this), false);
            this.layout_tracks.addEventListener("scroll", this.updateLayerScroll.bind(this), false);
            this.user_layers.addEventListener("scroll", this.updateLayerScroll.bind(this), false);
            this.end_hottext.addEventListener("changing", this.updateTrackContainerWidth.bind(this), false);
            this.playhead.addEventListener("mousedown", this.startPlayheadTracking.bind(this), false);
            this.playhead.addEventListener("mouseup", this.stopPlayheadTracking.bind(this), false);
            this.time_markers.addEventListener("click", this.updatePlayhead.bind(this), false);
            document.addEventListener("keydown", this.timelineLeftPaneKeydown.bind(this), false);
            document.addEventListener("keyup", this.timelineLeftPaneKeyup.bind(this), false);
            this.eventManager.addEventListener("updatedID", this.handleLayerIdUpdate.bind(this), false);
            
            // Bind some bindings
            Object.defineBinding(this, "currentSelectedContainer", {
                boundObject:this.application.ninja,
                boundObjectPropertyPath:"currentSelectedContainer",
                oneway:true
            });
            
			// Start the panel out in disabled mode by default
			// (Will be switched on later, if appropriate).
            this.enablePanel(false);

        }
    },

    // Initialize the timeline for a document.
    // Called when a document is opened (new or existing), or when documents are switched.
    initTimelineForDocument:{
        value:function () {


            var myIndex,
            	boolAlreadyInitialized = false;
            this.drawTimeMarkers();
            // Document switching
            // Check to see if we have saved timeline information in the currentDocument.
            //console.log("TimelinePanel.initTimelineForDocument");

            if ((typeof(this.application.ninja.currentDocument.isTimelineInitialized) === "undefined")) {
            	//console.log('TimelinePanel.initTimelineForDocument: new Document');
                // No, we have no information stored.
                // This could mean we are creating a new file, OR are opening an existing file.
                
                // First, initialize the caches.
				this.initTimelineCache();
                this.temparrLayers = [];

				// That's all we need to do for a brand new file. 
                // But what if we're opening an existing document?
                if (!this.application.ninja.documentController.creatingNewFile) {
                    // Opening an existing document. If it has DOM elements we need to restore their timeline info
                    if (this.application.ninja.currentDocument.documentRoot.children[0]) {
                        // Yes, it has DOM elements. Loop through them and create a new object for each.
                        for (myIndex = 0; this.application.ninja.currentDocument.documentRoot.children[myIndex]; myIndex++) {
                            this._openDoc = true;
                            this.restoreLayer(this.application.ninja.currentDocument.documentRoot.children[myIndex]);
                        }
                    }
                }
                
                // Draw the repetition.
                this.arrLayers = this.temparrLayers;
                this.currentLayerNumber = this.arrLayers.length;
                this._currentDocumentUuid = this.application.ninja.currentDocument.uuid;
                boolAlreadyInitialized = true;
                
			} else if (this.application.ninja.currentDocument.setLevel) {
            	//console.log('TimelinePanel.initTimelineForDocument: breadCrumbClick');
				// Information stored, but we're moving up or down in the breadcrumb.
				// Get the current selection and restore timeline info for its children.
				//debugger;
                var parentNode = this.application.ninja.currentSelectedContainer,
                	storedCurrentLayerNumber = this.application.ninja.currentDocument.tllayerNumber;
                this.temparrLayers = [];
                
                for (myIndex = 0; parentNode.children[myIndex]; myIndex++) {
                    this._openDoc = true;
                    this.restoreLayer(parentNode.children[myIndex]);

                }
                // Draw the repetition.
                this.arrLayers = this.temparrLayers;
                this.currentLayerNumber = storedCurrentLayerNumber;
                boolAlreadyInitialized = true;
                this.application.ninja.currentDocument.setLevel = false;
            } else {
            	//console.log('TimelinePanel.initTimelineForDocument: else fallback');
                // we do have information stored.  Use it.
                var i = 0, 
                	tlArrLayersLength = this.application.ninja.currentDocument.tlArrLayers.length;
                
                // We're reading from the cache, not writing to it.
            	this._boolCacheArrays = false;
                for (i = 0; i < tlArrLayersLength; i++) {
                    if (this.application.ninja.currentDocument.tlArrLayers[i].layerData.isSelected === true) {
                        this.application.ninja.currentDocument.tlArrLayers[i].layerData._isFirstDraw = true;
                    } else {
                    	this.application.ninja.currentDocument.tlArrLayers[i].layerData._isFirstDraw = false;
                    }
                }
                this.arrLayers = this.application.ninja.currentDocument.tlArrLayers;
                this.currentLayerNumber = this.application.ninja.currentDocument.tllayerNumber;
                this.currentLayerSelected = this.application.ninja.currentDocument.tlCurrentLayerSelected;
                this.currentLayersSelected = this.application.ninja.currentDocument.tlCurrentLayersSelected;
                this.currentElementsSelected = this.application.ninja.currentDocument.tlCurrentElementsSelected;
                this._currentDocumentUuid = this.application.ninja.currentDocument.uuid;
                
                // Are we only showing animated layers?
				if (this.application.ninja.currentDocument.boolShowOnlyAnimated) {
					// Fake a click.
					var evt = document.createEvent("MouseEvents");
					evt.initMouseEvent("click");
					this.checkable_animated.dispatchEvent(evt);
				}

				// Ok, done reading from the cache.
				this._boolCacheArrays = true;
				
				// Reset master duration
				this.resetMasterDuration();
            }
        }
    },

    // Clear the currently-displayed document (and its events) from the timeline.
    clearTimelinePanel:{
        value:function () {
            // Remove events
            this._bindDocumentEvents(true);

            // Remove every event listener for every selected tween in the timeline
            this.deselectTweens();

            // Reset visual appearance
            // TODO: Maybe playhead position should be stored per document, so we can persist between document switch?
            this.application.ninja.timeline.playhead.style.left = "-2px";
            this.application.ninja.timeline.playheadmarker.style.left = "0px";
            this.application.ninja.timeline.updateTimeText(0.00);
            this.timebar.style.width = "0px";
			this.checkable_animated.classList.remove("checked");
            this.currentLayerNumber = 0;
            this.currentLayerSelected = false;
            this.currentLayersSelected = false;
            this.currentElementsSelected = [];
            this.selectedKeyframes = [];
            this.selectedTweens = [];
            this._captureSelection = false;
            this._openDoc = false;
            this.end_hottext.value = 25;
            this.updateTrackContainerWidth();
            // Clear the repetitions
            if (this.arrLayers.length > 0) {
                this.arrLayers = [];
                this.arrLayers.length = 0;
            }
            this.resetMasterDuration();
        }
    },

    handleDocumentChange:{
        value:function () {
        	// console.log("TimelinePanel.handleDocumentChange");
        	
			if (this.application.ninja.currentDocument == null) {
				// On app initialization, the binding is triggered before
				// there is a currentDocument.  We don't do anything at that time.
				return;
			}
			
			// Is this the same document?
			if (this._currentDocumentUuid === this.application.ninja.currentDocument.uuid) {
				// Yes, same document, so we are changing levels.
				this.application.ninja.currentDocument.setLevel = true;
				this._ignoreSelectionChanges = true;
			}
			
            this._boolCacheArrays = false;
            this.clearTimelinePanel();
            this._boolCacheArrays = true;

            // Rebind the document events for the new document context
            this._bindDocumentEvents();

            // Reinitialize the timeline...but only if there are open documents.
            if (this.application.ninja.documentController._documents.length > 0) {
                this.enablePanel(true);
                this.initTimelineForDocument();

            } else {
                this.enablePanel(false);
            }
        }
    },

    updateTrackContainerWidth:{
        value:function () {
            this.container_tracks.style.width = (this.end_hottext.value * 80) + "px";
            this.master_track.style.width = (this.end_hottext.value * 80) + "px";
            this.time_markers.style.width = (this.end_hottext.value * 80) + "px";
            if (this.timeMarkerHolder) {
                this.time_markers.removeChild(this.timeMarkerHolder);
            }
            this.drawTimeMarkers();
        }
    },

    updateLayerScroll:{
        value:function () {
        	this._areTracksScrolling = true;
        	this.needsDraw = true;
        }
    },

    startPlayheadTracking:{
        value:function () {
            this.time_markers.onmousemove = this.updatePlayhead.bind(this);
        }
    },

    stopPlayheadTracking:{
        value:function () {
            this.time_markers.onmousemove = null;
        }
    },

    updatePlayhead:{
        value:function (event) {
            var clickedPosition = event.target.offsetLeft + event.offsetX;
            this.playhead.style.left = (clickedPosition - 2) + "px";
            this.playheadmarker.style.left = clickedPosition + "px";
            var currentMillisecPerPixel = Math.floor(this.millisecondsOffset / 80);
            var currentMillisec = currentMillisecPerPixel * clickedPosition;
            this.updateTimeText(currentMillisec);
        }
    },
	
	// Event handler for changes in stage selection.
    handleSelectionChange: {
        value:function (event) {
        	this.updateLayerSelection();
        }
    },

	// Select the layers whose indexes are passed in as arrSelectedIndexes.
	// Pass in an empty array to clear all selections.
    selectLayers:{
        value:function (arrSelectedIndexes) {
        	var i = 0,
        		j = 0,
        		arrLayersLength = this.arrLayers.length,
        		arrSelectedIndexesLength = arrSelectedIndexes.length,
        		currentLayersSelectedLength = this.currentLayersSelected.length,
        		boolContinue = false,
        		arrSelectedLayers = false,
        		arrCurrentElementsSelected = [];
        	/*
        	 // TODO: this should probably check to see if it actually needs to run.
        	        		console.log(arrSelectedIndexes);
        		console.log(this.currentLayersSelected);
        	// Compare arrSelectedIndexes with this.currentLayersSelected
        	// If the items are the same, we do not need to do anything.
        	if (arrSelectedIndexesLength !== currentLayersSelectedLength) {
        		// Different length in the arrays, we definitely need to continue.
        		console.log('diferent length')
        		boolContinue = true;
        	} else {
        		// Check each selected index and see if it's in this.currentLayersSelected
        		// If we find one that isn't, we need to continue

        		for (i = 0; i < arrSelectedIndexesLength; i++) {
        			console.log('checking for ', arrSelectedIndexes[i]);
					if (this.currentLayersSelected.indexOf(arrSelectedIndexes[i]) === -1) {
						// Ooops, one of them was not found.
						boolContinue = true;
					}
        		}
        	}
        	if (boolContinue === false) {
        		console.log('exiting')
        		return;
        	}
        	*/
        	

            // Deselect all layers.
            for (i = 0; i < arrLayersLength; i++) {
            	if (this.arrLayers[i].layerData.isSelected === true) {
					this.arrLayers[i].layerData.isSelected = false;
            		this.triggerLayerBinding(i);
            	}
            }
            if (this.currentLayersSelected !== false) {
            	this.currentLayersSelected = false;
            }
            
            // If we are actually going to be selecting things, create an empty array to use
            if (arrSelectedIndexesLength > 0) {
            	arrSelectedLayers = [];
            }
            
            // Loop through arrLayers and do the selection.
            for (i = 0; i < arrLayersLength; i++) {
            	if (arrSelectedIndexes.indexOf(i) > -1) {
            		this.arrLayers[i].layerData.isSelected = true;
            		this.arrLayers[i].isSelected = true;
            		this.triggerLayerBinding(i);
            		arrSelectedLayers.push(i);
            		arrCurrentElementsSelected.push(this.arrLayers[i].layerData.stageElement);
            	}
            }

			// Store the selected layer information
			this.currentLayersSelected = arrSelectedLayers;
			this.currentElementsSelected = arrCurrentElementsSelected;
			
			// Tell the repetition what has been selected
            this.layerRepetition.selectedIndexes = arrSelectedIndexes;

            // Finally, reset the master duration.
            this.resetMasterDuration();
        }
    },
    
    // Get the indexes of layers that should be selected from
    // the elements that are currently selected on stage.
    getSelectedLayerIndexesFromStage: {
    	value: function() {
    		var arrIndexes = [],
    			i = 0, 
    			j = 0,
    			arrLayersLength = this.arrLayers.length,
    			selectedElementsLength = this.application.ninja.selectedElements.length;
    		
    		for (i = 0; i < selectedElementsLength; i++) {
    			var currentTestElement = this.application.ninja.selectedElements[i];
    			for (j = 0; j < arrLayersLength; j++) {
    				if (this.arrLayers[j].layerData.stageElement == currentTestElement) {
    					arrIndexes.push(j);
    				}
    			}
    		}
    		return arrIndexes;
    	}
    },
    
    // Update the selected layers based on what is selected on stage
    updateLayerSelection: {
    	value: function() {
    		var arrIndexes = this.getSelectedLayerIndexesFromStage();
    		this.selectLayers(arrIndexes);
    	}
    },
    
    // Update stage selection based on what layers are selected
    updateStageSelection: {
    	value: function() {
    		var arrSelectedElements = [],
    			i = 0,
    			arrLayersLength = this.arrLayers.length;
    		
    		// Get the selected layers
    		for (i = 0; i < arrLayersLength; i++) {
    			if (this.arrLayers[i].layerData.isSelected === true) {
    				arrSelectedElements.push(this.arrLayers[i].layerData.stageElement);
    			}
    		}
    		
    		// Select the layers, or clear the selection if none were found
    		if (arrSelectedElements.length > 0) {
    			this.application.ninja.selectionController.selectElements(arrSelectedElements);
    		} else {
    			this.application.ninja.selectionController.executeSelectElement();
    		}
    		
    	}
    },

    deselectTweens:{
        value:function () {
            for (var i = 0; i < this.selectedTweens.length; i++) {
                this.selectedTweens[i].deselectTween();
            }
            this.selectedTweens = null;
            this.selectedTweens = new Array();
        }
    },

	timelineLeftPanelMousedown: {
        value:function (event) {
            var ptrParent = nj.queryParentSelector(event.target, ".container-layer"),
            	i = 0,
            	arrLayers = document.querySelectorAll(".container-layer"),
            	arrLayersLength = arrLayers.length,
            	targetIndex = 0,
	            isAlreadySelected = false,
	            indexAlreadySelected = 0,
	            indexLastClicked = 0;

			// Did the mousedown event originate within a layer?
			if (ptrParent === false) {
				// No it did not.  Do nothing.
				return;
			}
			
			// Get the targetIndex, the index in the arrLayers of the 
			// layer that was just clicked on
            for (i = 0; i < arrLayersLength; i++) {
            	if (arrLayers[i] == ptrParent) {
            		targetIndex = i;
            	}
            }
            
            // Did we just click on a layer that's already selected?
			if (this.currentLayersSelected !== false) {
				indexAlreadySelected = this.currentLayersSelected.indexOf(targetIndex);
			}
			if (indexAlreadySelected > -1) {
				isAlreadySelected = true;
			}
			
			/*
			if (targetIndex > -1) {
				indexLastClicked = targetIndex;
			}
			*/
            
            // Now, do the selection based on all of that information.
            if (this.currentLayersSelected.length === 0) {
            	// Nothing selected yet, so just push the new index into the array.
            	this.currentLayersSelected.push(targetIndex);
            } else {
            	// Something is already selected.  What do do depends on whether
            	// or not other keys are pressed.
	            if (this._isControlPressed === true) {
	            	// Control key is being pressed, so we need to 
	            	// either add the current layer to selectedLayers
	            	// or remove it if it's already there.
					if (this.currentLayersSelected === false) {
						this.currentLayersSelected = [];
						//this.currentLayerSelected = false;
					}
	            	if (isAlreadySelected === false) {
	            		this.currentLayersSelected.push(targetIndex);
	            	} else {
	            		this.currentLayersSelected.splice(indexAlreadySelected, 1);
	            	}
	            	this.lastLayerClicked = targetIndex;
	            } else if (this._isShiftPressed === true) {
	            	// The shift key is being pressed.
	            	// Start by selecting the lastLayerClicked
					if (this.currentLayersSelected === false) {
						this.currentLayersSelected = [];
						//this.currentLayerSelected = false;
					}
					this.currentLayersSelected = [this.lastLayerClicked];
					// Add all the layers between lastLayerClicked and targetIndex
	            	if (targetIndex > this.lastLayerClicked) {
	            		for (i = this.lastLayerClicked+1; i <= targetIndex; i++) {
	            			this.currentLayersSelected.push(i);
	            		}
	            	} else if (targetIndex < this.lastLayerClicked) {
	            		for (i = targetIndex; i < this.lastLayerClicked; i++) {
	            			this.currentLayersSelected.push(i);
	            		}
	            	}
	            } else {
	            	// No key is pressed, so just select the element
	            	// and update lastLayerClicked
	            	this.currentLayersSelected = [targetIndex];
	            	this.lastLayerClicked = targetIndex;
	            }
	            
            }
            //this._captureSelection = true;
            this.selectLayers(this.currentLayersSelected);
            this.updateStageSelection();
        }
	},

	timelineLeftPaneKeydown: {
		value: function(event) {
			if (event.keyCode === 16) {
				// Shift key has been pressed
				this._isShiftPressed = true;
			}
			if (event.keyCode === 17) {
				// Control key has been pressed
				this._isControlPressed = true;
			}
		}
	},
    
	timelineLeftPaneKeyup: {
		value: function(event) {
			if (event.keyCode === 16) {
				// Shift key has been released
				this._isShiftPressed = false;
			}
			if (event.keyCode === 17) {
				// Control key has been released
				this._isControlPressed = false;
			}
		}
	},
    createstageElement:{
        value:function (object) {
            var stageElementName = "",
                thingToPush = this.createLayerTemplate(),
                myIndex = 0,
                i = 0,
                arrLayersLength = this.arrLayers.length;

			// Make up a layer name.
            this.currentLayerNumber = this.currentLayerNumber + 1;
//            stageElementName = "Layer " + this.currentLayerNumber;
            stageElementName="         ";

			// Possibly currentLayerNumber doesn't correctly reflect the
			// number of layers.  Check that.
            // Commented out to fix WebGL rendering bug
            /*for(k = 0; k < arrLayersLength; k++){
                if(this.arrLayers[k].layerData.layerName === stageElementName){
                     this.currentLayerNumber = this.currentLayerNumber + 1;
                     stageElementName = "Layer " + this.currentLayerNumber;
                     break;
                }
            }*/
            // We will no longer have multiple things selected, so wipe that info out
            // if it isn't already gone.
            this.currentLayersSelected = false;
            
            // thingToPush is the template we just got.  Now fill it in.
            thingToPush.layerData.layerName = stageElementName;
            thingToPush.layerData.layerTag = "<" + object.nodeName.toLowerCase() + ">";
            thingToPush.layerData.layerID = this.currentLayerNumber;
            thingToPush.parentElement = this.application.ninja.currentSelectedContainer;
            thingToPush.layerData.isSelected = true;
            thingToPush.layerData._isFirstDraw = true;
            thingToPush.layerData.created = true;
            thingToPush.layerData.stageElement = object;

            if (this.checkable_animated.classList.contains("checked")) {
            	thingToPush.layerData.isVisible = false;
            }
            
            // Determine where the new array should be inserted in arrLayers.
            // Ordinarily we could use this.getInsertionIndex BUT the new element
            // insertion and selection has already fired, so getInsertionIndex will return 
            // incorrect info. So we need to look at the DOM.
            var childrenLength = this.application.ninja.currentSelectedContainer.children.length,
            	newIndex = childrenLength -1;
            for (i = 0; i < childrenLength; i++) {
            	var currTest = this.application.ninja.currentSelectedContainer.children[i];
            	if (object == currTest) {
            		myIndex = newIndex - i;
            	}
            }

            this.arrLayers.splice(myIndex, 0, thingToPush);
            this.selectLayers([myIndex]);
        }
    },

    restoreLayer:{
        value:function (ele) {
            var stageElementName, 
            	thingToPush = this.createLayerTemplate();

            this.currentLayerNumber = this.currentLayerNumber + 1;
//            stageElementName = "Layer " + this.currentLayerNumber;

//            if(ele.dataset.storedLayerName){
//                stageElementName = ele.dataset.storedLayerName;
//            }
            if(ele.id){
                thingToPush.layerData.layerName = ele.id;
            }
            thingToPush.layerData.layerID = this.currentLayerNumber;
            thingToPush.layerData.layerTag = "<" + ele.nodeName.toLowerCase() + ">";
            thingToPush.parentElement = this.application.ninja.currentSelectedContainer;
            if (this._openDoc) {
                //thingToPush.layerData.elementsList.push(ele);
                thingToPush.layerData.stageElement = ele;
            }
            if (this.checkable_animated.classList.contains("checked")) {
            	thingToPush.layerData.isVisible = false;
            }
            
            // Are there styles to add?
            thingToPush.layerData.arrLayerStyles = this.createLayerStyles();
            thingToPush.layerData.arrStyleTracks = this.createStyleTracks();

			// Add the layer to the repetition
            this.temparrLayers.splice(0, 0, thingToPush);
            thingToPush.layerData.trackPosition = this.temparrLayers.length - 1;
            thingToPush.layerData.layerPosition = this.temparrLayers.length - 1;

            this._openDoc = false;

        }
    },

    deleteLayers: {
    	value: function(arrElements) {
    		var i = 0, 
    			j = 0,
    			arrLayersLength = this.arrLayers.length,
    			arrElementsLength = arrElements.length;
    		
    		for (i = 0; i < arrElementsLength; i++) {
    			var currentTest = arrElements[i];
    			for (j = 0; j < arrLayersLength; j++) {
    				if (this.arrLayers[j].layerData.stageElement == currentTest) {
    					this.arrLayers.splice(j, 1);
    					// Super-secret magic trick: Now that we've spliced out an element,
    					// arrLayers.length is different. We need to update it.
    					arrLayersLength = this.arrLayers.length;
    				}
    			}
    		}
    		this.selectLayers([]);
    		this.resetMasterDuration();
    	}
    },

    resetMasterDuration:{
        value:function(){
            var trackDuration = 0,
            	arrLayersLength = this.arrLayers.length, 
            	i = 0;

            if (arrLayersLength > 0) {
            	for (i = 0; i < arrLayersLength; i++) {
            		var currLength = this.arrLayers[i].layerData.trackDuration;
            		if (currLength > trackDuration) {
            			trackDuration = currLength;
            		}
            	}
            }
            this.masterDuration = trackDuration;
        }
    },

    handleElementAdded:{
        value:function() {
            this.createstageElement(this.application.ninja.selectedElements[0]);
        }
    },

    handleElementsRemoved:{
        value:function (event) {
            this.deleteLayers(event.detail);
        }
    },

    handleElementReplaced:{
        value:function(event){
        	// TODO: this needs to be updated. Not sure when an elementReplaced event will be fired?
        	/*
            this.currentLayerSelected.layerData.elementsList.pop();
            this.currentLayerSelected.layerData.elementsList.push(event.detail.data.newChild);
            this.currentLayerSelected.layerData.animatedElement = event.detail.data.newChild;
            */
        }
    },

    drawTimeMarkers:{
        value:function () {
            this.timeMarkerHolder = document.createElement("div");

            if(this.time_markers.children[0]){
               this.time_markers.removeChild(this.time_markers.children[0]);
            }

            this.time_markers.appendChild(this.timeMarkerHolder);
            var i;
            var totalMarkers = Math.floor(this.time_markers.offsetWidth / 80);
            for (i = 0; i < totalMarkers; i++) {
                var timeMark = document.createElement("div");
                var markValue = this.calculateTimeMarkerValue(i);
                timeMark.className = "timemark";
                timeMark.innerHTML = markValue;
                this.timeMarkerHolder.appendChild(timeMark);
            }
        }
    },

    calculateTimeMarkerValue:{
        value:function (currentMarker) {
            var currentMilliseconds = currentMarker * this.millisecondsOffset;
            return this.convertMillisecondsToTime(currentMilliseconds);
        }
    },

    updateTimeText:{
        value:function (millisec) {
            this.timetext.innerHTML = this.convertMillisecondsToTime(millisec);
        }
    },

    convertMillisecondsToTime:{
        value:function(millisec){
            var timeToReturn;
            var sec = (Math.floor((millisec / 1000))) % 60;
            var min = (Math.floor((millisec / 1000) / 60)) % 60;
            var milliSeconds = String(Math.round(millisec / 10));
            var returnMillisec = milliSeconds.slice(milliSeconds.length - 2, milliSeconds.length);
            var returnSec;
            var returnMin;
            if (sec < 10) {
                returnSec = "0" + sec;
            } else {
                returnSec = sec;
            }
            if (min < 10) {
                returnMin = "0" + min;
            } else {
                returnMin = min;
            }
            if (returnMillisec == "0") {
                returnMillisec = "0" + returnMillisec;
            }
            timeToReturn = returnMin + ":" + returnSec + ":" + returnMillisec;
            return timeToReturn;
        }
    },

    selectLayer:{
        value:function (layerIndex, userSelection) {
        	console.log("=----> Please update this component to use TimelinePanel.selectLayers. See this message for syntax. <----=");
        	this.selectLayers([layerIndex]);
        	if (userSelection === true) {
        		this.updateStageSelection();
        	}
        }
    },

	// Get the index where a layer should be inserted based on selection.
	// If nothing is selected, returns false.
	// Used by ElementController.addElement.
	getInsertionIndex: {
		value: function() {
			var i = 0, 
				currentLayersSelectedLength = this.currentLayersSelected.length,
				arrLayersLength = this.arrLayers.length,
				returnVal = arrLayersLength -1;
			if (this.currentLayersSelected === false) {
				return false;
			}
			
			for (i = 0; i < arrLayersLength; i++) {
				if (this.arrLayers[i].layerData.isSelected) {
					returnVal = i;
				}
			}
			return returnVal;
		}
	},

    getLayerIndexByID:{
        value:function (layerID, tempArr) {
            var i = 0,
                returnVal = false,
                arrLayersLength = this.arrLayers.length;

            if (tempArr) {
                var tempArrLength = this.temparrLayers.length;

                for (i = 0; i < tempArrLength; i++) {
                    if (this.temparrLayers[i].layerData.layerID === layerID) {
                        returnVal = i;
                    }
                }

            } else {
                for (i = 0; i < arrLayersLength; i++) {
                    if (this.arrLayers[i].layerData.layerID === layerID) {
                        returnVal = i;
                    }
                }
            }
            return returnVal;
        }
    },

    enablePanel:{
        value:function (boolEnable) {
            if (boolEnable) {
                this.timeline_disabler.style.display = "none";
            } else {
                this.timeline_disabler.style.display = "block";
            }
        }
    },
    handleConfigButtonClick: {
    	value: function(event) {
    		event.stopPropagation();
    		this.handleCheckableClick(event);
    		
    	}
    },
    handleDocumentClick: {
    	value: function(event) {
    		if (this.tl_configbutton.classList.contains("checked")) {
    			this.tl_configbutton.classList.remove("checked");
    		}
    	}
    },
    
    handleAnimatedClick: {
    	value: function(event) {
    		if (typeof(this.application.ninja.currentDocument) === "undefined") {
    			return;
    		}
    		if (this.application.ninja.currentDocument == null) {
    			return;
    		}
    		this.handleCheckableClick(event);
    		this.application.ninja.currentDocument.boolShowOnlyAnimated = event.currentTarget.classList.contains("checked");
    		var boolHide = false,
    			i = 0,
    			arrLayersLength = this.arrLayers.length;
    		if (event.currentTarget.classList.contains("checked")) {
    			// Hide layers with isAnimated = false;
    			boolHide = true;
    		}
    		
    		for (i = 0; i < arrLayersLength; i++) {
    			if (boolHide) {
    				// Hide layers with isAnimated = false
    				if (this.arrLayers[i].layerData.isTrackAnimated === false) {
    					this.arrLayers[i].layerData.isVisible = false;
    					this.triggerLayerBinding(i);
    				}
    			} else {
    				this.arrLayers[i].layerData.isVisible = true;
    				this.triggerLayerBinding(i);
    			}
    		}
    		
    	}
    },
    handleRelativeClick: {
    	value: function(event) {
    		if (!event.currentTarget.classList.contains("checked")) {
    			this.handleCheckableClick(event);
    		}
    		this.checkable_absolute.classList.remove("checked");
            this.useAbsolutePosition = false;
    	}
    },
    handleAbsoluteClick: {
    	value: function(event) {
    		if (!event.currentTarget.classList.contains("checked")) {
    			this.handleCheckableClick(event);
    		}
    		this.checkable_relative.classList.remove("checked");
            this.useAbsolutePosition = true;
    	}
    },
    handleCheckableClick: {
    	value: function(event) {
    		if (event.currentTarget.classList.contains("checked")) {
    			event.currentTarget.classList.remove("checked");
    		} else {
    			event.currentTarget.classList.add("checked");
    		}
    	}
    },

	// A layer's ID has been updated in the property panel. We need to update
	// our layer.
	handleLayerIdUpdate: {
		value: function(event) {
			var i = 0,
				arrLayersLength = this.arrLayers.length; 
			for (i = 0; i < arrLayersLength; i++) {
				var myTest = this.arrLayers[i].layerData.stageElement;
				if (this.application.ninja.selectedElements[0] == myTest) {
					this.arrLayers[i].layerData.layerName = event.detail.id;
					this.arrLayers[i].layerName = event.detail.id;
					this.triggerLayerBinding(i);
				}
			}
		}
	},

    // Trigger the layer/track data binding
    triggerLayerBinding : {
    	value: function(intIndex) {
    		if (this.arrLayers[intIndex].layerData.triggerBinding === true) {
    			this.arrLayers[intIndex].layerData.triggerBinding = false;
    		} else {
    			this.arrLayers[intIndex].layerData.triggerBinding = true;
    		}
    	}
    },
    
    handleLayerDragStart : {
    	value: function(event) {
            var dragIcon = document.createElement("img");
            event.dataTransfer.effectAllowed = 'move';
            event.dataTransfer.setData('Text', this.identifier);
            // dragIcon.src = "/images/transparent.png";
            dragIcon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAA1JREFUGFdj+P//PwMACPwC/ohfBuAAAAAASUVORK5CYII="
            dragIcon.width = 1;
            event.dataTransfer.setDragImage(dragIcon, 0, 0);
            
            // Clone the element we're dragging
            this.buildDragHelper();
            
            // Get the offset 
    		var findYOffset = function(obj) {
				var curleft = curtop = 0;
				
				if (obj.offsetParent) {
					do {
							curleft += obj.offsetLeft;
							curtop += obj.offsetTop;
				
						} while (obj = obj.offsetParent);
				}
				return curtop;
    		}
    		this._dragAndDropHelperOffset = findYOffset(this.container_layers);
    		this._appendHelper = true;
    		this._deleteHelper = false;
    	}
    },
    
    buildDragHelper: {
    	value: function() {
    		var myContainer = document.createElement("div"),
    			i = 0, 
    			currentLayersSelectedLength = this.currentLayersSelected.length;
    		
    		for (i = 0; i < currentLayersSelectedLength; i++) {
    			var currentClone = this.layerRepetition.childComponents[this.currentLayersSelected[i]].element.cloneNode(true);
    			currentClone.classList.add("layerSelected");
    			myContainer.appendChild(currentClone);
    			this._dragLayerIndexes.push(this.currentLayersSelected[i]);
    		}
    		this._dragAndDropHelper = myContainer;
            this._dragAndDropHelper.style.opacity = 0.8;
            this._dragAndDropHelper.style.position = "absolute";
            this._dragAndDropHelper.style.top = "0px";
            this._dragAndDropHelper.style.left = "0px";
            this._dragAndDropHelper.style.zIndex = 700;
            
            this._dragAndDropHelper.style.width = window.getComputedStyle(this.container_layers, null).getPropertyValue("width");
            this._dragAndDropHelper.classList.add("timeline-dnd-helper");
    	}
    },
    
    handleLayerDragover: {
    	value: function(event) {
    		
    		// If this isn't a layer event we don't do anything.
    		if (this.draggingType !== "layer") {
    			return;
    		}
    		var currPos = 0,
    			myScrollTest = ((event.y - (this._dragAndDropHelperOffset - this.user_layers.scrollTop)) + 28) - this.user_layers.scrollTop;
    		if ((myScrollTest < 60) && (this.user_layers.scrollTop >0)) {
    			this._scrollTracks = (this.user_layers.scrollTop - 10)
    		}
    		if ((myScrollTest < 50) && (this.user_layers.scrollTop >0)) {
    			this._scrollTracks = (this.user_layers.scrollTop - 20)
    		}
    		if ((myScrollTest > (this.user_layers.clientHeight + 10))) {
    			this._scrollTracks = (this.user_layers.scrollTop + 10)
    		}
    		if ((myScrollTest > (this.user_layers.clientHeight + 20))) {
    			this._scrollTracks = (this.user_layers.scrollTop + 20)
    			
    		}
    		currPos = event.y - (this._dragAndDropHelperOffset - this.user_layers.scrollTop)- 28;
    		this._dragAndDropHelperCoords = currPos + "px";
    		this.needsDraw = true;
    	}
    },
    handleLayerDragEnd : {
    	value: function(event) {
    		
    		// If this isn't a layer event we don't do anything.
    		if (this.draggingType !== "layer") {
    			return;
    		}
    		this._deleteHelper = true;
    		this.needsDraw = true;
           
    	}
    },
    handleLayerDrop : {
    	value: function(event) {
    		
    		// If this isn't a layer event we don't do anything.
    		if (this.draggingType !== "layer") {
    			return;
    		}
            event.stopPropagation();
            event.preventDefault();
            this._deleteHelper = true; 
            this.needsDraw = true;
    	}
    },
    
    // Keyframe drag-and-drop
    handleKeyframeDragover: {
    	value: function(event) {
    		
    		// If this isn't a keyframe drag and drop event, we don't want to do anything.
    		if (this.draggingType !== "keyframe") {
    			return;
    		}
    		event.preventDefault();
    		var currPos = 0;

    		currPos = (event.x + this.layout_tracks.scrollLeft) - 277;
    		
    		// Prevent dragging beyond previous or next keyframe, if any
    		if (currPos < this.trackRepetition.childComponents[this.draggingTrackId]._keyframeMinPosition) {
    			currPos = this.trackRepetition.childComponents[this.draggingTrackId]._keyframeMinPosition;
    		}
    		if (currPos > this.trackRepetition.childComponents[this.draggingTrackId]._keyframeMaxPosition) {
    			currPos = this.trackRepetition.childComponents[this.draggingTrackId]._keyframeMaxPosition;
    		}

			// Automatic scrolling when dragged to edge of window
			if (currPos < (this.layout_tracks.scrollLeft + 10)) {
				this._scrollTracks = (this.layout_tracks.scrollLeft -10);
				this.needsDraw = true;
			}
			if (currPos > (this.layout_tracks.offsetWidth + this.layout_tracks.scrollLeft - 20)) {
				this._scrollTracks = (this.layout_tracks.scrollLeft +10);
				this.needsDraw = true;
			}

			// Set up values in appropriate track and set that track to draw.
    		this.trackRepetition.childComponents[this.draggingTrackId].dragAndDropHelperCoords = currPos + "px";
    		this.trackRepetition.childComponents[this.draggingTrackId].needsDraw = true;
    		return false;
    	}
    },
    handleKeyframeDrop: {
    	value: function(event) {
    		
    		// If this isn't a keyframe drop event, we don't want to do anything.
    		if (this.draggingType !== "keyframe") {
    			return;
    		}
			event.stopPropagation();
			
			var currPos = (event.x + this.layout_tracks.scrollLeft) - 277,
				currentMillisecPerPixel = Math.floor(this.millisecondsOffset / 80),
				currentMillisec = 0,
				i = 0,
				trackIndex = this.draggingTrackId, 
				tweenIndex = this.trackRepetition.childComponents[trackIndex].draggingIndex;
				
			// Make sure drop happens between previous and next keyframe, if any.
    		if (currPos < this.trackRepetition.childComponents[trackIndex]._keyframeMinPosition) {
    			currPos = this.trackRepetition.childComponents[trackIndex]._keyframeMinPosition + 3;
    		}
    		if (currPos > this.trackRepetition.childComponents[trackIndex]._keyframeMaxPosition) {
    			currPos = this.trackRepetition.childComponents[trackIndex]._keyframeMaxPosition + 3;
    		}
    		
    		// Calculate the millisecond values, set repetitions, and update the rule.
    		currentMillisec = currentMillisecPerPixel * currPos;

			this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex].tweenData.spanWidth = 
				currPos - this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex - 1].tweenData.keyFramePosition;
				
			this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex].tweenData.keyFramePosition = currPos;
			this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex].tweenData.keyFrameMillisec = currentMillisec;
			
			this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex].tweenData.spanPosition = 
				currPos - this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex].tweenData.spanWidth;
				
			this.trackRepetition.childComponents[trackIndex].tweenRepetition.childComponents[tweenIndex].setData();
			
			if (tweenIndex < this.trackRepetition.childComponents[trackIndex].tweens.length -1) {
				var spanWidth = this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex +1].tweenData.keyFramePosition - currPos,
					spanPosition = currPos; 
				this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex +1].tweenData.spanWidth = spanWidth;
				this.trackRepetition.childComponents[trackIndex].tweens[tweenIndex +1].tweenData.spanPosition = currPos;
				this.trackRepetition.childComponents[trackIndex].tweenRepetition.childComponents[tweenIndex+1].setData();
			}
			this.trackRepetition.childComponents[trackIndex].tweenRepetition.childComponents[tweenIndex].selectTween();
			this.trackRepetition.childComponents[trackIndex].updateKeyframeRule();
			
			// If this is the last keyframe, we'll need to update the track duration
			if (tweenIndex === (this.trackRepetition.childComponents[trackIndex].tweens.length-1)) {
				this.arrLayers[trackIndex].layerData.trackDuration = currentMillisec;
				this.resetMasterDuration();
			}
			return false;
    	}
    },
    /* === END: Controllers === */
   
    /* === BEGIN: Logging routines === */
    _boolDebug:{
        enumerable:false,
        value:false // set to true to enable debugging to console; false for turning off all debugging.
    },
    boolDebug:{
        get:function () {
            return this._boolDebug;
        },
        set:function (boolDebugSwitch) {
            this._boolDebug = boolDebugSwitch;
        }
    },
    log:{
        value:function (strMessage) {
            if (this.boolDebug) {
                console.log(this.getLineNumber() + ": " + strMessage);
            }
        }
    },
    getLineNumber:{
        value:function () {
            try {
                throw new Error('bazinga')
            } catch (e) {
                return e.stack.split("at")[3].split(":")[2];
            }
        }
    }
    /* === END: Logging routines === */
});