Index: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilBody.js |
— | — | @@ -366,8 +366,7 @@ |
367 | 367 | $node.data( 'implictDuration', childDuration); |
368 | 368 | } |
369 | 369 | } |
370 | | - }); |
371 | | - alert( 'getting duration for ' + $node.children().length + ' children '); |
| 370 | + }); |
372 | 371 | } |
373 | 372 | |
374 | 373 | // Check the explicit duration attribute: |
Index: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.Smil.js |
— | — | @@ -86,34 +86,38 @@ |
87 | 87 | * @param {string} |
88 | 88 | * SmilXmlString Xml string of smil to be loaded |
89 | 89 | */ |
90 | | - loadFromString : function( smilXmlString ) { |
| 90 | + loadFromString: function( smilXmlString ) { |
91 | 91 | // Load the parsed string into the local "dom" |
92 | 92 | this.$dom = $j( smilXmlString ); |
93 | | - |
94 | 93 | mw.log("Smil::loadFromString: loaded smil dom: " + this.$dom.length + "\n" + smilXmlString ); |
95 | | - /* |
96 | | - // Clear out the layout |
97 | | - this.layout = null; |
98 | | - |
99 | | - // Clear out the body |
100 | | - this.body = null; |
101 | | - |
102 | | - // Clear out the top level duration |
103 | | - this.duration = null; |
104 | | - |
105 | | - // Clear out the "buffer" object |
106 | | - this.buffer = null; |
107 | | - */ |
108 | 94 | }, |
109 | 95 | updateFromString: function( smilXmlString ){ |
110 | 96 | delete this.$dom; |
111 | | - this.$dom = $j( smilXmlString ); |
| 97 | + // jQuery strips some html native tags when parsing xml passed into jQuery |
| 98 | + // since smil has html tags ( "body" "head" ) we need to first convert it to |
| 99 | + // an xml object:: |
| 100 | + this.$dom = $j( this.getXMLDomObject( smilXmlString ) ); |
112 | 101 | }, |
| 102 | + // simple XML DOMParser object parser wrapper |
| 103 | + // xxx Add error handling |
| 104 | + getXMLDomObject: function( smilXmlString ){ |
| 105 | + if (window.DOMParser){ |
| 106 | + parser=new DOMParser(); |
| 107 | + xmlDoc=parser.parseFromString(smilXmlString, "text/xml"); |
| 108 | + } else // Internet Explorer |
| 109 | + { |
| 110 | + xmlDoc=new ActiveXObject("Microsoft.XMLDOM"); |
| 111 | + xmlDoc.async="false"; |
| 112 | + xmlDoc.loadXML( smilXmlString ); |
| 113 | + } |
| 114 | + return xmlDoc; |
| 115 | + }, |
| 116 | + |
113 | 117 | /** |
114 | 118 | * Internal function to get the jQuery smil dom |
115 | 119 | */ |
116 | 120 | getDom : function() { |
117 | | - if (this.$dom) { |
| 121 | + if ( this.$dom ) { |
118 | 122 | return this.$dom; |
119 | 123 | } |
120 | 124 | mw.log("Error SMIL Dom not available"); |
— | — | @@ -132,7 +136,7 @@ |
133 | 137 | this.getLayout().setupLayout(this.embedPlayer.getRenderTarget()); |
134 | 138 | |
135 | 139 | // Update the render target with bodyElements for the requested time |
136 | | - this.getBody().renderTime(time); |
| 140 | + this.getBody().renderTime( time ); |
137 | 141 | |
138 | 142 | // Wait until buffer is ready and run the callback |
139 | 143 | this.getBuffer().addAssetsReadyCallback(callback); |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.Sequencer.js |
— | — | @@ -97,19 +97,14 @@ |
98 | 98 | */ |
99 | 99 | updateSmilXML: function( smilXML ){ |
100 | 100 | mw.log("Sequencer::updateSmilXML" + smilXML); |
101 | | - var _this = this; |
102 | | - alert( 'before Dur:' + this.getEmbedPlayer().getDuration( true ) + ' update seq len: ' + this.getSmil().$dom.find('seq').children().length ) |
| 101 | + var _this = this; |
103 | 102 | // Update the embedPlayer smil: |
104 | | - this.getSmil().updateFromString( smilXML ); |
105 | | - debugger; |
106 | | - alert( 'after Dur:' + this.getEmbedPlayer().getDuration( true ) + ' update seq len: ' + this.getSmil().$dom.find('seq').children().length ) |
| 103 | + this.getSmil().updateFromString( smilXML ); |
107 | 104 | // Get a duration ( forceRefresh to clear the cache ) |
108 | | - var dur = this.getEmbedPlayer().getDuration( true ); |
109 | | - alert( 'restored dur should be: ' + dur); |
110 | | - /* |
111 | | - // redraw the timeline |
112 | | - this.getTimeline().drawTimeline(); |
113 | | - */ |
| 105 | + this.getEmbedPlayer().getDuration( true ); |
| 106 | + |
| 107 | + // Redraw the timeline |
| 108 | + this.getTimeline().drawTimeline(); |
114 | 109 | }, |
115 | 110 | |
116 | 111 | /** |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTimeline.js |
— | — | @@ -27,9 +27,11 @@ |
28 | 28 | getTimelineContainer: function(){ |
29 | 29 | return this.sequencer.getContainer().find('.mwseq-timeline'); |
30 | 30 | }, |
31 | | - |
32 | | - getTracksContainer: function(){ |
33 | | - if( ! this.$timelineTracksContainer ){ |
| 31 | + /** |
| 32 | + * xxx needs to support multiple tracks |
| 33 | + */ |
| 34 | + getTracksContainer: function( trackId ){ |
| 35 | + if( this.getTimelineContainer().find( '.timelineTrackContainer' ).length == 0 ){ |
34 | 36 | // getTimelineContainer |
35 | 37 | this.getTimelineContainer().append( |
36 | 38 | $j('<div />') |
— | — | @@ -43,8 +45,7 @@ |
44 | 46 | ) |
45 | 47 | ) |
46 | 48 | // Apply layout control to track name / clipTrackSet division |
47 | | - this.$timelineTracksContainer = this.getTimelineContainer().find( '.timelineTrackContainer'); |
48 | | - this.trackLayout = this.$timelineTracksContainer |
| 49 | + this.getTimelineContainer().find( '.timelineTrackContainer') |
49 | 50 | .layout( { |
50 | 51 | 'applyDefaultStyles': true, |
51 | 52 | 'west__size' : 150, |
— | — | @@ -52,56 +53,52 @@ |
53 | 54 | 'west__maxSize' : 300 |
54 | 55 | } ); |
55 | 56 | } |
56 | | - return this.$timelineTracksContainer; |
| 57 | + return this.getTimelineContainer().find( '.timelineTrackContainer'); |
57 | 58 | }, |
58 | 59 | resizeTimeline: function(){ |
59 | | - if( this.trackLayout ){ |
60 | | - this.trackLayout.resizeAll(); |
61 | | - } |
| 60 | + this.getTimelineContainer().find( '.timelineTrackContainer').resizeAll(); |
62 | 61 | }, |
63 | 62 | |
64 | 63 | // Draw the timeline |
65 | 64 | drawTimeline: function(){ |
66 | 65 | // Empty the timeline container |
67 | | - this.getTimelineContainer().empty(); |
| 66 | + //this.getTimelineContainer().empty(); |
68 | 67 | |
69 | 68 | // Get the top level sequence tracks |
70 | 69 | var seqTracks = this.sequencer.getSmil().getBody().getSeqElements(); |
71 | | - var trackType = 'video'; |
72 | | - // For now just two tracks first is video second is audio |
73 | | - for( var trackIndex=0; trackIndex < seqTracks.length; trackIndex++){ |
74 | | - |
75 | | - if( trackType == 'audio' ){ |
76 | | - mw.log("SequencerTimeline::Error only two tracks presently suppoted"); |
77 | | - break; |
78 | | - } |
79 | | - // Draw the sequence track |
80 | | - this.drawSequenceTrack( trackIndex, seqTracks[ trackIndex ], trackType); |
81 | | - trackType = 'audio'; |
82 | | - } |
| 70 | + // For now just one video track: |
| 71 | + this.drawSequenceTrack( 0, seqTracks[ 0 ], 'video'); |
83 | 72 | }, |
84 | 73 | |
85 | 74 | drawSequenceTrack: function( trackIndex, sequenceNode, trackType ){ |
86 | 75 | var _this = this; |
87 | | - mw.log(" drawSequenceTrack: Track inx: " + trackIndex + ' trackType:' + trackType ); |
88 | | - // Check if we already have a container for this track set |
| 76 | + mw.log("SequenceTimeline::drawSequenceTrack: Track inx: " + trackIndex + ' trackType:' + trackType ); |
| 77 | + // Check if we already have a container for this track set |
89 | 78 | |
90 | | - // Add a sequence track Name |
91 | | - this.getTracksContainer().find('.trackNamesContainer').append( |
92 | | - this.getTrackNameInterface( trackIndex, sequenceNode, trackType ) |
93 | | - ) |
| 79 | + // Add / update the sequence track name if not present |
| 80 | + // xxx check for specific sequenceTrack updates that require interface update |
| 81 | + if( this.getTracksContainer().find('.trackNamesContainer').children().length == 0 ){ |
| 82 | + this.getTracksContainer().find('.trackNamesContainer').append( |
| 83 | + this.getTrackNameInterface( trackIndex, sequenceNode, trackType ) |
| 84 | + ) |
| 85 | + }; |
94 | 86 | |
95 | | - // Add Sequence clips |
96 | | - this.getTracksContainer().find('.clipTrackSetContainer').append( |
97 | | - this.getTrackClipInterface( trackIndex ,sequenceNode , trackType ) |
98 | | - ).click( function(){ |
99 | | - // xxx todo catch de-select clicks in clipTrackSetContainer that are not a click in the timeline |
100 | | - //_this.getTracksContainer().find('.timelineClip').removeClass( 'selectedClip' ); |
101 | | - }) |
| 87 | + // Add Sequence track clips |
| 88 | + // xxx check for specific sequenceTrack updates that require interface update |
| 89 | + this.drawTrackClipsInterface( trackIndex ,sequenceNode , trackType ) |
| 90 | + /*if( this.getTracksContainer().find('.clipTrackSetContainer').childrend().length == 0 ){ |
| 91 | + this.getTracksContainer().find('.clipTrackSetContainer').append( |
| 92 | + |
| 93 | + ).click( function(){ |
| 94 | + // xxx todo catch de-select clicks in clipTrackSetContainer that are not a click in the timeline |
| 95 | + //_this.getTracksContainer().find('.timelineClip').removeClass( 'selectedClip' ); |
| 96 | + }) |
| 97 | + } |
| 98 | + */ |
102 | 99 | // Load and display all clip thumbnails |
103 | 100 | this.drawTrackThumbs( trackIndex, sequenceNode, trackType ); |
104 | 101 | }, |
105 | | - |
| 102 | + |
106 | 103 | drawTrackThumbs: function( trackIndex, sequenceNode, trackType ){ |
107 | 104 | var _this = this; |
108 | 105 | var smil = this.sequencer.getSmil(); |
— | — | @@ -121,67 +118,80 @@ |
122 | 119 | }); |
123 | 120 | }, |
124 | 121 | /** |
125 | | - * Get Track Clip Interface |
| 122 | + * add Track Clips and Interface binding |
126 | 123 | */ |
127 | | - getTrackClipInterface: function( trackIndex, sequenceNode, trackType ){ |
| 124 | + drawTrackClipsInterface: function( trackIndex, sequenceNode, trackType ){ |
128 | 125 | var _this = this; |
129 | | - // setup a local pointer to the smil engine: |
| 126 | + // Setup a local pointer to the smil engine: |
130 | 127 | var smil = this.sequencer.getSmil(); |
| 128 | + |
131 | 129 | // Get all the refs that are children of the sequenceNode with associated offsets and durations |
132 | 130 | // for now assume all tracks start at zero: |
133 | 131 | var startOffset = 0; |
134 | 132 | var clipTrackSetId = this.sequencer.getId() + '_clipTrackSet_' + trackIndex; |
135 | 133 | |
136 | | - var $clipTrackSet = |
137 | | - $j('<ul />') |
138 | | - .attr('id', clipTrackSetId) |
139 | | - .addClass('clipTrackSet ui-corner-all') |
140 | | - // Add "sortable |
141 | | - .sortable({ |
142 | | - placeholder: "clipSortTarget timelineClip ui-corner-all", |
143 | | - opacity: 0.6, |
144 | | - cursor: 'move', |
145 | | - helper: function( event, helper ){ |
146 | | - // xxxx might need some fixes for multi-track |
147 | | - var $selected = _this.getTimelineContainer().find( '.selectedClip' ) |
148 | | - if ( $selected.length === 0 || $selected.length == 1) { |
149 | | - return $j( helper ); |
150 | | - } |
151 | | - |
152 | | - return $j('<ul />') |
153 | | - .css({ |
154 | | - 'width' : (_this.timelineThumbSize.width + 16) * $selected.length |
155 | | - }) |
156 | | - .append( $selected.clone() ); |
157 | | - }, |
158 | | - scroll: true, |
159 | | - update: function( event, ui ) { |
160 | | - // Update the html dom |
161 | | - _this.handleReorder( ui.item ); |
162 | | - } |
163 | | - }) |
164 | | - |
165 | | - smil.getBody().getRefElementsRecurse( sequenceNode, startOffset, function( $node ){ |
166 | | - // Draw the node onto the timeline: |
167 | | - |
168 | | - // xxx would be good to support both "storyboard" and "timeline" view modes. |
169 | | - // for now just "storyboard" |
170 | | - |
171 | | - // add a clip float left box container |
172 | | - $clipTrackSet.append( |
173 | | - $j('<li />') |
174 | | - .attr('id', _this.getTimelineClipId( $node ) ) |
175 | | - .data( { |
176 | | - 'smilId': $node.attr('id'), |
177 | | - 'prevIndex' : $clipTrackSet.length |
| 134 | + var $clipTrackSet = this.getTracksContainer().find('.clipTrackSetContainer').find( '.clipTrackSet' ); |
| 135 | + // Add the $clipTrackSet if not already in dom: |
| 136 | + if( $clipTrackSet.length == 0 ){ |
| 137 | + $clipTrackSet = this.getTracksContainer().find('.clipTrackSetContainer').append( |
| 138 | + $j('<ul />') |
| 139 | + .attr('id', clipTrackSetId) |
| 140 | + .addClass('clipTrackSet ui-corner-all') |
| 141 | + // Add "sortable |
| 142 | + .sortable({ |
| 143 | + placeholder: "clipSortTarget timelineClip ui-corner-all", |
| 144 | + opacity: 0.6, |
| 145 | + cursor: 'move', |
| 146 | + helper: function( event, helper ){ |
| 147 | + // xxxx might need some fixes for multi-track |
| 148 | + var $selected = _this.getTimelineContainer().find( '.selectedClip' ) |
| 149 | + if ( $selected.length === 0 || $selected.length == 1) { |
| 150 | + return $j( helper ); |
| 151 | + } |
| 152 | + |
| 153 | + return $j('<ul />') |
| 154 | + .css({ |
| 155 | + 'width' : (_this.timelineThumbSize.width + 16) * $selected.length |
| 156 | + }) |
| 157 | + .append( $selected.clone() ); |
| 158 | + }, |
| 159 | + scroll: true, |
| 160 | + update: function( event, ui ) { |
| 161 | + // Update the html dom |
| 162 | + _this.handleReorder( ui.item ); |
| 163 | + } |
178 | 164 | }) |
179 | | - .addClass('timelineClip ui-corner-all') |
180 | | - .loadingSpinner() |
181 | | - .click(function(){ |
182 | | - //Add clip to selection |
183 | | - _this.handleMultiSelect( this ); |
184 | | - }) |
185 | | - ); |
| 165 | + ).find( '.clipTrackSet' ) |
| 166 | + } |
| 167 | + var $previusClip = null; |
| 168 | + smil.getBody().getRefElementsRecurse( sequenceNode, startOffset, function( $node ){ |
| 169 | + // Draw the node onto the timeline if the clip is not already there: |
| 170 | + if( $clipTrackSet.find('#' + _this.getTimelineClipId( $node ) ).length == 0 ){ |
| 171 | + var $timelineClip = $j('<li />') |
| 172 | + .attr('id', _this.getTimelineClipId( $node ) ) |
| 173 | + .data( { |
| 174 | + 'smilId': $node.attr('id'), |
| 175 | + 'prevIndex' : $clipTrackSet.length |
| 176 | + }) |
| 177 | + .addClass('timelineClip ui-corner-all') |
| 178 | + .loadingSpinner() |
| 179 | + .click(function(){ |
| 180 | + //Add clip to selection |
| 181 | + _this.handleMultiSelect( this ); |
| 182 | + }) |
| 183 | + if( $previusClip ){ |
| 184 | + $previusClip.after( |
| 185 | + $timelineClip |
| 186 | + ) |
| 187 | + } else { |
| 188 | + // Add to the start of the track set: |
| 189 | + $clipTrackSet.prepend( |
| 190 | + $timelineClip |
| 191 | + ); |
| 192 | + } |
| 193 | + } |
| 194 | + // Update the $previusClip |
| 195 | + $previusClip = $clipTrackSet.find('#' + _this.getTimelineClipId( $node ) ); |
186 | 196 | }) |
187 | 197 | // Give the track set a width relative to the number of clips |
188 | 198 | $clipTrackSet.css('width', ($clipTrackSet.find( '.timelineClip' ).length + 1) * |
— | — | @@ -197,8 +207,7 @@ |
198 | 208 | 'delete': function(){ |
199 | 209 | _this.removeSelectedClips(); |
200 | 210 | } |
201 | | - }) |
202 | | - return $clipTrackSet; |
| 211 | + }) |
203 | 212 | }, |
204 | 213 | // calls the edit interface passing in the selected clip: |
205 | 214 | editClip: function( selectedClip ){ |
— | — | @@ -410,18 +419,24 @@ |
411 | 420 | .css( { |
412 | 421 | 'top': '0px', |
413 | 422 | 'position' : 'absolute', |
414 | | - 'opacity' : '.8', |
| 423 | + 'opacity' : '.9', |
415 | 424 | 'left': '0px', |
416 | 425 | 'height': _this.timelineThumbSize.height |
417 | 426 | }) |
418 | 427 | .attr( 'src', smil.getAssetUrl( $node.attr('poster') ) ) |
419 | | - .load( function(){ |
| 428 | + .load( function(){ |
420 | 429 | if( $thumbTarget.children().length == 0 ){ |
421 | | - $thumbTarget.html(this); |
| 430 | + $thumbTarget.html( img ); |
422 | 431 | } |
423 | | - }) |
| 432 | + }); |
| 433 | + |
| 434 | + // Sometimes the load event does not fire force the fallback image after 5 seconds |
| 435 | + setTimeout( function(){ |
| 436 | + if( $thumbTarget.children().length == 0 ){ |
| 437 | + $thumbTarget.html( img ); |
| 438 | + } |
| 439 | + }, 5000); |
424 | 440 | } |
425 | | - |
426 | 441 | // Buffer the asset then render it into the layout target: |
427 | 442 | smil.getBuffer().bufferedSeek( $node, relativeTime, function(){ |
428 | 443 | // Add the seek, add to canvas and draw thumb request |