Index: branches/MwEmbedStandAlone/mwEmbed.js |
— | — | @@ -2380,33 +2380,37 @@ |
2381 | 2381 | 'text' : '', |
2382 | 2382 | |
2383 | 2383 | // The icon id that precedes the button link: |
2384 | | - 'icon_id' : 'carat-1-n' |
| 2384 | + 'icon' : 'carat-1-n' |
2385 | 2385 | }; |
2386 | 2386 | |
2387 | 2387 | $.button = function( options ) { |
2388 | | - var options = $j.extend( mw_default_button_options, options); |
| 2388 | + var options = $j.extend( {}, mw_default_button_options, options); |
2389 | 2389 | |
2390 | 2390 | // Button: |
2391 | | - var $btn = $j('<a />') |
| 2391 | + var $button = $j('<a />') |
2392 | 2392 | .attr('href', '#') |
2393 | 2393 | .addClass( 'ui-state-default ui-corner-all ui-icon_link' ); |
2394 | 2394 | // Add css if set: |
2395 | 2395 | if( options.css ) { |
2396 | | - $btn.css( options.css ) |
| 2396 | + $button.css( options.css ) |
2397 | 2397 | } |
2398 | 2398 | |
2399 | 2399 | if( options['class'] ) { |
2400 | | - $btn.addClass( options['class'] ) |
| 2400 | + $button.addClass( options['class'] ) |
2401 | 2401 | } |
2402 | 2402 | |
2403 | 2403 | |
2404 | 2404 | // return the button: |
2405 | | - return $btn.append( |
2406 | | - $j('<span />').addClass( 'ui-icon ui-icon-' + options.icon_id ), |
2407 | | - $j('<span />').addClass( 'btnText' ) |
2408 | | - .text( options.text ) |
2409 | | - ) |
2410 | | - .buttonHover(); // add buttonHover binding; |
| 2405 | + $button.append( |
| 2406 | + $j('<span />').addClass( 'ui-icon ui-icon-' + options.icon ), |
| 2407 | + $j('<span />').addClass( 'btnText' ) |
| 2408 | + .text( options.text ) |
| 2409 | + ) |
| 2410 | + .buttonHover(); // add buttonHover binding; |
| 2411 | + if( !options.text ){ |
| 2412 | + $button.css('padding', '1em'); |
| 2413 | + } |
| 2414 | + return $button; |
2411 | 2415 | }; |
2412 | 2416 | |
2413 | 2417 | // Shortcut to bind hover state |
Index: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilAnimate.js |
— | — | @@ -197,7 +197,7 @@ |
198 | 198 | * @param {float} animateTime The relative time to be transformed. |
199 | 199 | */ |
200 | 200 | transformElement: function( smilElement, animateTime ) { |
201 | | - mw.log("SmilAnimate::transformForTime:" + animateTime ); |
| 201 | + //mw.log("SmilAnimate::transformForTime:" + animateTime ); |
202 | 202 | switch( this.smil.getRefType( smilElement ) ){ |
203 | 203 | case 'smiltext': |
204 | 204 | this.transformTextForTime( smilElement, animateTime ); |
Index: branches/MwEmbedStandAlone/modules/TimedText/mw.TimedTextEdit.js |
— | — | @@ -226,7 +226,7 @@ |
227 | 227 | 'style': { 'float' : 'left' }, |
228 | 228 | 'class': 'language-select-btn', |
229 | 229 | 'text': gM('mwe-timedtext-select-language'), |
230 | | - 'icon_id': 'triangle-1-e' |
| 230 | + 'icon': 'triangle-1-e' |
231 | 231 | } ) |
232 | 232 | .attr('id', 'language-select') |
233 | 233 | ) |
— | — | @@ -298,7 +298,7 @@ |
299 | 299 | $j.button( { |
300 | 300 | 'style': { 'float' : 'left' }, |
301 | 301 | 'text': gM('mwe-timedtext-upload-text'), |
302 | | - 'icon_id': 'disk' |
| 302 | + 'icon': 'disk' |
303 | 303 | } ) |
304 | 304 | .click( function() { |
305 | 305 | _this.uploadTextFile(); |
Index: branches/MwEmbedStandAlone/modules/AddMedia/mw.RemoteSearchDriver.js |
— | — | @@ -988,7 +988,7 @@ |
989 | 989 | |
990 | 990 | |
991 | 991 | var $searchButton = $j.button({ |
992 | | - icon_id: 'search', |
| 992 | + icon: 'search', |
993 | 993 | text: gM( 'mwe-media_search' ) |
994 | 994 | }) |
995 | 995 | .addClass( 'rsd_search_button' ) |
— | — | @@ -1027,7 +1027,7 @@ |
1028 | 1028 | |
1029 | 1029 | // Add optional upload buttons. |
1030 | 1030 | if ( this.content_providers['upload'].enabled) { |
1031 | | - $uploadButton = $j.button( { icon_id: 'disk', text: gM( 'mwe-upload_tab' ) }) |
| 1031 | + $uploadButton = $j.button( { icon: 'disk', text: gM( 'mwe-upload_tab' ) }) |
1032 | 1032 | .addClass("rsd_upload_button") |
1033 | 1033 | .click(function() { |
1034 | 1034 | // Update the previus_provider to swap back |
— | — | @@ -1041,7 +1041,7 @@ |
1042 | 1042 | $searchForm.append( $uploadButton ); |
1043 | 1043 | /* |
1044 | 1044 | // Import functionality not yet supported |
1045 | | - $importButton = $j.button({icon_id: 'import', text: 'import'}) |
| 1045 | + $importButton = $j.button({icon: 'import', text: 'import'}) |
1046 | 1046 | .addClass("rsd_import_button"); |
1047 | 1047 | .append( $importButton ); |
1048 | 1048 | */ |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.FirefoggRender.js |
— | — | @@ -46,6 +46,9 @@ |
47 | 47 | // Bollean attribute if we should save to local file |
48 | 48 | saveToLocalFile : true, |
49 | 49 | |
| 50 | + // Callback function for render progress |
| 51 | + onProgress: null, |
| 52 | + |
50 | 53 | // Constructor |
51 | 54 | init:function( options ) { |
52 | 55 | var _this = this; |
— | — | @@ -72,7 +75,7 @@ |
73 | 76 | |
74 | 77 | // Extend the render options with any provided details |
75 | 78 | if( options['renderOptions'] ){ |
76 | | - this.renderOptions = $j.extend( this.renderOptions, options['renderOptions'] ); |
| 79 | + this.renderOptions = $j.extend( {}, this.renderOptions, options['renderOptions'] ); |
77 | 80 | } |
78 | 81 | |
79 | 82 | if( options ['statusTarget']){ |
— | — | @@ -82,8 +85,13 @@ |
83 | 86 | if( options [ 'doneRenderCallback' ] ){ |
84 | 87 | this.doneRenderCallback = options [ 'doneRenderCallback' ]; |
85 | 88 | } |
| 89 | + // xxx should probably be a normal event binding .. oh well |
| 90 | + if( options['onProgress'] ){ |
| 91 | + this.onProgress = options['onProgress']; |
| 92 | + } |
86 | 93 | |
87 | | - if( options['saveToLocalFile'] ){ |
| 94 | + |
| 95 | + if( typeof options['saveToLocalFile'] != 'undefiend' ){ |
88 | 96 | this.saveToLocalFile = options['saveToLocalFile'] ; |
89 | 97 | } |
90 | 98 | // If no height width provided use target DOM width/height |
— | — | @@ -136,6 +144,8 @@ |
137 | 145 | // Update previusAudioTime |
138 | 146 | previusAudioTime = currentAudio.startTime + currentAudio.duration; |
139 | 147 | } |
| 148 | + // xxx localize status? |
| 149 | + $j( _this.statusTarget ).text( 'rendering' ); |
140 | 150 | // Now issue the save video as call |
141 | 151 | _this.doNextFrame(); |
142 | 152 | return true; |
— | — | @@ -150,18 +160,21 @@ |
151 | 161 | /*mw.log( "FirefoggRender::doNextFrame: on " + ( Math.round( _this.renderTime * 10 ) / 10 ) + " of " + |
152 | 162 | ( Math.round( _this.player.getDuration() * 10 ) / 10 ) ); |
153 | 163 | */ |
| 164 | + if( this.onProgress ){ |
| 165 | + this.onProgress( |
| 166 | + _this.renderTime / _this.getPlayer().getDuration() |
| 167 | + ) |
| 168 | + } |
154 | 169 | |
155 | | - _this.getPlayer().setCurrentTime( _this.renderTime, function() { |
156 | | - |
| 170 | + _this.getPlayer().setCurrentTime( _this.renderTime, function() { |
157 | 171 | _this.fogg.addFrame( $j( _this.playerTarget ).attr( 'id' ) ); |
158 | | - $j( _this.statusTarget ).text( "AddFrame::" + ( Math.round( _this.renderTime * 1000 ) / 1000 ) ); |
159 | | - |
| 172 | + // $j( _this.statusTarget ).text( "AddFrame::" + ( Math.round( _this.renderTime * 1000 ) / 1000 ) ); |
160 | 173 | _this.renderTime += _this.interval; |
161 | 174 | |
162 | 175 | if ( _this.renderTime >= _this.getPlayer().getDuration() || ! _this.continueRendering ) { |
163 | 176 | _this.doFinalRender(); |
164 | 177 | } else { |
165 | | - // Don't block on render requests |
| 178 | + // Don't block on render |
166 | 179 | setTimeout( function(){ |
167 | 180 | _this.doNextFrame(); |
168 | 181 | },1 ) |
— | — | @@ -204,7 +217,9 @@ |
205 | 218 | return ; |
206 | 219 | } |
207 | 220 | if( this.doneRenderCallback ){ |
208 | | - this.doneRenderCallback( this ) |
| 221 | + // Pass the firefogg object to the render done callback for other operations |
| 222 | + // ( such as uploading the asset ) |
| 223 | + this.doneRenderCallback( this.fogg ) |
209 | 224 | } |
210 | 225 | } |
211 | 226 | } |
\ No newline at end of file |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.Sequencer.js |
— | — | @@ -8,13 +8,21 @@ |
9 | 9 | * Setup the sequencer jQuery binding: |
10 | 10 | */ |
11 | 11 | ( function( $ ) { |
12 | | - $.fn.sequencer = function( options ) { |
| 12 | + $.fn.sequencer = function( options ) { |
13 | 13 | // Debugger |
14 | 14 | if( $j( this.selector ).length == 0 ){ |
15 | 15 | mw.log("Sequencer::Error missing target container"); |
16 | 16 | return; |
| 17 | + } |
| 18 | + var seqContainer = $j( this.selector ).get(0); |
| 19 | + // support jquery ui style 'destroy' call |
| 20 | + if( options == 'destroy' ){ |
| 21 | + if( seqContainer['sequencer'] ) |
| 22 | + delete seqContainer['sequencer']; |
| 23 | + return this; |
17 | 24 | } |
18 | | - var seqContainer = $j( this.selector ).get(0); |
| 25 | + |
| 26 | + |
19 | 27 | // Check if we already have a sequencer associated with this target |
20 | 28 | if( seqContainer['sequencer'] ){ |
21 | 29 | // xxx todo: pass on the options / action |
— | — | @@ -50,6 +58,7 @@ |
51 | 59 | 'newSequence' : null, |
52 | 60 | 'server' : null, |
53 | 61 | 'addMedia': null, |
| 62 | + 'onExitCallback' : null, |
54 | 63 | 'videoAspect' : '4:3' |
55 | 64 | } |
56 | 65 | mw.Sequencer = function( options ) { |
— | — | @@ -155,7 +164,6 @@ |
156 | 165 | }); |
157 | 166 | |
158 | 167 | }, |
159 | | - |
160 | 168 | /** |
161 | 169 | * Load a smil source if newSequence flag is set create new sequence source |
162 | 170 | * @param {function} callback Function called with smilSource |
— | — | @@ -166,9 +174,9 @@ |
167 | 175 | if( _this.getOption( 'newSequence' ) ){ |
168 | 176 | _this.smilSource = _this.getDataUrl( _this.getNewSmilXML() ); |
169 | 177 | } else { |
170 | | - mw.log("Load smil source from server") |
171 | | - // Try to load from the server |
172 | | - _this.getServer().getSmilXml(function( smilXml ){ |
| 178 | + mw.log( "Load smil source from server" ) |
| 179 | + // Load from the server |
| 180 | + _this.getServer().getSmilXml( function( smilXml ){ |
173 | 181 | _this.smilSource = _this.getDataUrl( smilXml ); |
174 | 182 | callback( _this.smilSource ) |
175 | 183 | }) |
Index: branches/MwEmbedStandAlone/modules/Sequencer/Sequencer.i18n.php |
— | — | @@ -8,6 +8,7 @@ |
9 | 9 | |
10 | 10 | $messages = array(); |
11 | 11 | $messages['en'] = array( |
| 12 | + 'mwe-sequencer-loading-sequencer' => 'Loading sequencer ...', |
12 | 13 | 'mwe-sequencer-loading-timeline' => 'Loading timeline ...', |
13 | 14 | 'mwe-sequencer-loading-player' => 'Loading player ...', |
14 | 15 | 'mwe-sequencer-loading-menu' => 'Loading menu ...', |
— | — | @@ -45,9 +46,14 @@ |
46 | 47 | 'mwe-sequencer-menu-sequence-open-desc' => 'Open a sequence for editing', |
47 | 48 | |
48 | 49 | 'mwe-sequencer-menu-sequence-save' => 'Save', |
| 50 | + 'mwe-sequencer-menu-sequence-save-desc' => 'Save sequence changes', |
49 | 51 | 'mwe-sequencer-menu-sequence-publish' => 'Publish', |
| 52 | + 'mwe-sequencer-menu-sequence-publish-desc' => 'Publish sequence', |
50 | 53 | 'mwe-sequencer-menu-sequence-renderdisk' => 'Render to disk', |
51 | 54 | 'mwe-sequencer-menu-sequence-exit' => 'Exit', |
| 55 | + 'mwe-sequencer-menu-sequence-exit-desc' => 'Exit the sequencer', |
| 56 | + 'mwe-sequencer-confirm-exit' => 'Are you sure you want to exit?', |
| 57 | + 'mwe-sequencer-confirm-exit-desc' => 'Are you sure you want to exit the sequencer? Unsaved edits will be lost.', |
52 | 58 | |
53 | 59 | 'mwe-sequencer-menu-edit' => 'Edit', |
54 | 60 | 'mwe-sequencer-menu-edit-undo' => 'Undo', |
— | — | @@ -64,11 +70,14 @@ |
65 | 71 | 'mwe-sequencer-insert'=> 'Insert into sequence', |
66 | 72 | |
67 | 73 | 'mwe-sequencer-no-server-defined' => 'No server has been defined for this sequence session. You will not be able to save sequences', |
68 | | - 'mwe-sequencer-no_edit_permissions' => 'You do not have permissions to save changes to this sequence', |
| 74 | + |
| 75 | + 'mwe-sequencer-no_edit_permissions' => 'Can not save sequence', |
| 76 | + 'mwe-sequencer-no_edit_permissions-desc' => 'You do not have permissions to save changes to this sequence', |
| 77 | + |
| 78 | + 'mwe-sequencer-save-no-changes' => 'There are no new edits to save', |
69 | 79 | 'mwe-sequencer-save-summary' => 'Please enter a short summary of changes:', |
70 | 80 | 'mwe-sequencer-edit_cancel' => 'Cancel sequence edit', |
71 | | - |
72 | | - 'mwe-sequencer-edit_save' => 'Save sequence changes', |
| 81 | + |
73 | 82 | 'mwe-sequencer-saving_wait' => 'Save in progress (please wait)', |
74 | 83 | 'mwe-sequencer-save_done' => 'Save complete', |
75 | 84 | |
— | — | @@ -76,10 +85,18 @@ |
77 | 86 | 'mwe-sequencer-not-published' => 'This sequence has not yet been published. <i>Browser preview is shown</i>. <b>[$1 Review and publish this sequence]</b>.', |
78 | 87 | 'mwe-sequencer-published-out-of-date' =>'This published sequence is not the most recent version. You can <b>[$1 review and publish]</b> the most recent version.', |
79 | 88 | |
| 89 | + 'mwe-sequencer-already-published' => "The most recent version of this sequence is already published", |
| 90 | + |
80 | 91 | 'mwe-sequencer-loading-publish-render' => 'Loading publisher...', |
| 92 | + 'mwe-sequencer-please-save-publish' => 'Please save changes before you publish', |
81 | 93 | 'mwe-sequencer-running-publish' => 'Publishing sequence, please wait...', |
82 | 94 | 'mwe-sequencer-publishing-status' => 'Publish status:', |
| 95 | + 'mwe-sequencer-publishing-error' => 'Publish error', |
| 96 | + 'mwe-sequencer-publishing-error-upload-desc' => 'There was an error in uploading the published file', |
83 | 97 | |
| 98 | + 'mwe-sequencer-publishing-success' => 'Publish success', |
| 99 | + 'mwe-sequencer-publishing-success-desc' => 'Sequence has successfully been published. [$1 Published file]', |
| 100 | + |
84 | 101 | 'mwe-sequencer-transition_in' => 'Transition in', |
85 | 102 | 'mwe-sequencer-transition_out' => 'Transition out', |
86 | 103 | 'mwe-sequencer-effects' => 'Effects stack', |
— | — | @@ -95,7 +112,6 @@ |
96 | 113 | |
97 | 114 | |
98 | 115 | |
99 | | - |
100 | 116 | 'mwe-sequencer-edit_cancel_confirm' => 'Are you sure you want to cancel your edit? Changes will be lost.', |
101 | 117 | 'mwe-sequencer-zoom_in' => 'Zoom in', |
102 | 118 | 'mwe-sequencer-zoom_out' => 'Zoom out', |
Index: branches/MwEmbedStandAlone/modules/Sequencer/actions/mw.SequencerActionsSequence.js |
— | — | @@ -16,23 +16,35 @@ |
17 | 17 | this.sequencer = sequencer; |
18 | 18 | }, |
19 | 19 | save: function(){ |
20 | | - var _this = this; |
21 | | - // Check if we have an api provider defined |
22 | | - if( ! this.sequencer.getServer().exists() ){ |
23 | | - mw.addDialog( gM('mwe-sequencer-no-server-defined') ) |
24 | | - return ; |
25 | | - } |
| 20 | + var _this = this; |
26 | 21 | var $dialog = mw.addDialog({ |
27 | 22 | 'resizable':'true', |
28 | | - 'title' : gM('mwe-sequencer-loading_user_rights'), |
| 23 | + 'title' : gM('mwe-sequencer-menu-sequence-save-desc'), |
29 | 24 | 'content' : gM('mwe-sequencer-loading_user_rights'), |
30 | 25 | 'width' : 450 |
31 | 26 | }); |
32 | 27 | |
| 28 | + // Check if we have an api provider defined |
| 29 | + if( ! this.sequencer.getServer().isConfigured() ){ |
| 30 | + $dialog.html( gM('mwe-sequencer-no-server-defined') ); |
| 31 | + return ; |
| 32 | + } |
| 33 | + |
| 34 | + // Check if we have unsaved changes ( don't save for no reason ) |
| 35 | + if( !_this.sequencer.getServer().hasLocalChanges() ){ |
| 36 | + $dialog.html( gM('mwe-sequencer-save-no-changes') ); |
| 37 | + var closeButton = {}; |
| 38 | + closeButton[gM('mwe-ok')]= function(){ $j(this).dialog('close') }; |
| 39 | + $dialog.dialog( "option", "buttons", closeButton); |
| 40 | + return ; |
| 41 | + } |
| 42 | + |
| 43 | + |
33 | 44 | // Check if we can save |
34 | 45 | this.sequencer.getServer().userCanSave( function( canSave ){ |
35 | 46 | if( canSave === false ){ |
36 | | - $dialog.html( gM( 'mwe-sequencer-no_edit_permissions') ); |
| 47 | + $dialog.dialog( "option", "title", gM('mwe-sequencer-no_edit_permissions') ); |
| 48 | + $dialog.html( gM( 'mwe-sequencer-no_edit_permissions-desc') ); |
37 | 49 | // Add close text |
38 | 50 | $dialog.dialog( "option", "closeText", gM('mwe-ok') ); |
39 | 51 | return ; |
— | — | @@ -43,133 +55,295 @@ |
44 | 56 | showSaveDialog: function( $dialog ){ |
45 | 57 | var _this = this; |
46 | 58 | // Else user 'can save' present a summary text box |
47 | | - var saveDialogButtons = { }; |
48 | | - saveDialogButtons[ gM('mwe-sequencer-edit_save') ] = function(){ |
| 59 | + var saveDialogButtons = {}; |
| 60 | + saveDialogButtons[ gM('mwe-sequencer-menu-sequence-save-desc') ] = function(){ |
| 61 | + // grab the save summary before setting dialog to loading: |
| 62 | + var saveSummary = $j('#sequenceSaveSummary').val(); |
| 63 | + // set dialog to loading |
49 | 64 | $dialog.empty().append( |
50 | 65 | gM('mwe-sequencer-saving_wait' ), |
51 | | - $j('<div />').loadingSpinner() |
| 66 | + $j('<div />').loadingSpinner() |
52 | 67 | ); |
53 | | - _this.doSaveWithSummary( $dialog.find('.saveSummary').val(), function( status, errorMsg ){ |
54 | | - if( status === false ){ |
55 | | - $dialog.text( errorMsg ) |
56 | | - } else { |
57 | | - // save success |
58 | | - $dialog.text( gM( 'mwe-sequencer-save_done' ) ) |
| 68 | + // Remove buttons while loading |
| 69 | + $dialog.dialog( "option", "buttons", {} ); |
| 70 | + $dialog.dialog( "option", "title", gM('mwe-sequencer-saving_wait' ) ); |
| 71 | + |
| 72 | + _this.sequencer.getServer().save( |
| 73 | + /* Save summary */ |
| 74 | + saveSummary, |
| 75 | + /* Save xml */ |
| 76 | + _this.sequencer.getSmil().getXMLString(), |
| 77 | + /* Save callback */ |
| 78 | + function( status, errorMsg ){ |
| 79 | + if( status === false ){ |
| 80 | + $dialog.text( errorMsg ) |
| 81 | + } else { |
| 82 | + // save success |
| 83 | + $dialog.text( gM( 'mwe-sequencer-save_done' ) ) |
| 84 | + } |
| 85 | + // Only let the user hit 'ok' |
| 86 | + var closeButton = {}; |
| 87 | + closeButton[gM('mwe-ok')]= function(){ $j(this).dialog('close') }; |
| 88 | + $dialog.dialog( "option", "buttons", closeButton); |
59 | 89 | } |
60 | | - // Only let the user hit 'ok' |
61 | | - var closeButton = {}; |
62 | | - closeButton[gM('mwe-ok')]= function(){ $j(this).dialog('close') }; |
63 | | - $dialog.dialog( "option", "buttons", closeButton); |
64 | | - }); |
| 90 | + ); |
65 | 91 | }; |
66 | 92 | saveDialogButtons[ gM('mwe-sequencer-edit_cancel') ] = function(){ |
67 | 93 | $dialog.dialog('close'); |
68 | 94 | }; |
| 95 | + |
69 | 96 | $dialog.empty().append( |
70 | 97 | gM('mwe-sequencer-save-summary' ), |
71 | 98 | $j('<input />') |
72 | | - .css({'width': 400 }) |
73 | | - .addClass( 'saveSummary' ) |
74 | | - .attr({ |
75 | | - 'maxlength': 255 |
76 | | - }) |
| 99 | + .css({ 'width': 400 }) |
| 100 | + .attr({ |
| 101 | + 'id' : 'sequenceSaveSummary', |
| 102 | + 'maxlength': 255 |
| 103 | + }) |
| 104 | + // Make sure keys press does not affect the sequencer interface |
| 105 | + .sequencerInput( _this.sequencer ) |
77 | 106 | ) |
78 | | - .dialog( "option", "buttons", saveDialogButtons ); |
79 | | - }, |
80 | | - doSaveWithSummary : function( summary, callback ){ |
81 | | - this.sequencer.getServer().save( |
82 | | - summary, |
83 | | - this.sequencer.getSmil().getXMLString(), |
84 | | - callback |
85 | | - ); |
86 | | - }, |
| 107 | + .dialog( "option", "buttons", saveDialogButtons ) |
| 108 | + .dialog( "option", "title", gM('mwe-sequencer-menu-sequence-save-desc') ) |
| 109 | + }, |
87 | 110 | /** |
88 | 111 | * Display the publish dialog |
89 | 112 | * ( confirm the user has firefogg and rights to save a new version of the file ) |
90 | 113 | */ |
91 | 114 | publish: function(){ |
92 | | - var _this = this; |
| 115 | + var _this = this; |
93 | 116 | // add a loading dialog |
94 | 117 | var $dialog = mw.addDialog({ |
95 | 118 | 'resizable':'true', |
96 | | - 'title' : gM('mwe-sequencer-loading-publish-render'), |
| 119 | + 'title' : gM('mwe-sequencer-menu-sequence-publish-desc'), |
97 | 120 | 'content' : gM('mwe-sequencer-loading-publish-render'), |
98 | 121 | 'width' : 450, |
99 | 122 | 'height' : 400 |
100 | 123 | }); |
| 124 | + |
| 125 | + // Check if we have unsaved changes ( don't publish unsaved changes ) |
| 126 | + if( _this.sequencer.getServer().hasLocalChanges() ){ |
| 127 | + $dialog.empty().html( gM('mwe-sequencer-please-save-publish')) |
| 128 | + var buttons = {}; |
| 129 | + buttons[ gM( 'mwe-sequencer-menu-sequence-save-desc') ] = function(){ |
| 130 | + _this.save(); |
| 131 | + }; |
| 132 | + buttons[ gM('mwe-cancel') ] = function(){ |
| 133 | + $j( this ).dialog( 'close' ); |
| 134 | + } |
| 135 | + $dialog.dialog( 'option', 'buttons', buttons); |
| 136 | + return; |
| 137 | + } |
| 138 | + |
101 | 139 | $dialog.append( $j('<div />').loadingSpinner() ); |
102 | | - // Grab the firefogg render |
103 | | - mw.load( ['AddMedia.firefogg','FirefoggRender'], function(){ |
104 | | - // Get a Firefogg object to check if firefogg is installed |
105 | | - var myFogg = new mw.Firefogg( { |
106 | | - 'only_fogg':true |
107 | | - }); |
108 | | - if ( !myFogg.getFirefogg() ) { |
109 | | - $dialog.empty().append( |
110 | | - $j('<div />').attr('id', 'show_install_firefogg') |
111 | | - ); |
112 | | - myFogg.showInstallFirefog( '#show_install_firefogg' ); |
113 | | - return ; |
114 | | - } |
115 | | - |
116 | | - // Build a data-url of the current sequence: |
117 | | - $dialog.dialog( "option", "title", gM('mwe-sequencer-running-publish') ); |
118 | | - |
119 | | - $dialog.empty().append( |
120 | | - $j( '<video />' ) |
121 | | - .attr({ |
122 | | - 'id': 'publishVideoTarget', |
123 | | - 'src' : _this.sequencer.getDataUrl(), |
124 | | - 'type' : 'application/smil' |
125 | | - }) |
126 | | - .css({ |
127 | | - 'width' : '400px', |
128 | | - 'height' : '300px' |
129 | | - }) |
130 | | - , |
131 | | - $j('<div />' ) |
132 | | - .css( 'clear', 'both' ), |
133 | | - $j('<span />' ).text( gM( 'mwe-sequencer-publishing-status') ), |
134 | | - $j('<span />' ).attr( 'id', 'firefoggStatusTarget' ), |
135 | | - $j('<span />') |
136 | | - .css('float', 'right') |
137 | | - .text("%") |
138 | | - ); |
139 | | - |
140 | | - // Embed the player and continue application flow |
141 | | - $j('#publishVideoTarget').embedPlayer({ |
142 | | - 'controls' : false |
143 | | - }, function(){ |
144 | | - // this should be depreciated ( hidden interface bug in mwEmbed ) |
145 | | - $j('#publishVideoTarget').parent().show(); |
146 | | - // Start up the render |
147 | | - var foggRender = $j('#publishVideoTarget').firefoggRender({ |
148 | | - 'statusTarget' : '#firefoggStatusTarget', |
149 | | - 'saveToLocalFile' : false, |
150 | | - 'doneRenderCallback': function( fogg ){ |
151 | | - _this.uploadRenderedVideo( $dialog, fogg ); |
152 | | - } |
| 140 | + |
| 141 | + // Check if the published version is already the latest |
| 142 | + _this.sequencer.getServer().isPublished( function( isPublished ){ |
| 143 | + if( !isPublished ){ |
| 144 | + mw.load( ['AddMedia.firefogg','FirefoggRender'], function(){ |
| 145 | + _this.doPublish( $dialog ) |
153 | 146 | }); |
| 147 | + } else { |
| 148 | + $dialog.empty().text( gM('mwe-sequencer-already-published') ) |
154 | 149 | var buttons = {}; |
155 | | - buttons[ gM('mwe-cancel') ] = function(){ |
156 | | - foggRender.stopRender(); |
| 150 | + buttons[ gm('mwe-ok') ] = function(){ |
157 | 151 | $j( this ).dialog( 'close' ); |
158 | 152 | } |
159 | | - // Add cancel button |
160 | | - $dialog.dialog( "option", "buttons", buttons ); |
161 | | - foggRender.doRender(); |
162 | | - }); |
| 153 | + $dialog.dialog( 'option', 'buttons', buttons); |
| 154 | + } |
163 | 155 | }); |
| 156 | + |
| 157 | + |
| 158 | + |
164 | 159 | }, |
| 160 | + doPublish: function( $dialog ){ |
| 161 | + var _this = this; |
| 162 | + // Get a Firefogg object to check if firefogg is installed |
| 163 | + var myFogg = new mw.Firefogg( { |
| 164 | + 'only_fogg':true |
| 165 | + }); |
| 166 | + if ( !myFogg.getFirefogg() ) { |
| 167 | + $dialog.empty().append( |
| 168 | + $j('<div />').attr('id', 'show_install_firefogg') |
| 169 | + ); |
| 170 | + myFogg.showInstallFirefog( '#show_install_firefogg' ); |
| 171 | + return ; |
| 172 | + } |
| 173 | + |
| 174 | + // Build a data-url of the current sequence: |
| 175 | + $dialog.dialog( "option", "title", gM('mwe-sequencer-running-publish') ); |
| 176 | + |
| 177 | + $dialog.empty().append( |
| 178 | + $j( '<video />' ) |
| 179 | + .attr({ |
| 180 | + 'id': 'publishVideoTarget', |
| 181 | + 'src' : _this.sequencer.getDataUrl(), |
| 182 | + 'type' : 'application/smil' |
| 183 | + }) |
| 184 | + .css({ |
| 185 | + 'width' : '400px', |
| 186 | + 'height' : '300px' |
| 187 | + }) |
| 188 | + , |
| 189 | + $j('<div />' ) |
| 190 | + .css( 'clear', 'both' ), |
| 191 | + $j('<span />' ).text( gM( 'mwe-sequencer-publishing-status') ), |
| 192 | + $j('<span />' ).attr( 'id', 'firefoggStatusTarget' ), |
| 193 | + $j('<span />').attr('id', 'firefoggPercentDone') |
| 194 | + .css('float', 'right') |
| 195 | + .text("%"), |
| 196 | + $j('<div />').attr( 'id', 'firefoggProgressbar') |
| 197 | + |
| 198 | + ); |
| 199 | + $j('<div />').attr( 'id', 'firefoggProgressbar') |
| 200 | + // Embed the player and continue application flow |
| 201 | + $j('#publishVideoTarget').embedPlayer({ |
| 202 | + 'controls' : false |
| 203 | + }, function(){ |
| 204 | + // this should be depreciated ( hidden interface bug in mwEmbed ) |
| 205 | + $j('#publishVideoTarget').parent().show(); |
| 206 | + // Start up the render |
| 207 | + var foggRender = $j('#publishVideoTarget').firefoggRender({ |
| 208 | + 'statusTarget' : '#firefoggStatusTarget', |
| 209 | + 'saveToLocalFile' : false, |
| 210 | + 'onProgress' : function( progress ){ |
| 211 | + var progressPrecent = ( Math.round( progress * 10000 ) / 100 ); |
| 212 | + $j('#firefoggPercentDone').text( |
| 213 | + progressPrecent + |
| 214 | + '%' |
| 215 | + ) |
| 216 | + $j("#firefoggProgressbar").progressbar( |
| 217 | + "option", "value", Math.round( progress * 100 ) |
| 218 | + ); |
| 219 | + }, |
| 220 | + 'doneRenderCallback': function( fogg ){ |
| 221 | + _this.uploadRenderedVideo( $dialog, fogg ); |
| 222 | + } |
| 223 | + }); |
| 224 | + var buttons = {}; |
| 225 | + buttons[ gM('mwe-cancel') ] = function(){ |
| 226 | + foggRender.stopRender(); |
| 227 | + $j( this ).dialog( 'close' ); |
| 228 | + } |
| 229 | + // Add cancel button |
| 230 | + $dialog.dialog( "option", "buttons", buttons ); |
| 231 | + foggRender.doRender(); |
| 232 | + }); |
| 233 | + }, |
165 | 234 | // Upload the video from a supplied fogg target |
166 | | - // note xx this might be better handlded in a firefogg library |
| 235 | + // note xx this might be better in the firefogg library since it has firefogg specific calls |
167 | 236 | // @param {jQuery Object } $dialog |
168 | 237 | // @param {firefogg Object} |
169 | 238 | uploadRenderedVideo: function( $dialog, fogg ){ |
170 | | - var uploadStatus = function(){ |
| 239 | + var _this = this; |
| 240 | + $j( '#firefoggStatusTarget' ).text( 'uploading' ); |
| 241 | + var updateUploadStatus = function(){ |
| 242 | + if( fogg.status() == 'uploading' ){ |
| 243 | + $j('#firefoggPercentDone').text( |
| 244 | + ( Math.round( fogg.progress() * 10000 ) / 100 ) + |
| 245 | + '%' |
| 246 | + ) |
| 247 | + setTimeout(updateUploadStatus, 1000); |
| 248 | + return ; |
| 249 | + } |
| 250 | + // Parts of this code are replicated in firefogg upload handler |
| 251 | + // xxx should refactor so they share a common handler |
| 252 | + if( fogg.status() == 'upload done' ){ |
| 253 | + var response_text = fogg.responseText; |
| 254 | + if ( !response_text ) { |
| 255 | + try { |
| 256 | + var pstatus = JSON.parse( fogg.uploadstatus() ); |
| 257 | + response_text = pstatus["responseText"]; |
| 258 | + } catch( e ) { |
| 259 | + mw.log( "Error: could not parse firefogg responseText: " + e ); |
| 260 | + } |
| 261 | + } |
| 262 | + try { |
| 263 | + var apiResult = JSON.parse( response_text ); |
| 264 | + } catch( e ) { |
| 265 | + mw.log( "Error: could not parse response_text::" + response_text + |
| 266 | + ' ...for now try with eval...' ); |
| 267 | + try { |
| 268 | + var apiResult = eval( response_text ); |
| 269 | + } catch( e ) { |
| 270 | + var apiResult = null; |
| 271 | + } |
| 272 | + } |
| 273 | + _this.uploadDone( $dialog, apiResult ); |
| 274 | + return ; |
| 275 | + } |
171 | 276 | } |
172 | | - this.sequencer.getServer().getVideoUploadSettings( function( url, request ){ |
173 | | - fogg.post( url, 'file', request ) |
| 277 | + this.sequencer.getServer().getUploadRequestConfig( function( url, request ){ |
| 278 | + fogg.post( url, 'file', JSON.stringify( request ) ); |
| 279 | + updateUploadStatus(); |
174 | 280 | }) |
175 | | - } |
| 281 | + }, |
| 282 | + uploadDone: function($dialog, apiResult){ |
| 283 | + var _this = this; |
| 284 | + // Check the api response |
| 285 | + if ( apiResult.error || ( apiResult.upload && |
| 286 | + ( apiResult.upload.result == "Failure" || apiResult.upload.error ) ) ) { |
| 287 | + |
| 288 | + $dialog.dialog( 'option', 'title', gM('mwe-sequencer-publishing-error' ) ); |
| 289 | + // xxx improve error handling |
| 290 | + $dialog.empty().text( gM( 'mwe-sequencer-publishing-error-upload-desc' ) ) |
| 291 | + return ; |
| 292 | + } |
| 293 | + |
| 294 | + // Success link to the sequence page / ok closes dialog |
| 295 | + $dialog.dialog( 'option', 'title', gM('mwe-sequencer-publishing-success' ) ); |
| 296 | + $dialog.empty().html( gM('mwe-sequencer-publishing-success-desc', |
| 297 | + $j('<a />') |
| 298 | + .attr({ |
| 299 | + 'target': '_new', |
| 300 | + 'href': wgArticlePath.replace( '$1', 'File:' +_this.sequencer.getServer().getVideoFileName() ) |
| 301 | + }) |
| 302 | + ) ); |
| 303 | + // Update the buttons |
| 304 | + var buttons = {}; |
| 305 | + buttons[ gM('mwe-ok') ] = function(){ |
| 306 | + $j( this ).dialog('close'); |
| 307 | + }; |
| 308 | + $dialog.dialog( 'option', 'buttons', buttons); |
| 309 | + }, |
| 310 | + /** |
| 311 | + * exit the sequencer. |
| 312 | + * confirm we want to 'lose' changes (if not let the user save changes) |
| 313 | + */ |
| 314 | + exit: function(){ |
| 315 | + var _this = this; |
| 316 | + if( _this.sequencer.getServer().hasLocalChanges() ){ |
| 317 | + var buttons = {}; |
| 318 | + buttons[ gM( 'mwe-sequencer-menu-sequence-save-desc') ] = function(){ |
| 319 | + _this.save(); |
| 320 | + }; |
| 321 | + buttons[ gM('mwe-sequencer-menu-sequence-exit-desc') ] = function(){ |
| 322 | + _this.closeSequencer(); |
| 323 | + } |
| 324 | + // Confirm the user wants to exit |
| 325 | + mw.addDialog( { |
| 326 | + 'title': gM('mwe-sequencer-confirm-exit'), |
| 327 | + 'content' : gM('mwe-sequencer-confirm-exit-desc'), |
| 328 | + 'buttons' : buttons, |
| 329 | + 'width' : '400px' |
| 330 | + }) |
| 331 | + } else { |
| 332 | + _this.closeSequencer(); |
| 333 | + } |
| 334 | + }, |
| 335 | + closeSequencer: function(){ |
| 336 | + var _this = this; |
| 337 | + this.sequencer.getContainer().fadeOut( |
| 338 | + function(){ |
| 339 | + // Check if there is an on exit callback |
| 340 | + if( _this.sequencer.getOption('onExitCallback') ){ |
| 341 | + // Send a flag of weather the sequence 'changed' or not |
| 342 | + _this.sequencer.getOption('onExitCallback')( |
| 343 | + _this.sequencer.getServer().hasSequenceBeenSaved() |
| 344 | + ); |
| 345 | + } |
| 346 | + $j( this ).remove(); |
| 347 | + } |
| 348 | + ); |
| 349 | + } |
176 | 350 | } |
\ No newline at end of file |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerAddMedia.js |
— | — | @@ -58,7 +58,7 @@ |
59 | 59 | // The text of the button link |
60 | 60 | 'text' : gM('mwe-sequencer-get-media'), |
61 | 61 | // The icon id that precedes the button link: |
62 | | - 'icon_id' : 'plus' |
| 62 | + 'icon' : 'plus' |
63 | 63 | }) |
64 | 64 | .click(function(){ |
65 | 65 | // only do the search if the user has given the search input focus |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerPlayer.js |
— | — | @@ -40,7 +40,7 @@ |
41 | 41 | |
42 | 42 | // Draw the player ( keep the playhead for now ) |
43 | 43 | // xxx we will eventually replace the playhead with sequence |
44 | | - // based playhead interface for doing easy trims. |
| 44 | + // based playhead interface for doing easy trims |
45 | 45 | $j( '#' + _this.getSmilPlayerId() ).embedPlayer({ |
46 | 46 | 'overlayControls' : false |
47 | 47 | }, function(){ |
— | — | @@ -99,12 +99,14 @@ |
100 | 100 | } |
101 | 101 | return size; |
102 | 102 | }, |
| 103 | + |
103 | 104 | /** |
104 | | - * get the embedplayer object instance |
| 105 | + * Get the embedplayer object instance |
105 | 106 | */ |
106 | 107 | getEmbedPlayer: function(){ |
107 | 108 | return $j( '#' + this.getSmilPlayerId() ).get(0); |
108 | 109 | }, |
| 110 | + |
109 | 111 | /** |
110 | 112 | * Get a player id |
111 | 113 | */ |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTools.js |
— | — | @@ -304,7 +304,7 @@ |
305 | 305 | var _this = this; |
306 | 306 | var editAction = this.editActions[ editActionId ]; |
307 | 307 | $actionButton = $j.button({ |
308 | | - icon_id: editAction.icon, |
| 308 | + icon: editAction.icon, |
309 | 309 | text: editAction.title |
310 | 310 | }) |
311 | 311 | .css({ |
— | — | @@ -333,7 +333,8 @@ |
334 | 334 | return $j( '<div />' ) |
335 | 335 | .css({ |
336 | 336 | 'float': 'left', |
337 | | - 'width': '150px', |
| 337 | + 'font-size': '12px', |
| 338 | + 'width': '160px', |
338 | 339 | 'border': 'solid thin #999', |
339 | 340 | 'background-color': '#EEE', |
340 | 341 | 'padding' : '2px', |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerMenu.js |
— | — | @@ -59,7 +59,7 @@ |
60 | 60 | 'icon' : 'power', |
61 | 61 | 'action' : function( _this ){ |
62 | 62 | mw.log( 'check for save') |
63 | | - _this.sequencer.getContainer().fadeOut().remove(); |
| 63 | + _this.sequencer.getActionsSequence().exit(); |
64 | 64 | } |
65 | 65 | } |
66 | 66 | }, |
— | — | @@ -152,7 +152,22 @@ |
153 | 153 | this.menuWidgets[widgetKey]( this ) |
154 | 154 | ); |
155 | 155 | } |
156 | | - |
| 156 | + // Append close button to the upper right |
| 157 | + $menuTarget.append( |
| 158 | + $j.button({ |
| 159 | + 'icon' : 'circle-close' |
| 160 | + }) |
| 161 | + .css({ |
| 162 | + 'float' : 'right', |
| 163 | + 'right':'-6px', |
| 164 | + 'top':'-9px' |
| 165 | + }) |
| 166 | + .attr('title', gM('mwe-sequencer-menu-sequence-exit-desc') ) |
| 167 | + .click( function(){ |
| 168 | + _this.sequencer.getActionsSequence().exit(); |
| 169 | + }) |
| 170 | + ); |
| 171 | + |
157 | 172 | // Check if we should include kaltura credits |
158 | 173 | if( mw.getConfig( 'Sequencer.KalturaAttribution' ) ){ |
159 | 174 | $menuTarget.append( |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerConfig.js |
— | — | @@ -23,10 +23,16 @@ |
24 | 24 | // Default image source width |
25 | 25 | "Sequencer.AddMediaImageWidth" : 640, |
26 | 26 | |
27 | | - // Default timeline "video / image" clip thumb size |
| 27 | + // Default timeline clip timeline track height |
28 | 28 | "Sequencer.TimelineTrackHeight" : 100, |
29 | 29 | |
30 | | - // Default timeline "audio / collapsed" size |
31 | | - "Sequencer.TimelineColapsedTrackSize" : 35 |
| 30 | + // Default timeline audio or collapsed timeline height |
| 31 | + "Sequencer.TimelineColapsedTrackSize" : 35, |
| 32 | + |
| 33 | + // Asset domain restriction array of domains or keyword 'none' |
| 34 | + // Before any asset is displayed its domain is checked against this array of wildcard domains |
| 35 | + // Additionally best effort is made to check any text/html asset references |
| 36 | + // for example [ '*.wikimedia.org', 'en.wikipeida.org'] |
| 37 | + "Sequencer.DomainRestriction" : 'none' |
32 | 38 | }) |
33 | 39 | |
\ No newline at end of file |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTimeline.js |
— | — | @@ -594,6 +594,8 @@ |
595 | 595 | .buttonHover() |
596 | 596 | .click( function(){ |
597 | 597 | _this.editClip( $timelineClip ) |
| 598 | + $timelineClip.addClass( 'selectedClip' ); |
| 599 | + return false; |
598 | 600 | }), |
599 | 601 | |
600 | 602 | // Remove clip button: |
— | — | @@ -648,7 +650,7 @@ |
649 | 651 | } |
650 | 652 | }); |
651 | 653 | |
652 | | - // Sometimes the load event does not fire force the fallback image after 5 seconds |
| 654 | + // Sometimes the load event does not fire. Force the fallback image after 5 seconds |
653 | 655 | setTimeout( function(){ |
654 | 656 | if( $thumbTarget.children().length == 0 ){ |
655 | 657 | $thumbTarget.html( img ); |
Index: branches/MwEmbedStandAlone/modules/Sequencer/remotes/mw.MediaWikiRemoteSequencer.js |
— | — | @@ -11,6 +11,7 @@ |
12 | 12 | "mwe-sequencer-edit-sequence", |
13 | 13 | "mwe-sequencer-embed-sequence", |
14 | 14 | "mwe-sequencer-embed-sequence-desc", |
| 15 | + "mwe-sequencer-loading-sequencer", |
15 | 16 | |
16 | 17 | "mwe-sequencer-not-published", |
17 | 18 | "mwe-sequencer-published-out-of-date" |
— | — | @@ -217,24 +218,42 @@ |
218 | 219 | 'left' : '5px', |
219 | 220 | 'right' : '5px', |
220 | 221 | 'background': '#FFF' |
221 | | - }) |
222 | | - .loadingSpinner() |
| 222 | + }) |
| 223 | + .append( |
| 224 | + $j('<div />').append( |
| 225 | + gM('mwe-sequencer-loading-sequencer'), |
| 226 | + $j('<span />').loadingSpinner() |
| 227 | + ) |
| 228 | + .css( {'width':'200px', 'margin':'auto'}) |
| 229 | + ) |
223 | 230 | ) |
224 | 231 | |
225 | | - mw.load( 'Sequencer', function(){ |
226 | | - $j('#edit_sequence_container').sequencer({ |
227 | | - 'title' : _this.getTitle(), |
228 | | - 'newSequence' : ( wgArticleId == 0 ), |
229 | | - 'server': { |
| 232 | + mw.load( 'Sequencer', function(){ |
| 233 | + // Send a jquery ui style destroy command |
| 234 | + $j('#edit_sequence_container').sequencer( 'destroy'); |
| 235 | + $j('#edit_sequence_container').sequencer({ |
| 236 | + // The title for this sequence: |
| 237 | + title : _this.getTitle(), |
| 238 | + // If the sequence is new |
| 239 | + newSequence : ( wgArticleId == 0 ), |
| 240 | + // Server config: |
| 241 | + server: { |
230 | 242 | 'type' : 'mediaWiki', |
231 | 243 | 'url' : _this.getApiUrl(), |
232 | 244 | 'titleKey' : wgPageName, |
233 | 245 | }, |
234 | 246 | // Set the add media wizard to only include commons: |
235 | | - 'addMedia' : { |
| 247 | + addMedia : { |
236 | 248 | 'enabled_providers':[ 'wiki_commons' ], |
237 | | - 'default_query' : _this.getTitle() |
238 | | - } |
| 249 | + 'default_query' : _this.getTitle() |
| 250 | + }, |
| 251 | + // Function called on sequence exit |
| 252 | + onExitCallback: function( sequenceHasChanged ){ |
| 253 | + if( sequenceHasChanged ){ |
| 254 | + window.location.reload(); |
| 255 | + } |
| 256 | + // else do nothing |
| 257 | + } |
239 | 258 | }); |
240 | 259 | }); |
241 | 260 | }, |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerServer.js |
— | — | @@ -17,14 +17,29 @@ |
18 | 18 | // lazy init save token for the server config |
19 | 19 | saveToken : null, |
20 | 20 | |
| 21 | + // Api type ( always mediaWiki for now) |
| 22 | + apiType: null, |
| 23 | + |
| 24 | + // Api url ( url to query for api updates ) |
| 25 | + apiUrl: null, |
| 26 | + |
| 27 | + // The sequence title key for api queries |
| 28 | + titleKey: null, |
| 29 | + |
| 30 | + // Stores the most recent version of the sequence xml from the server |
| 31 | + serverSmilXml: null, |
| 32 | + |
| 33 | + // Flags if the sequence was successfully saved in this session |
| 34 | + sequenceSaved: false, |
| 35 | + |
21 | 36 | /** |
22 | 37 | * init the sequencer |
23 | 38 | */ |
24 | 39 | init: function( sequencer ){ |
25 | 40 | this.sequencer = sequencer; |
26 | 41 | // Set local config from sequencer options |
27 | | - var serverConfig = this.sequencer.getOption( 'server' ); |
28 | | - |
| 42 | + var serverConfig = this.sequencer.getOption( 'server' ); |
| 43 | + |
29 | 44 | // NOTE this should trigger an apiHandler once we have more than one api backend |
30 | 45 | if( serverConfig ){ |
31 | 46 | if( serverConfig.type ) |
— | — | @@ -33,19 +48,24 @@ |
34 | 49 | this.apiUrl = serverConfig.url; |
35 | 50 | if( serverConfig.titleKey ) |
36 | 51 | this.titleKey = serverConfig.titleKey; |
37 | | - } else { |
38 | | - mw.log("Error: Sequencer server needs a serverConfig to be initialized") |
39 | 52 | } |
| 53 | + if( this.isConfigured() ){ |
| 54 | + mw.log("Error: Sequencer server needs a full serverConfig to be initialized") |
| 55 | + return false; |
| 56 | + } |
40 | 57 | }, |
41 | 58 | |
| 59 | + |
42 | 60 | // Check if the server exists / is configured |
43 | | - exists: function( ){ |
44 | | - if( ! this.apiUrl || ! this.titleKey ){ |
| 61 | + isConfigured: function( ){ |
| 62 | + if( !this.apiType || !this.apiUrl || !this.titleKey){ |
45 | 63 | return false; |
46 | 64 | } |
47 | 65 | return true; |
48 | 66 | }, |
49 | | - |
| 67 | + getApiUrl: function(){ |
| 68 | + return this.apiUrl; |
| 69 | + }, |
50 | 70 | /** |
51 | 71 | * Check if the user in the current session can save to the server |
52 | 72 | */ |
— | — | @@ -53,10 +73,26 @@ |
54 | 74 | this.getSaveToken( callback ); |
55 | 75 | }, |
56 | 76 | |
| 77 | + /** |
| 78 | + * Get up to date sequence xml from the server |
| 79 | + */ |
57 | 80 | getSmilXml: function( callback ){ |
58 | | - mw.getTitleText( this.apiUrl, this.titleKey, callback ) |
| 81 | + var _this = this; |
| 82 | + mw.getTitleText( this.getApiUrl(), this.titleKey, function( smilXml ){ |
| 83 | + // Cache the latest serverSmil ( for local change checks ) |
| 84 | + // ( save requests automatically respond with warnings on other user updates ) |
| 85 | + _this.serverSmilXml = smilXml; |
| 86 | + callback( smilXml ); |
| 87 | + }) |
| 88 | + }, |
| 89 | + // Check if there have been local changes |
| 90 | + hasLocalChanges: function(){ |
| 91 | + return ( this.serverSmilXml != this.sequencer.getSmil().getXMLString() ); |
59 | 92 | }, |
60 | | - |
| 93 | + // Check if the sequence was saved in this edit sesssion |
| 94 | + hasSequenceBeenSaved: function(){ |
| 95 | + return this.sequenceSaved; |
| 96 | + }, |
61 | 97 | // Get a save token, if unable to do so return false |
62 | 98 | getSaveToken: function( callback ){ |
63 | 99 | var _this = this; |
— | — | @@ -64,14 +100,16 @@ |
65 | 101 | callback ( this.saveToken ); |
66 | 102 | return ; |
67 | 103 | } |
68 | | - mw.getToken( this.apiUrl, this.titleKey, function( saveToken ){ |
| 104 | + mw.getToken( this.getApiUrl(), this.titleKey, function( saveToken ){ |
69 | 105 | _this.saveToken = saveToken; |
70 | 106 | callback ( _this.saveToken ) |
71 | 107 | }); |
72 | 108 | }, |
| 109 | + |
73 | 110 | // Save the sequence |
74 | | - save: function( summary, sequenceXML, callback){ |
| 111 | + save: function( saveSummary, sequenceXML, callback){ |
75 | 112 | var _this = this; |
| 113 | + mw.log("SequenceServer::Save: " + saveSummary ); |
76 | 114 | this.getSaveToken( function( token ){ |
77 | 115 | if( !token ){ |
78 | 116 | callback( false, 'could not get edit token') |
— | — | @@ -79,13 +117,17 @@ |
80 | 118 | } |
81 | 119 | var request = { |
82 | 120 | 'action' : 'edit', |
83 | | - 'summary' : summary, |
| 121 | + 'summary' : saveSummary, |
84 | 122 | 'title' : _this.titleKey, |
85 | 123 | 'text' : sequenceXML, |
86 | 124 | 'token': token |
87 | 125 | }; |
88 | | - mw.getJSON( _this.apiUrl, request, function( data ) { |
| 126 | + mw.getJSON( _this.getApiUrl(), request, function( data ) { |
89 | 127 | if( data.edit && data.edit.result == 'Success' ) { |
| 128 | + // Update the latest local variables |
| 129 | + _this.saveSummary = saveSummary |
| 130 | + _this.sequenceSaved = true; |
| 131 | + _this.serverSmilXml = sequenceXML; |
90 | 132 | callback( true ); |
91 | 133 | } else { |
92 | 134 | // xxx Should have more error handling ( conflict version save etc ) |
— | — | @@ -96,16 +138,93 @@ |
97 | 139 | }, |
98 | 140 | |
99 | 141 | /** |
| 142 | + * Check if the published file is up-to-date with the saved sequence |
| 143 | + * ( higher page revision for file than sequence ) |
| 144 | + */ |
| 145 | + isPublished: function( callback ){ |
| 146 | + var _this = this; |
| 147 | + var request = { |
| 148 | + 'prop':'revisions', |
| 149 | + 'titles' : 'File:' + this.getVideoFileName() + '|' + this.titleKey, |
| 150 | + 'rvprop' : 'ids' |
| 151 | + }; |
| 152 | + var videoPageRevision = null; |
| 153 | + var xmlPageRevision = null; |
| 154 | + mw.getJSON( _this.getApiUrl(), request, function( data ) { |
| 155 | + if( data.query && data.query.pages ){ |
| 156 | + for( page_id in data.query.pages ){ |
| 157 | + var page = data.query.pages[page_id]; |
| 158 | + if( page.revisions && page.revisions[0] && page.revisions[0].revid ){ |
| 159 | + if( page.title == _this.titleKey ){ |
| 160 | + xmlPageRevision = page.revisions[0].revid; |
| 161 | + } else { |
| 162 | + videoPageRevision = page.revisions[0].revid; |
| 163 | + } |
| 164 | + } |
| 165 | + } |
| 166 | + } |
| 167 | + if( videoPageRevision != null && xmlPageRevision != null){ |
| 168 | + callback ( ( videoPageRevision > xmlPageRevision ) ); |
| 169 | + return ; |
| 170 | + } |
| 171 | + callback( null ); |
| 172 | + }); |
| 173 | + }, |
| 174 | + |
| 175 | + /** |
| 176 | + * Get a save summary and run a callback |
| 177 | + */ |
| 178 | + getSaveSummary: function( callback ){ |
| 179 | + var _this = this; |
| 180 | + if( this.saveSummary ){ |
| 181 | + callback( this.saveSummary ); |
| 182 | + return ; |
| 183 | + } |
| 184 | + // Get the save summary for the latest revision |
| 185 | + var request = { |
| 186 | + 'prop':'revisions', |
| 187 | + 'titles' : _this.titleKey, |
| 188 | + 'rvprop' : 'user|comment|timestamp' |
| 189 | + }; |
| 190 | + mw.getJSON( _this.getApiUrl(), request, function( data ) { |
| 191 | + if( data.query && data.pages ){ |
| 192 | + for( page_id in data.pages ){ |
| 193 | + var page = data.pages[page_id]; |
| 194 | + if( page.revisions && page.revisions[0] && page.revisions[0].comment ){ |
| 195 | + callback( page.revisions[0].comment ); |
| 196 | + return; |
| 197 | + } |
| 198 | + } |
| 199 | + } |
| 200 | + callback( false ); |
| 201 | + }); |
| 202 | + }, |
| 203 | + /** |
| 204 | + * Get the video file name for saving the flat video asset to the server |
100 | 205 | * @return {String} |
101 | 206 | */ |
102 | | - getVideoTitleKey: function(){ |
103 | | - return 'File:' + this.titleKey.replace( ':', '-'); |
| 207 | + getVideoFileName: function(){ |
| 208 | + return this.titleKey.replace( ':', '-') + '.ogv'; |
104 | 209 | }, |
105 | 210 | |
106 | 211 | // get upload settings runs the callback with the post url and request data |
107 | | - getVideoUploadSettings: function( callback ){ |
| 212 | + getUploadRequestConfig: function( callback ){ |
108 | 213 | var _this = this; |
109 | | - mw.getToken( this.apiUrl, this.getVideoTitleKey, function( saveToken ){ |
| 214 | + mw.getToken( this.getApiUrl(), 'File:' + this.getVideoFileName(), function( saveToken ){ |
| 215 | + // xxx Get the latest save comment |
| 216 | + _this.getSaveSummary(function( saveSummary ){ |
| 217 | + var request = { |
| 218 | + 'token' : saveToken, |
| 219 | + 'action' : 'upload', |
| 220 | + 'format': 'json', |
| 221 | + 'filename': _this.getVideoFileName(), |
| 222 | + 'comment': 'Published Sequence: ' + saveSummary, |
| 223 | + 'ignorewarnings' : true |
| 224 | + } |
| 225 | + // Return the apiUrl and request |
| 226 | + callback( _this.getApiUrl(), request ); |
| 227 | + }); |
| 228 | + }); |
110 | 229 | } |
111 | 230 | } |
112 | 231 | |
Index: branches/MwEmbedStandAlone/remotes/mediaWiki.js |
— | — | @@ -123,15 +123,17 @@ |
124 | 124 | if( wgPageName.indexOf( "Sequence:" ) === 0 ){ |
125 | 125 | //console.log( 'spl: ' + typeof mwSetPageToLoading ); |
126 | 126 | // If on a view page set content to "loading" |
127 | | - mwSetPageToLoading(); |
128 | | - loadMwEmbed( [ 'mw.MediaWikiRemoteSequencer' ], function(){ |
129 | | - var remote = new mw.MediaWikiRemoteSequencer({ |
130 | | - 'action': wgAction, |
131 | | - 'title' : wgTitle, |
132 | | - 'target' : '#bodyContent' |
133 | | - }); |
134 | | - remote.drawUI(); |
135 | | - } ); |
| 127 | + if( wgAction == 'view' ){ |
| 128 | + mwSetPageToLoading(); |
| 129 | + loadMwEmbed( [ 'mw.MediaWikiRemoteSequencer' ], function(){ |
| 130 | + var remote = new mw.MediaWikiRemoteSequencer({ |
| 131 | + 'action': wgAction, |
| 132 | + 'title' : wgTitle, |
| 133 | + 'target' : '#bodyContent' |
| 134 | + }); |
| 135 | + remote.drawUI(); |
| 136 | + } ); |
| 137 | + } |
136 | 138 | return ; |
137 | 139 | } |
138 | 140 | |
— | — | @@ -597,7 +599,7 @@ |
598 | 600 | mw.log('gadget not installed, show install menu'); |
599 | 601 | var $gadgetBtn = $j.button({ |
600 | 602 | 'text' : gM( 'mwe-enable-gadget' ), |
601 | | - 'icon_id': 'check' |
| 603 | + 'icon': 'check' |
602 | 604 | }) |
603 | 605 | .css({ |
604 | 606 | 'font-size': '90%' |