var Montage = require("montage/core/core").Montage; var Component = require("montage/ui/component").Component; var Layer = require("js/panels/Timeline/Layer.reel").Layer; var TimelineTrack = require("js/panels/Timeline/TimelineTrack.reel").TimelineTrack; var nj = require("js/lib/NJUtils").NJUtils; // var Track = require("js/panels/Timeline/Track.reel").Track; var TimelinePanel = exports.TimelinePanel = Montage.create(Component, { hasTemplate: { value: true }, /* === BEGIN: Models === */ // Layer models: arrays for the data and repetition, current layer number, _arrLayers : { value: [] }, arrLayers: { get: function() { return this._arrLayers; }, set: function(newVal) { this._arrLayers = newVal; } }, _layerRepetition: { value: null }, layerRepetition: { get: function() { return this._layerRepetition; }, set: function(newVal) { this._layerRepetition = newVal; } }, currentLayerNumber:{ value:0 }, millisecondsOffset:{ value:5000 }, // Track model _arrTracks: { serializable: true, value: [] }, arrTracks: { serializable: true, get: function() { return this._arrTracks; }, set: function(newVal) { this._arrTracks = newVal; } }, _trackRepetition: { serializable: true, value: null }, trackRepetition : { serializable: true, 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; } }, /* === END: Models === */ /* === BEGIN: Draw cycle === */ prepareForDraw: { value: function() { this.eventManager.addEventListener("deleteLayerClick", this, false); this.eventManager.addEventListener("newLayer", this, false); this.eventManager.addEventListener("deleteLayer", this, false); this.eventManager.addEventListener( "layerBinding", this, false); this.eventManager.addEventListener("elementAdded", this, false); this.eventManager.addEventListener("elementDeleted", this, false); this.eventManager.addEventListener("deleteSelection", this, false); this.hashInstance=this.createLayerHashTable(); this.hashTrackInstance=this.createTrackHashTable(); this.initTimelineView(); } }, _isLayer: { value: false }, _isLayerAdded:{ value:false }, addButtonClicked:{ value:true }, _firstLayerDraw:{ value:false, writable:true }, willDraw: { value: function() { if (this._isLayer) { this.addButtonClicked=false; this._isElementAdded=true; NJevent('newLayer',this) this._isLayer = false; this.addButtonClicked=true; } } }, /* === END: Draw cycle === */ /* === BEGIN: Controllers === */ // Initialize the timeline initTimelineView : { value:function(){ // Get some selectors to make life easier. this.layout_tracks = this.element.querySelector(".layout-tracks"); this.layout_markers = this.element.querySelector(".layout_markers"); // Add event handlers on the buttons. this.newlayer_button.identifier = "addLayer"; this.newlayer_button.addEventListener("click", this, false); this.deletelayer_button.identifier = "deleteLayer"; this.deletelayer_button.addEventListener("click", this, false); // New click listener to handle select/deselect events this.timeline_leftpane.addEventListener("click", this.timelineLeftPaneClick.bind(this), false); this.timeline_leftpane.addEventListener("blur", this.timelineLeftPanelBlur.bind(this), false); this.timeline_leftpane.addEventListener("mousedown", this.timelineLeftPanelMousedown.bind(this), false); // Simultaneous scrolling of the layer and tracks this.layout_tracks.addEventListener("scroll", this.updateLayerScroll.bind(this), false); this.user_layers.addEventListener("scroll", this.updateLayerScroll.bind(this), false); // Calculate and draw time markers this.drawTimeMarkers(); // Default to one layer for new doc // this.newLayer(); this._hashKey="123"; _firstLayerDraw = false; NJevent('newLayer',this._hashKey); _firstLayerDraw = true; // TODO - add condition for existing doc and parse DOM for top level elements } }, updateLayerScroll:{ value:function(){ console.log(this.layout_tracks.scrollLeft) this.user_layers.scrollTop = this.layout_tracks.scrollTop; this.layout_markers.scrollLeft = this.layout_tracks.scrollLeft; } }, deselectKeyframes:{ value:function () { for (var i = 0; i < this.selectedKeyframes.length; i++) { this.selectedKeyframes[i].deselect(); } this.selectedKeyframes = null; this.selectedKeyframes = new Array(); } }, handleAddLayerClick:{ value:function(event){ //event.stopPropagation(); this._isLayer = true; this.needsDraw = true; } }, handleDeleteLayerClick:{ value:function(event){ //event.stopPropagation(); this._deleteKeyDown=false; if(this.application.ninja.currentSelectedContainer.id==="UserContent"){ this._hashKey="123"; } NJevent('deleteLayer') } }, handleLayerBinding:{ value:function(event){ var i=0; this.currentParentNode=this.application.ninja.currentSelectedContainer.parentNode; this.removeLayerFromParentUUid = this.application.ninja.currentSelectedContainer.parentNode.uuid; this.currentElement= event.detail.element; // if(this._firstLayerDraw===false){ // while(this.arrLayers.pop()){ // } // while(this.arrTracks.pop()){ // } // } this._hashKey = event.detail.element.uuid; if(this.returnedObject = this.hashInstance.getItem(this._hashKey)){ this.returnedTrack = this.hashTrackInstance.getItem(this._hashKey); this._hashFind = true; NJevent('newLayer'); } } }, timelineLeftPaneClick : { value: function(event) { // Check ALL THE CLICKS // Are they in a particular layer? If so, we need to select that layer and // deselect the others. var ptrParent = nj.queryParentSelector(event.target, ".container-layer"); if (ptrParent !== false) { // Why yes, the click was within a layer. But which one? var strLabel = ptrParent.querySelector(".label-layer .collapsible-label").innerText, myIndex = 0, i = 0, arrLayersLength = this.arrLayers.length; console.log(strLabel); for (i = 0; i < arrLayersLength; i++) { if (this.arrLayers[i].layerName === strLabel) { myIndex = i; this.arrLayers[i].isSelected = true; } else { this.arrLayers[i].isSelected = false; } } } } }, timelineLeftPanelBlur: { value: function(event) { console.log('blur called with ' + this._skipNextBlur); if (this._skipNextBlur === true) { this._skipNextBlur = false; } else { var i = 0, arrLayersLength = this.arrLayers.length; for (i = 0; i < arrLayersLength; i++) { this.arrLayers[i].isSelected = false; } this.layerRepetition.selectedIndexes = null; } } }, _skipNextBlur : { value: false }, timelineLeftPanelMousedown : { value: function(event) { console.log(event.target.classList) var ptrParent = nj.queryParentSelector(event.target, ".label-style"); console.log('mousedown with ' + ptrParent) if (ptrParent !== false) { this._skipNextBlur = true; } } }, handleNewLayer:{ value:function(event){ // Add a new layer. It should be added above the currently selected layer, // Or at the end, if no layer is selected. var hashIndex =0 ,hashVariable=0,layerResult,trackResult,layerObject,trackObject,dLayer,parentNode; if(this._hashFind){ while(layerResult = this.returnedObject[hashIndex]){ trackResult=this.returnedTrack[hashIndex]; if(layerResult.deleted!==true){ this.arrLayers.push(layerResult); this.arrTracks.push(trackResult); } hashIndex++; } this._hashFind=false; return; } // if(this.addButtonClicked){ // layerObject = this.hashInstance.getItem(this.application.ninja.currentSelectedContainer.uuid); // trackObject = this.hashTrackInstance.getItem(this.application.ninja.currentSelectedContainer.uuid); // if(layerObject!==undefined){ // while(layerObject[hashVariable]){ // if(event.detail.parentElement!==this.application.ninja.currentSelectedContainer){ // dLayer=this.hashInstance.getItem(event.detail.parentNode.uuid); // while(dLayer[hashVariable]){ // if(dLayer[hashVariable].element===event.detail){ // dLayer[hashVariable].deleted=true; // parentNode=dLayer[hashVariable].parentElement; // break; // } // hashVariable++; // } // this._setBreadCrumb=true; // NJevent('breadCrumbTrail',{"element":parentNode,"setFlag":this._setBreadCrumb}); // // } // else if(layerObject[hashVariable].element===event.detail){ // this.arrLayers.splice(layerObject[hashVariable].layerPosition,0,layerObject[hashVariable]); // this.arrTracks.splice(trackObject[hashVariable].trackPosition,0,trackObject[hashVariable]); // this._isLayerAdded=true; // break; // } // hashVariable++; // this._isLayerAdded=false; // } // } // } var newLayerName = "", //thingToPush = Layer.create(), thingToPush = {}, // newTrack = TimelineTrack.create(), newTrack = {}, myIndex = 0; // Build the thingToPush object if(_firstLayerDraw){ if(this.application.ninja.currentSelectedContainer.id==="UserContent"){ this._hashKey="123"; } } this.currentLayerNumber = this.currentLayerNumber +1; newLayerName = "Layer " + this.currentLayerNumber; thingToPush.layerName = newLayerName; thingToPush.layerID = this.currentLayerNumber; thingToPush.isMainCollapsed = true; thingToPush.isPositionCollapsed = true; thingToPush.isTransformCollapsed = true; thingToPush.isStyleCollapsed = false; thingToPush.arrLayerStyles = []; thingToPush.element=[]; thingToPush.deleted=false; thingToPush.isSelected = false; thingToPush.parentElement=this.application.ninja.currentSelectedContainer; // this.layerElement.dataset.parentUUID=this.application.ninja.currentSelectedContainer.uuid; newTrack.trackID = this.currentLayerNumber; newTrack.isMainCollapsed = true; newTrack.isPositionCollapsed = true; newTrack.isTransformCollapsed = true; newTrack.isStyleCollapsed = false; newTrack.tweens = []; // If a layer is selcted, splice the new layer on top // Otherwise, just push the new layer in at the bottom. if (!!this.layerRepetition.selectedIndexes) { myIndex = this.layerRepetition.selectedIndexes[0]; this.hashInstance.setItem(this._hashKey,thingToPush,myIndex); this.hashTrackInstance.setItem(this._hashKey,newTrack,myIndex); thingToPush.layerPosition=myIndex; thingToPush.isSelected = true; newTrack.trackPosition=myIndex; this.arrLayers.splice(myIndex, 0, thingToPush); this.arrTracks.splice(myIndex, 0, newTrack); this.currentLayerSelected= this.arrLayers[myIndex]; var i = 0, arrLayersLength = this.arrLayers.length; for (i = 0; i < arrLayersLength; i++) { if (i === myIndex) { this.arrLayers[i].isSelected = true; } else { this.arrLayers[i].isSelected = false; } } this.layerRepetition.selectedIndexes = [myIndex]; } else { this.arrLayers.splice(0, 0, thingToPush); this.arrTracks.splice(0, 0, newTrack); thingToPush.layerPosition=this.arrLayers.length-1; newTrack.trackPosition=this.arrTracks.length-1; this.currentLayerSelected= this.arrLayers[this.arrLayers.length-1]; this.hashInstance.setItem(this._hashKey,thingToPush,thingToPush.layerPosition); this.hashTrackInstance.setItem(this._hashKey,newTrack,newTrack.trackPosition); } } }, handleDeleteLayer:{ value:function(event){ var dLayer,dTrack,parentNode,hashVariable=0,k=0,index=0,j=0; if (this.arrLayers.length > 0) { if(this._undoElementDeleted){ if(event.detail.dataset.parentUUID!==this.application.ninja.currentSelectedContainer.uuid){ dLayer=this.hashInstance.getItem(event.detail.dataset.parentUUID); while(dLayer[hashVariable]){ if(dLayer[hashVariable].element===event.detail){ dLayer[hashVariable].deleted=true; parentNode=dLayer[hashVariable].parentElement; break; } hashVariable++; } this._setBreadCrumb=true; NJevent('breadCrumbTrail',{"element":parentNode,"setFlag":this._setBreadCrumb}); }else{ dLayer=this.hashInstance.getItem(event.detail.dataset.parentUUID) while(dLayer[hashVariable]){ if(dLayer[hashVariable].deleted===true){ }else if(dLayer[hashVariable].element.uuid === event.detail.uuid){ while(this.arrLayers.length){ if(dLayer[hashVariable].layerID===this.arrLayers[k].layerID){ dLayer[hashVariable].deleted=true; this.arrLayers.splice(k,1); this.arrTracks.splice(k,1); break; } k++; } } hashVariable++; } } } else{ if (!!this.layerRepetition.selectedIndexes) { var myIndex = this.layerRepetition.selectedIndexes[0]; dLayer = this.hashInstance.getItem(this._hashKey); dTrack = this.hashTrackInstance.getItem(this._hashKey); dLayer[myIndex].deleted=true; this.arrLayers.splice(myIndex, 1); this.arrTracks.splice(myIndex, 1); } else if(this._deleteKeyDown) { dLayer = this.hashInstance.getItem(this._hashKey); dTrack = this.hashTrackInstance.getItem(this._hashKey); if(this.deleteElement === this.application.ninja.currentSelectedContainer){ while(dLayer[hashVariable]){ dLayer[hashVariable].deleted=true; hashVariable++; } this.dObject=this.hashInstance.getItem(this.removeLayerFromParentUUid); hashVariable=0; while(this.dObject[hashVariable]){ if(this.application.ninja.currentSelectedContainer===this.dObject[hashVariable].element){ this.dObject[hashVariable].deleted=true; this._setBreadCrumb=true; NJevent('breadCrumbTrail',{"element":this.currentParentNode,"setFlag":this._setBreadCrumb}); this._setBreadCrumb=false; break; } hashVariable++; } this._deleteKeyDown=false; } else if(this.deleteElement!== this.application.ninja.currentSelectedContainer){ while(dLayer[hashVariable]){ if(dLayer[hashVariable].deleted===true){ }else if(dLayer[hashVariable].element.uuid === this.deleteElement.uuid){ while(this.arrLayers.length){ if(dLayer[hashVariable].layerID===this.arrLayers[k].layerID){ dLayer[hashVariable].deleted=true; this.arrLayers.splice(k,1); this.arrTracks.splice(k,1); break; } k++; } } hashVariable++; } } } else{ dLayer = this.hashInstance.getItem(this._hashKey); dTrack = this.hashTrackInstance.getItem(this._hashKey); dLayer[this.arrLayers.length-1].deleted=true; this.arrLayers.pop(); this.arrTracks.pop(); } } }else if (this.arrLayers.length <= 0) { if(this._undoElementDeleted){ if(event.detail.dataset.parentUUID!==this.application.ninja.currentSelectedContainer.uuid){ dLayer=this.hashInstance.getItem(event.detail.dataset.parentUUID); while(dLayer[hashVariable]){ if(dLayer[hashVariable].element===event.detail){ dLayer[hashVariable].deleted=true; parentNode=dLayer[hashVariable].parentElement; break; } hashVariable++; } this._setBreadCrumb=true; NJevent('breadCrumbTrail',{"element":parentNode,"setFlag":this._setBreadCrumb}); } }else if(this._deleteKeyDown) { this.dObject=this.hashInstance.getItem(this.removeLayerFromParentUUid); hashVariable=0; while(this.dObject[hashVariable]){ if(this.application.ninja.currentSelectedContainer===this.dObject[hashVariable].element){ this.dObject[hashVariable].deleted=true; this._setBreadCrumb=true; NJevent('breadCrumbTrail',{"element":this.currentParentNode,"setFlag":this._setBreadCrumb}); this._setBreadCrumb=false; break; } hashVariable++; } } } // TODO: actually remove the selected style from the layer. (Maybe by publishing an event?) } }, handleElementAdded: { value: function(event) { this.layerElement=event.detail; this._isElementAdded=true; this.currentLayerSelected.element.push(event.detail); this._isElementAdded=false; } }, // handleDeleteSelection:{ // value:function(event){ // var length; // this._deleteKeyDown=true; // this.deleteElement = event.detail[0]; // length =this.currentLayerSelected.element.length-1; // while(length >= 0){ // if(this.currentLayerSelected.element[length]===this.deleteElement){ // this.currentLayerSelected.element.splice(length,0); // } // } // // } // }, handleElementDeleted: { value: function(event) { var length; this._deleteKeyDown=true; this.deleteElement = event.detail; length =this.currentLayerSelected.element.length-1; while(length >= 0){ if(this.currentLayerSelected.element[length]===this.deleteElement){ this.currentLayerSelected.element.splice(length,1); break; } length--; } } }, drawTimeMarkers:{ value:function(){ var i; var totalMarkers = Math.floor(this.time_markers.offsetWidth / 80); for(i=0;i