~jstys-z/helioviewer.org/client5

« back to all changes in this revision

Viewing changes to src/js/Media/MovieManagerUI.js

  • Committer: Keith Hughitt
  • Date: 2012-06-12 20:18:46 UTC
  • Revision ID: keith.hughitt@gmail.com-20120612201846-k3nm2g2sznpfumhc
Added latest movies page

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/**
2
2
 * MovieManagerUI class definition
3
 
 * @author <a href="mailto:jeff.stys@nasa.gov">Jeff Stys</a>
4
3
 * @author <a href="mailto:keith.hughitt@nasa.gov">Keith Hughitt</a>
5
4
 */
6
 
/*jslint browser: true, white: true, onevar: true, undef: true, nomen: false,
 
5
/*jslint browser: true, white: true, onevar: true, undef: true, nomen: false, 
7
6
eqeqeq: true, plusplus: true, bitwise: true, regexp: false, strict: true,
8
7
newcap: true, immed: true, maxlen: 80, sub: true */
9
8
/*global $, window, MovieManager, MediaManagerUI, Helioviewer, helioviewer,
10
 
  layerStringToLayerArray, humanReadableNumSeconds
11
 
 */
 
9
  layerStringToLayerArray, humanReadableNumSeconds, addthis */
12
10
"use strict";
13
11
var MovieManagerUI = MediaManagerUI.extend(
14
12
    /** @lends MovieManagerUI */
16
14
    /**
17
15
     * @constructs
18
16
     * Creates a new MovieManagerUI instance
19
 
     *
 
17
     * 
20
18
     * @param {MovieManager} model MovieManager instance
21
 
     */
 
19
     */    
22
20
    init: function (movieManager) {
23
21
        var movies = Helioviewer.userSettings.get('history.movies');
24
22
        this._manager = new MovieManager(movies);
31
29
        this._movieScale = null;
32
30
        this._movieROI = null;
33
31
        this._movieLayers = null;
34
 
        this._movieEvents = null;
35
 
        this._movieEventsLabels = null;
36
32
        this._initEvents();
37
33
        this._initSettings();
38
 
 
39
 
        this.show();
40
34
    },
41
 
 
 
35
    
42
36
    /**
43
37
     * Plays the movie with the specified id if it is ready
44
38
     */
45
39
    playMovie: function (id) {
46
40
        var movie = this._manager.get(id);
47
 
 
 
41
        
48
42
        // If the movie is ready, open movie player
49
43
        if (movie.status === 2) {
50
44
            this._createMoviePlayerDialog(movie);
52
46
            return;
53
47
        }
54
48
    },
55
 
 
 
49
    
56
50
    /**
57
 
     * Uses the layers passed in to send an Ajax request to api.php, to have it
 
51
     * Uses the layers passed in to send an Ajax request to api.php, to have it 
58
52
     * build a movie. Upon completion, it displays a notification that lets the
59
 
     * user click to view it in a popup.
 
53
     * user click to view it in a popup. 
60
54
     */
61
55
    _buildMovieRequest: function (serializedFormParams) {
62
56
        var formParams, baseParams, params, frameRate;
63
 
 
 
57
        
64
58
        // Convert to an associative array for easier processing
65
59
        formParams = {};
66
 
 
 
60
        
67
61
        $.each(serializedFormParams, function (i, field) {
68
 
            formParams[field.name] = field.value;
 
62
               formParams[field.name] = field.value;
69
63
        });
70
 
 
 
64
        
71
65
        this.building = true;
72
 
 
73
 
        if ( Helioviewer.userSettings.get("state.eventLayerVisible") === false ) {
74
 
            this._movieEvents = '';
75
 
            this._movieEventsLabels = false;
76
 
        }
77
 
 
 
66
        
78
67
        // Movie request parameters
79
68
        baseParams = {
80
 
            action       : "queueMovie",
81
 
            imageScale   : this._movieScale,
82
 
            layers       : this._movieLayers,
83
 
            events       : this._movieEvents,
84
 
            eventsLabels : this._movieEventsLabels,
85
 
            scale        : Helioviewer.userSettings.get("state.scale"),
86
 
            scaleType    : Helioviewer.userSettings.get("state.scaleType"),
87
 
            scaleX       : Helioviewer.userSettings.get("state.scaleX"),
88
 
            scaleY       : Helioviewer.userSettings.get("state.scaleY"),
89
 
            format       : this._manager.format
 
69
            action     : "queueMovie",
 
70
            imageScale : this._movieScale,
 
71
            layers     : this._movieLayers,
 
72
            format     : this._manager.format
90
73
        };
91
 
 
 
74
       
92
75
        // Add ROI and start and end dates
93
 
        params = $.extend(baseParams, this._movieROI,
94
 
                          this._getMovieTimeWindow());
 
76
        params = $.extend(baseParams, this._movieROI, this._getMovieTimeWindow());
95
77
 
96
78
        // (Optional) Frame-rate or movie-length
97
79
        if (formParams['speed-method'] === "framerate") {
102
84
            baseParams['frameRate'] = formParams['framerate'];
103
85
        }
104
86
        else {
105
 
            if (formParams['movie-length'] < 5 ||
106
 
                formParams['movie-length'] > 100) {
 
87
            if (formParams['movie-length'] < 5 || formParams['movie-length'] > 100) {
107
88
                throw "Movie length must be between 5 and 100 seconds.";
108
89
            }
109
90
            baseParams['movieLength'] = formParams['movie-length'];
110
91
        }
111
 
 
 
92
        
 
93
        //console.dir(params);
 
94
        //return false;
 
95
        
112
96
        // Submit request
113
97
        this._queueMovie(params);
114
 
 
 
98
        
115
99
        this._advancedSettings.hide();
116
100
        this._settingsDialog.hide();
117
 
 
118
 
        this.show();
119
 
 
 
101
        
 
102
        //this.hideDialogs();
120
103
        this.building = false;
121
104
    },
122
 
 
 
105
    
123
106
    /**
124
107
     * Determines the start and end dates to use when requesting a movie
125
108
     */
126
109
    _getMovieTimeWindow: function () {
127
 
        var movieLength, currentTime, endTime, startTimeStr, endTimeStr,
128
 
            now, diff;
129
 
 
 
110
        var movieLength, currentTime, endTime, startTimeStr, endTimeStr, now, diff; 
 
111
        
130
112
        movieLength = Helioviewer.userSettings.get("options.movies.duration");
131
 
 
 
113
        
132
114
        // Webkit doesn't like new Date("2010-07-27T12:00:00.000Z")
133
115
        currentTime = helioviewer.getDate();
134
 
 
 
116
        
135
117
        // We want shift start and end time if needed to ensure that entire
136
118
        // duration will be used. For now, we will assume that the most
137
119
        // recent data available is close to now() to make things simple
140
122
        now = new Date();
141
123
        diff = endTime.getTime() - now.getTime();
142
124
        currentTime.addSeconds(Math.min(0, -diff / 1000));
143
 
 
 
125
        
144
126
        // Start and end datetime strings
145
127
        return {
146
128
            "startTime": currentTime.addSeconds(-movieLength / 2).toISOString(),
147
129
            "endTime"  : currentTime.addSeconds(movieLength).toISOString()
148
 
        };
 
130
        }
149
131
    },
150
 
 
 
132
    
151
133
    /**
152
134
     * Displays movie settings dialog
153
135
     */
157
139
        }
158
140
 
159
141
        var layers = helioviewer.getVisibleLayers(roi);
160
 
        var events = helioviewer.getEvents();
161
142
 
162
143
        // Make sure selection region and number of layers are acceptible
163
144
        if (!this._validateRequest(roi, layers)) {
164
145
            return;
165
146
        }
166
 
 
 
147
        
167
148
        // Store chosen ROI and layers
168
149
        this._movieScale  = helioviewer.getImageScale();
169
150
        this._movieROI    = this._toArcsecCoords(roi, this._movieScale);
170
151
        this._movieLayers = layers;
171
 
        this._movieEvents = events;
172
 
        this._movieEventsLabels = helioviewer.getEventsLabels();
173
 
 
 
152
        
174
153
        this.hide();
175
154
        this._settingsConsole.hide();
176
155
        this._settingsDialog.show();
177
 
        this._advancedSettings.show();
178
156
    },
179
 
 
 
157
    
180
158
    /**
181
159
     * Queues a movie request
182
160
     */
183
161
    _queueMovie: function (params) {
184
162
        var callback, self = this;
185
 
 
 
163
        
186
164
        // AJAX Responder
187
165
        callback = function (response) {
188
166
            var msg, movie, waitTime;
189
167
 
190
168
            if ((response === null) || response.error) {
191
169
                // Queue full
192
 
                if (response.errno === 40) {
 
170
                if (response.errno == 40) {
193
171
                    msg = response.error;
194
172
                } else {
195
173
                    // Other error
196
174
                    msg = "We are unable to create a movie for the time you " +
197
 
                        "requested. Please select a different time range " +
198
 
                        "and try again.";
 
175
                        "requested. Please select a different time range and try " +
 
176
                        "again.";
199
177
                }
200
178
                $(document).trigger("message-console-info", msg);
201
179
                return;
205
183
            }
206
184
 
207
185
            movie = self._manager.queue(
208
 
                response.id, response.eta, response.token,
209
 
                params.imageScale, params.layers, params.events,
210
 
                params.eventsLabels, params.scale, params.scaleType,
211
 
                params.scaleX, params.scaleY, new Date().toISOString(),
212
 
                params.startTime, params.endTime, params.x1, params.x2,
213
 
                params.y1, params.y2
 
186
                response.id, response.eta, response.token, 
 
187
                params.imageScale, params.layers, 
 
188
                new Date().toISOString(), params.startTime, params.endTime, 
 
189
                params.x1, params.x2, params.y1, params.y2
214
190
            );
215
191
            self._addItem(movie);
216
 
 
 
192
            
217
193
            waitTime = humanReadableNumSeconds(response.eta);
218
 
            msg = "Your video is processing and will be available in " +
 
194
            msg = "Your video is processing and will be available in " + 
219
195
                  "approximately " + waitTime + ". You may view it at any " +
220
196
                  "time after it is ready by clicking the 'Movie' button";
221
197
            $(document).trigger("message-console-info", msg);
222
198
        };
223
 
 
 
199
        
224
200
        // Make request
225
201
        $.get(Helioviewer.api, params, callback, Helioviewer.dataType);
226
202
    },
227
 
 
228
 
 
 
203
    
 
204
    
229
205
    /**
230
206
     * Initializes MovieManager-related event handlers
231
207
     */
232
208
    _initEvents: function () {
233
209
        var timer, self = this;
234
 
 
 
210
       
235
211
        this._super();
236
 
 
 
212
        
237
213
        // ROI selection buttons
238
214
        this._fullViewportBtn.click(function () {
 
215
            self.hide();
239
216
            self._showMovieSettings();
240
217
        });
241
 
 
 
218
        
242
219
        this._selectAreaBtn.click(function () {
243
 
            $(document).trigger("enable-select-tool",
 
220
            self.hide();
 
221
            $(document).trigger("enable-select-tool", 
244
222
                                $.proxy(self._showMovieSettings, self));
245
223
        });
246
 
 
 
224
        
247
225
        // Setup hover and click handlers for movie history items
248
226
        $("#movie-history .history-entry")
249
227
           .live('click', $.proxy(this._onMovieClick, this))
250
228
           .live('mouseover mouseout', $.proxy(this._onMovieHover, this));
251
 
 
252
 
 
 
229
           
 
230
        
253
231
        // Download completion notification link
254
232
        $(".message-console-movie-ready").live('click', function (event) {
255
 
            var movie = $(event.currentTarget).data('movie');
 
233
            var movie = $(event.currentTarget).data('movie');            
256
234
            self._createMoviePlayerDialog(movie);
257
235
        });
258
 
 
 
236
        
259
237
        // Update tooltip when movie is finished downloading
260
238
        $(document).bind("movie-ready", function (event, movie) {
261
239
            $("#" + self._type + "-" + movie.id).qtip("destroy");
262
240
            self._buildPreviewTooltip(movie);
263
241
        });
264
 
 
 
242
        
265
243
        // Upload form submission
266
244
        $("#youtube-video-info").submit(function () {
267
245
            self.submitVideoUploadForm();
268
246
            return false;
269
247
        });
270
 
 
 
248
        
271
249
        // Toggle advanced settings display
272
250
        $("#movie-settings-toggle-advanced").click(function () {
273
251
            // If help is visible, simply hide
276
254
                self._settingsForm.show();
277
255
                return;
278
256
            }
279
 
 
 
257
            
280
258
            // Otherwise, toggle advanced settings visibility
281
259
            if (self._advancedSettings.is(":visible")) {
282
260
                self._advancedSettings.animate({"height": 0}, function () {
288
266
                });
289
267
            }
290
268
        });
291
 
 
 
269
        
292
270
        // Toggle help display
293
271
        $("#movie-settings-toggle-help").click(function () {
294
272
            self._settingsForm.toggle();
295
273
            self._settingsHelp.toggle();
296
274
        });
297
275
    },
298
 
 
 
276
    
299
277
    /**
300
278
     * Initializes movie settings events
301
279
     */
302
280
    _initSettings: function () {
303
 
        var length, lengthInput, duration, durationSelect,
304
 
            frameRateInput, settingsForm, self = this;
 
281
        var length, lengthInput, duration, durationSelect,  
 
282
            frameRateInput, lengthInput, settingsForm, self = this;
305
283
 
306
284
        // Advanced movie settings
307
 
        frameRateInput = $("#frame-rate");
308
 
        lengthInput    = $("#movie-length");
309
 
        durationSelect = $("#movie-duration");
310
 
 
 
285
        frameRateInput    = $("#frame-rate");
 
286
        lengthInput       = $("#movie-length");
 
287
        durationSelect    = $("#movie-duration");
 
288
        
311
289
        // Speed method enable/disable
312
290
        $("#speed-method-f").change(function () {
313
291
            lengthInput.attr("disabled", true);
314
292
            frameRateInput.attr("disabled", false);
315
293
        }).attr("checked", "checked").change();
316
 
 
 
294
                
317
295
        $("#speed-method-l").change(function () {
318
296
            frameRateInput.attr("disabled", true);
319
 
            lengthInput.attr("disabled", false);
 
297
            lengthInput.attr("disabled", false);            
320
298
        });
321
 
 
 
299
        
322
300
        // Cancel button
323
301
        $("#movie-settings-cancel-btn").button().click(function (e) {
324
302
            self._advancedSettings.hide();
325
303
            self._settingsDialog.hide();
326
304
            self.show();
327
305
        });
328
 
 
 
306
        
329
307
        // Submit button
330
308
        settingsForm = $("#movie-settings-form");
331
309
 
334
312
            try {
335
313
                self._buildMovieRequest(settingsForm.serializeArray());
336
314
            } catch (ex) {
337
 
                // Display an error message if invalid values are specified
338
 
                // for movie settings
 
315
                // Display an error message if invalid values are specified for movie settings
339
316
                self._settingsConsole.text(ex).fadeIn(1000, function () {
340
317
                    setTimeout(function () {
341
318
                        self._settingsConsole.text(ex).fadeOut(1000);
347
324
 
348
325
        // Movie duration
349
326
        duration = Helioviewer.userSettings.get("options.movies.duration"),
350
 
 
 
327
        
351
328
        // Duration event listener
352
329
        durationSelect.bind('change', function (e) {
353
330
            Helioviewer.userSettings.set("options.movies.duration",
354
331
            parseInt(this.value, 10));
355
332
        });
356
 
 
 
333
        
357
334
        // Reset to default values
358
335
        frameRateInput.val(15);
359
336
        lengthInput.val(20);
360
 
        durationSelect.find("[value=" + duration + "]").attr("selected", "selected");
 
337
        durationSelect.find("[value=" + duration + "]").attr("selected", "selected")
 
338
        
361
339
    },
362
 
 
 
340
    
363
341
    /**
364
342
     * If the movie is ready, play the movie in a popup dialog. Otherwise do
365
343
     * nothing.
366
344
     */
367
345
    _onMovieClick: function (event) {
368
346
        var id, movie, dialog, action;
369
 
 
 
347
        
370
348
        id    = $(event.currentTarget).data('id');
371
349
        movie = this._manager.get(id);
372
 
 
 
350
        
373
351
        // If the movie is ready, open movie player
374
352
        if (movie.status === 2) {
375
353
            dialog = $("movie-player-" + id);
378
356
            if (dialog.length > 0) {
379
357
                action = dialog.dialog('isOpen') ? "close" : "open";
380
358
                dialog.dialog(action);
381
 
 
 
359
                
382
360
            // Otherwise create and display the movie player dialog
383
361
            } else {
384
362
                this._createMoviePlayerDialog(movie);
386
364
        }
387
365
        return false;
388
366
    },
389
 
 
 
367
   
390
368
   /**
391
369
    * Shows movie details and preview.
392
370
    */
393
371
    _onMovieHover: function (event) {
394
372
        if (event.type === 'mouseover') {
395
 
            //console.log('hover on');
 
373
            //console.log('hover on'); 
396
374
        } else {
397
 
            //console.log('hover off');
 
375
            //console.log('hover off'); 
398
376
        }
399
377
    },
400
 
 
 
378
    
401
379
    /**
402
 
     * Creates HTML for a preview tooltip with a preview thumbnail,
 
380
     * Creates HTML for a preview tooltip with a preview thumbnail, 
403
381
     * if available, and some basic information about the screenshot or movie
404
382
     */
405
383
    _buildPreviewTooltipHTML: function (movie) {
406
384
        var width, height, thumbnail, html = "";
407
 
 
 
385
        
408
386
        if (movie.status === 2) {
409
 
            thumbnail = movie.thumbnail;
 
387
            // Use relative paths for thumbnails (helps with debugging in VM)
 
388
            if (Helioviewer.api === "api/index.php") {
 
389
                thumbnail = movie.thumbnail.substr(movie.thumbnail.search("cache"));    
 
390
            } else {
 
391
                thumbnail = movie.thumbnail;
 
392
            }            
410
393
 
411
 
            html += "<div style='text-align: center;'>" +
 
394
            html += "<div style='text-align: center;'>" + 
412
395
                "<img src='" + thumbnail +
413
396
                "' width='95%' alt='preview thumbnail' /></div>";
414
 
 
 
397
                
415
398
            width  = movie.width;
416
399
            height = movie.height;
417
400
        } else {
421
404
 
422
405
        html += "<table class='preview-tooltip'>" +
423
406
            "<tr><td><b>Start:</b></td><td>" + movie.startDate + "</td></tr>" +
424
 
            "<tr><td><b>End:</b></td><td>"   + movie.endDate   + "</td></tr>" +
425
 
            "<tr><td><b>Scale:</b></td><td>" + movie.imageScale.toFixed(2) +
 
407
            "<tr><td><b>End:</b></td><td>" + movie.endDate + "</td></tr>" +
 
408
            "<tr><td><b>Scale:</b></td><td>" + movie.imageScale.toFixed(2) + 
426
409
            " arcsec/px</td></tr>" +
427
 
            "<tr><td><b>Dimensions:</b></td><td>" + width +
 
410
            "<tr><td><b>Dimensions:</b></td><td>" + width + 
428
411
            "x" + height +
429
412
            " px</td></tr>" +
430
413
            "</table>";
431
414
 
432
415
        return html;
433
416
    },
434
 
 
 
417
   
435
418
    /**
436
419
     * @description Opens a pop-up with the movie player in it.
437
420
     */
438
421
    _createMoviePlayerDialog: function (movie) {
439
 
        var dimensions, title, uploadURL, flvURL, swfURL, html, dialog,
 
422
        var dimensions, title, uploadURL, flvURL, swfURL, html, dialog, 
440
423
            screenshot, callback, self = this;
441
 
 
 
424
        
442
425
        // Make sure dialog fits nicely inside the browser window
443
426
        dimensions = this.getVideoPlayerDimensions(movie.width, movie.height);
444
 
 
 
427
        
445
428
        // Movie player HTML
446
 
        html = self.getVideoPlayerHTML(movie, dimensions.width,
447
 
                                       dimensions.height);
448
 
 
 
429
        html = self.getVideoPlayerHTML(movie.id, dimensions.width, 
 
430
                                       dimensions.height, movie.url);
 
431
        
449
432
        // Movie player dialog
450
433
        dialog = $(
451
 
            "<div id='movie-player-" + movie.id + "' " +
 
434
            "<div id='movie-player-" + movie.id + "' " + 
452
435
            "class='movie-player-dialog'></div>"
453
436
        ).append(html);
454
 
 
 
437
        
455
438
        dialog.find(".video-download-icon").click(function () {
456
439
            // Google analytics event
457
440
            if (typeof(_gaq) != "undefined") {
458
441
                _gaq.push(['_trackEvent', 'Movies', 'Download']);
459
 
            }
 
442
            } 
460
443
        });
461
 
 
 
444
        
462
445
        // Movie dialog title
463
 
        title = movie.name + " (" + movie.startDate + " - " +
 
446
        title = movie.name + " (" + movie.startDate + " - " + 
464
447
                movie.endDate + " UTC)";
465
 
 
 
448
        
466
449
        // Have to append the video player here, otherwise adding it to the div
467
 
        // beforehand results in the browser attempting to download it.
 
450
        // beforehand results in the browser attempting to download it. 
468
451
        dialog.dialog({
469
452
            title     : "Movie Player: " + title,
470
 
            width     : ((dimensions.width < 575)?600:dimensions.width+25),
471
 
            height    : dimensions.height + 80,
 
453
            width     : dimensions.width  + 34,
 
454
            height    : dimensions.height + 104,
472
455
            resizable : $.support.h264 || $.support.vp8,
473
456
            close     : function () {
474
457
                            $(this).empty();
476
459
            zIndex    : 9999,
477
460
            show      : 'fade'
478
461
        });
479
 
 
480
 
        // TODO 2011/01/04: Disable keyboard shortcuts when in text fields!
 
462
        
 
463
        // TODO 2011/01/04: Disable keyboard shortcuts when in text fields! 
481
464
        // (already done for input fields...)
482
 
 
 
465
       
483
466
        // Initialize YouTube upload button
484
467
        $('#youtube-upload-' + movie.id).click(function () {
485
468
            self.showYouTubeUploadDialog(movie);
486
469
            return false;
487
470
        });
488
 
 
 
471
        
489
472
        // Initialize video link button
490
473
        $('#video-link-' + movie.id).click(function () {
491
474
            // Hide flash movies to prevent blocking
495
478
            helioviewer.displayMovieURL(movie.id);
496
479
            return false;
497
480
        });
498
 
 
 
481
        
 
482
        // BBCode link
 
483
        $("#bbcode-" + movie.id).click(function () {
 
484
            // Hide flash movies to prevent blocking
 
485
            if (!($.support.h264 || $.support.vp8)) {
 
486
                $(".movie-player-dialog").dialog("close");
 
487
            }
 
488
            self._showBBCode(movie.id, dimensions.width, dimensions.height);
 
489
            return false;
 
490
        });
 
491
        
499
492
        // Flash video URL
500
 
        flvURL = Helioviewer.api +
 
493
        flvURL = Helioviewer.api + 
501
494
                "/?action=downloadMovie&format=flv&id=" + movie.id;
502
 
 
 
495
                 
503
496
        // SWF URL (The flowplayer SWF directly provides best Facebook support)
504
 
        swfURL = Helioviewer.root +
505
 
                 "/lib/flowplayer/flowplayer-3.2.8.swf?config=" +
506
 
                 encodeURIComponent("{'clip':{'url': '../../" + flvURL + "'}}");
507
 
 
508
 
        screenshot = movie.thumbnail.substr(0, movie.thumbnail.length - 9) +
 
497
        swfURL = Helioviewer.root + 
 
498
                 "/lib/flowplayer/flowplayer-3.2.8.swf?config=" + 
 
499
                 encodeURIComponent("{'clip':{'url':'" + flvURL + "'}}");
 
500
                 
 
501
        screenshot = movie.thumbnail.substr(0, movie.thumbnail.length - 9) + 
509
502
                     "full.png";
 
503
                     
 
504
        // If AddThis is not supported, skip toolbox initialization
 
505
        if (typeof(addthis) == "undefined") {
 
506
            return;
 
507
        }
 
508
        
 
509
        // First get a shortened version of the movie URL
 
510
        callback = function (response) {
 
511
            // Then initialize AddThis toolbox
 
512
            addthis.toolbox('#add-this-' + movie.id, {}, {
 
513
                url: response.data.url,
 
514
                title: "Helioviewer.org",
 
515
                description: title,
 
516
                screenshot: screenshot,
 
517
                swfurl: swfURL,
 
518
                width: movie.width,
 
519
                height: movie.height
 
520
            });
 
521
        };
 
522
 
 
523
        // Initialize AddThis toolbox once a shortened URL has been requested
 
524
        $.ajax({
 
525
            url: Helioviewer.api,
 
526
            dataType: Helioviewer.dataType,
 
527
            data: {
 
528
                "action": "shortenURL",
 
529
                "queryString": "movieId=" + movie.id 
 
530
            },
 
531
            success: callback
 
532
        });
510
533
    },
511
 
 
 
534
       
512
535
    /**
513
536
     * Opens YouTube uploader either in a separate tab or in a dialog
514
537
     */
515
538
    showYouTubeUploadDialog: function (movie) {
516
539
        var title, tags, url1, url2, description;
517
 
 
 
540
        
518
541
        // Suggested movie title
519
 
        title = movie.name + " (" + movie.startDate + " - " +
 
542
        title = movie.name + " (" + movie.startDate + " - " + 
520
543
                movie.endDate + " UTC)";
521
544
 
522
 
        // Suggested YouTube tags
 
545
        // Suggested YouTube tags  
523
546
        tags = [];
524
547
 
525
548
        $.each(movie.layers.split("],["), function (i, layerStr) {
526
 
alert('MovieManagerUI.showYouTubeUploadDialog() assumes 4-level hierarchy in layerStr');
527
549
            var parts = layerStr.replace(']', "").replace('[', "")
528
550
                        .split(",").slice(0, 4);
529
 
 
 
551
            
530
552
            // Add observatories, instruments, detectors and measurements
531
553
            $.each(parts, function (i, item) {
532
554
                if ($.inArray(item, tags) === -1) {
533
555
                    tags.push(item);
534
 
                }
 
556
                }                
535
557
            });
536
558
        });
537
 
 
 
559
        
538
560
        // URLs
539
 
        url1 = Helioviewer.api + "/?action=playMovie&id=" + movie.id +
540
 
               "&format=mp4&hq=true";
541
 
        url2 = Helioviewer.api + "/?action=downloadMovie&id=" + movie.id +
542
 
               "&format=mp4&hq=true";
543
 
 
 
561
        url1 = Helioviewer.root + "/?movieId=" + movie.id;
 
562
        url2 = Helioviewer.root + "/api/?action=downloadMovie&id=" + movie.id + "&format=mp4&hq=true"; 
 
563
               
544
564
        // Suggested Description
545
 
        description = "This movie was produced by Helioviewer.org. See the " +
546
 
                      "original at " + url1 + " or download a high-quality " +
 
565
        description = "This movie was produced by Helioviewer.org. See the " + 
 
566
                      "original at " + url1 + " or download a high-quality " + 
547
567
                      "version from " + url2;
548
 
 
 
568
                     
549
569
        // Update form defaults
550
570
        $("#youtube-title").val(title);
551
571
        $("#youtube-tags").val(tags);
562
582
            "height": 440
563
583
        });
564
584
    },
565
 
 
 
585
    
566
586
    /**
567
587
     * Processes form and submits video upload request to YouTube
568
588
     */
569
589
    submitVideoUploadForm: function (event) {
570
 
        var params, successMsg, uploadDialog, url, form, loader, callback,
571
 
            self = this;
572
 
 
 
590
        var params, successMsg, uploadDialog, url, form, loader, callback, self = this;
 
591
            
573
592
        // Validate and submit form
574
593
        try {
575
594
            this._validateVideoUploadForm();
577
596
            this._displayValidationErrorMsg(ex);
578
597
            return false;
579
598
        }
580
 
 
 
599
        
581
600
        // Clear any remaining error messages before continuing
582
601
        $("#upload-error-console").hide();
583
602
 
588
607
        callback = function (auth) {
589
608
            loader.hide();
590
609
            form.show();
591
 
 
 
610
    
592
611
            // Base URL
593
612
            url = Helioviewer.api + "?" + $("#youtube-video-info").serialize();
594
 
 
 
613
    
595
614
            // If the user has already authorized Helioviewer, upload the movie
596
615
            if (auth) {
597
 
                $.get(url, {"action": "uploadMovieToYouTube"},
598
 
                    function (response) {
599
 
                        if (response.error) {
600
 
                            self.hide();
601
 
                            $(document).trigger("message-console-warn",
602
 
                                                [response.error]);
603
 
                        }
 
616
                $.get(url, {"action": "uploadMovieToYouTube"}, function (response) {
 
617
                    if (response.error) {
 
618
                        self.hide();
 
619
                        $(document).trigger("message-console-warn", [response.error]);
 
620
                    }
604
621
                }, "json");
605
622
            } else {
606
623
                // Otherwise open an authorization page in a new tab/window
607
624
                window.open(url + "&action=getYouTubeAuth", "_blank");
608
625
            }
609
 
 
 
626
            
610
627
            // Close the dialog
611
628
            $("#upload-dialog").dialog("close");
612
629
            return false;
619
636
            success: callback
620
637
        });
621
638
    },
622
 
 
 
639
    
623
640
    /**
624
641
     * Displays an error message in the YouTube upload dialog
625
 
     *
 
642
     * 
626
643
     * @param string Error message
627
644
     */
628
645
    _displayValidationErrorMsg: function (ex) {
629
646
        var errorConsole = $("#upload-error-console");
630
 
 
 
647
        
631
648
        errorConsole.html("<b>Error:</b> " + ex).fadeIn(function () {
632
649
            window.setTimeout(function () {
633
650
                errorConsole.fadeOut();
634
651
            }, 15000);
635
652
        });
636
653
    },
637
 
 
 
654
    
638
655
    /**
639
656
     * Validates title, description and keyword fields for YouTube upload.
640
 
     *
 
657
     * 
641
658
     * @see http://code.google.com/apis/youtube/2.0/reference.html
642
659
     *      #Media_RSS_elements_reference
643
660
     */
645
662
        var keywords         = $("#youtube-tags").val(),
646
663
            keywordMinLength = 2,
647
664
            keywordMaxLength = 30;
648
 
 
 
665
            
649
666
        // Make sure the title field is not empty
650
667
        if ($("#youtube-title").val().length === 0) {
651
668
            throw "Please specify a title for the movie.";
652
669
        }
653
 
 
 
670
        
654
671
        // User must specify at least one keyword
655
672
        if (keywords.length === 0) {
656
673
            throw "You must specifiy at least one tag for your video.";
657
674
        }
658
 
 
 
675
        
659
676
        // Make sure each keywords are between 2 and 30 characters each
660
677
        $.each(keywords.split(","), function (i, keyword) {
661
678
            var len = $.trim(keyword).length;
662
 
 
 
679
    
663
680
            if (len > keywordMaxLength) {
664
 
                throw "YouTube tags must not be longer than " +
 
681
                throw "YouTube tags must not be longer than " + 
665
682
                      keywordMaxLength + " characters each.";
666
683
            } else if (len < keywordMinLength) {
667
 
                throw "YouTube tags must be at least " + keywordMinLength +
 
684
                throw "YouTube tags must be at least " + keywordMinLength + 
668
685
                      " characters each.";
669
686
            }
670
 
            return;
 
687
            return;                     
671
688
        });
672
 
 
 
689
    
673
690
        // < and > are not allowed in title, description or keywords
674
 
        $.each($("#youtube-video-info input[type='text'], " +
 
691
        $.each($("#youtube-video-info input[type='text'], " + 
675
692
                 "#youtube-video-info textarea"), function (i, input) {
676
693
            if ($(input).val().match(/[<>]/)) {
677
694
                throw "< and > characters are not allowed";
679
696
            return;
680
697
        });
681
698
    },
682
 
 
 
699
    
683
700
    /**
684
701
     * Adds a movie to the history using it's id
685
702
     */
686
703
    addMovieUsingId: function (id) {
687
704
        var callback, params, movie, self = this;
688
 
 
 
705
        
689
706
        callback = function (response) {
690
707
            if (response.status === 2) {
 
708
                // id, duration, imageScale, layers, dateRequested, startDate, 
 
709
                //  endDate, frameRate, numFrames, x1, x2, y1, y2, width, height
691
710
                movie = self._manager.add(
692
711
                    id,
693
712
                    response.duration,
694
713
                    response.imageScale,
695
714
                    response.layers,
696
 
                    response.events,
697
 
                    response.eventsLabels,
698
 
                    response.scale,
699
 
                    response.scaleType,
700
 
                    response.scaleX,
701
 
                    response.scaleY,
702
715
                    response.timestamp.replace(" ", "T") + ".000Z",
703
716
                    response.startDate,
704
717
                    response.endDate,
713
726
                    response.thumbnails.small,
714
727
                    response.url
715
728
                );
716
 
 
 
729
                
717
730
                self._addItem(movie);
718
731
                self._createMoviePlayerDialog(movie);
719
732
            }
720
733
        };
721
 
 
 
734
        
722
735
        params = {
723
 
            "action" : "getMovieStatus",
 
736
            "action" : "getMovieStatus", 
724
737
            "id"     : id,
725
738
            "format" : self._manager.format,
726
739
            "verbose": true
727
740
        };
728
741
        $.get(Helioviewer.api, params, callback, Helioviewer.dataType);
729
742
    },
730
 
 
 
743
    
731
744
    /**
732
745
     * Determines dimensions for which movie should be displayed
733
746
     */
735
748
        var maxWidth    = $(window).width() * 0.80,
736
749
            maxHeight   = $(window).height() * 0.80,
737
750
            scaleFactor = Math.max(1, width / maxWidth, height / maxHeight);
738
 
 
 
751
        
739
752
        return {
740
753
            "width"  : Math.floor(width  / scaleFactor),
741
754
            "height" : Math.floor(height / scaleFactor)
742
755
        };
743
756
    },
744
 
 
 
757
    
745
758
    /**
746
 
     * Decides how to display video and returns HTML corresponding to that
 
759
     * Decides how to display video and returns HTML corresponding to that 
747
760
     * method
748
761
     */
749
 
    getVideoPlayerHTML: function (movie, width, height) {
750
 
        var downloadURL, downloadLink, youtubeBtn,
751
 
            linkBtn, linkURL, tweetBtn, facebookBtn;
752
 
 
753
 
        // Download
754
 
        downloadURL = Helioviewer.api + "?action=downloadMovie&id=" + movie.id +
 
762
    getVideoPlayerHTML: function (id, width, height, url) {
 
763
        var downloadURL, downloadLink, youtubeBtn, bbcodeBtn, addthisBtn, 
 
764
            linkBtn, linkURL;
 
765
        
 
766
        downloadURL = Helioviewer.api + "?action=downloadMovie&id=" + id + 
755
767
                      "&format=mp4&hq=true";
756
768
 
757
 
        downloadLink = "<div style='float:left;'><a target='_parent' href='" + downloadURL +
758
 
            "' title='Download high-quality video'>" +
759
 
            "<img style='width:93px; height:32px;' class='video-download-icon' " +
760
 
            "src='resources/images/download_93x32.png' /></a></div>";
761
 
 
762
 
        // Upload to YouTube
763
 
        youtubeBtn = '<div style="float:left;"><a id="youtube-upload-' + movie.id + '" href="#" ' +
764
 
            'target="_blank"><img class="youtube-icon" ' +
765
 
            'title="Upload video to YouTube" style="width:79px;height:32px;" ' +
766
 
            'src="resources/images/youtube_79x32.png" /></a></div>';
767
 
 
768
 
        // Link
769
 
        linkURL = helioviewer.serverSettings.rootURL + "/?movieId=" + movie.id;
770
 
 
771
 
        linkBtn = "<div style='float:left;'><a id='video-link-" + movie.id + "' href='" + linkURL +
772
 
            "' title='Get a link to the movie' " +
773
 
            "target='_blank'><img class='video-link-icon' " +
774
 
            "style='width:79px; height:32px;' " +
775
 
            "src='resources/images/link_79x32.png' /></a></div>";
776
 
 
777
 
        // Tweet Movie Button
778
 
        tweetBtn = '<div style="float:right;"><a href="https://twitter.com/share" class="twitter-share-button" data-related="helioviewer" data-lang="en" data-size="medium" data-count="horizontal" data-url="http://'+document.domain+'/?movieId='+movie.id+'" data-text="Movie of the Sun created on Helioviewer.org:" data-hashtags="helioviewer" data-related="helioviewer">Tweet</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script></div>';
779
 
 
780
 
        // Like Movie on Facebook Button
781
 
        facebookBtn = '<div style="float:right;"><iframe src="//www.facebook.com/plugins/like.php?href='+encodeURIComponent('http://'+document.domain+'/?movieId='+movie.id)+'&amp;width=90&amp;height=21&amp;colorscheme=dark&amp;layout=button_count&amp;action=like&amp;show_faces=false&amp;send=false&amp;appId=6899099925" scrolling="no" frameborder="0" style="border:none; overflow:hidden; height:21px; width:90px;" allowTransparency="false"></iframe></div>';
782
 
 
 
769
        downloadLink = "<a target='_parent' href='" + downloadURL + 
 
770
            "' title='Download high-quality video'>" + 
 
771
            "<img class='video-download-icon' " + 
 
772
            "src='resources/images/Tango/1321375855_go-bottom.png' /></a>";
 
773
        
 
774
        youtubeBtn = "<a id='youtube-upload-" + id + "' href='#' " + 
 
775
            "target='_blank'><img class='youtube-icon' " + 
 
776
            "title='Upload video to YouTube' " + 
 
777
            "src='resources/images/Social.me/48 " + 
 
778
            "by 48 pixels/youtube.png' /></a>";
 
779
            
 
780
        linkURL = helioviewer.serverSettings.rootURL + "/?movieId=" + id;
 
781
            
 
782
        linkBtn = "<a id='video-link-" + id + "' href='" + linkURL + 
 
783
            "' title='Get a link to the movie' " + 
 
784
            "target='_blank'><img class='video-link-icon' " + 
 
785
            "style='margin-left: 3px' " + 
 
786
            "src='resources/images/berlin/32x32/link.png' /></a>";
 
787
            
 
788
        bbcodeBtn = "<img class='bbcode-btn' id='bbcode-" + id + "' src='resources/images/codefisher/bbcode_32.png' alt='Helioviewer.org Community Forums BBCode link string' />";
 
789
            
 
790
        addthisBtn = "<div style='display:inline; " + 
 
791
            "float: right;' id='add-this-" + id + 
 
792
            "' class='addthis_default_style addthis_32x32_style'>" +
 
793
            "<a class='addthis_button_facebook addthis_32x32_style'></a>" +
 
794
            "<a class='addthis_button_twitter addthis_32x32_style'></a>" +
 
795
            "<a class='addthis_button_email addthis_32x32_style'></a>" +
 
796
            "<a class='addthis_button_google addthis_32x32_style'></a>" +
 
797
            "<a class='addthis_button_compact addthis_32x32_style'></a>" +
 
798
            "</div>";
 
799
        
783
800
        // HTML5 Video (H.264 or WebM)
784
801
        if ($.support.vp8 || $.support.h264) {
785
802
            // Work-around: use relative paths to simplify debugging
786
 
            url = movie.url.substr(movie.url.search("cache"));
787
 
 
 
803
            url = url.substr(url.search("cache"));
 
804
            
788
805
            // IE9 only supports relative dimensions specified using CSS
789
 
            return '<div><video id="movie-player-' + movie.id + '" src="' + url +
790
 
                   '" controls preload autoplay' +
791
 
                   ' style="width:100%; height: 90%;"></video></div>' +
792
 
                   '<div style="width:100%"><div style="float:left;" class="video-links">' +
793
 
                   youtubeBtn + linkBtn + downloadLink +
794
 
                   '</div> <div style="float:right;">' + facebookBtn +
795
 
                   tweetBtn + '</div></div>';
 
806
            return "<video id='movie-player-" + id + "' src='" + url +
 
807
                   "' controls preload autoplay" + 
 
808
                   " style='width:100%; height: 90%;'></video>" + 
 
809
                   "<span class='video-links'>" + downloadLink + youtubeBtn +
 
810
                   linkBtn + addthisBtn + "</span>";
796
811
        }
797
812
 
798
813
        // Fallback (flash player)
799
814
        else {
800
 
            var url = Helioviewer.api + '?action=playMovie&id=' + movie.id +
801
 
                  '&width=' + width + "&height=" + height +
 
815
            url = Helioviewer.api + '?action=playMovie&id=' + id +
 
816
                  '&width=' + width + "&height=" + height + 
802
817
                  '&format=flv';
803
 
 
804
 
            return '<div id="movie-player-' + movie.id + '">' +
805
 
                       '<iframe src="' + url + '" width="' + width +
806
 
                       '" height="' + height + '" marginheight="0" marginwidth="0" ' +
807
 
                       'scrolling="no" frameborder="0" style="width: 100%; margin-bottom: 2px;" />' +
808
 
                   '</div>' +
809
 
                   '<div style="width:100%;">' +
810
 
                       '<div style="float:left;" class="video-links">' +
811
 
                        youtubeBtn + linkBtn + downloadLink +
812
 
                   '</div>' +
813
 
                   '<div style="float:right;">' + facebookBtn + tweetBtn +
814
 
                   '</div>';
 
818
            
 
819
            return "<div id='movie-player-" + id + "'>" + 
 
820
                   "<iframe src=" + url + " width='" + width +  
 
821
                   "' height='" + height + "' marginheight=0 marginwidth=0 " +
 
822
                   "scrolling=no frameborder=0 style='margin-bottom: 2px;' />" +
 
823
                   "<br />" + 
 
824
                   "<span class='video-links'>" + downloadLink + youtubeBtn +
 
825
                   linkBtn + bbcodeBtn + addthisBtn + "</span></div>";
815
826
        }
816
827
    },
817
 
 
 
828
    
 
829
    _showBBCode: function(id, width, height) {
 
830
            var bbcode = "[hvmovie width=" + width + " height=" + height + 
 
831
                         "]" + id + "[/hvmovie]";             
 
832
            
 
833
            // Display BBCode
 
834
            $("#bbcode-dialog").dialog({
 
835
                //dialogClass: 'helioviewer-modal-dialog',
 
836
                height    : 100,
 
837
                width     : $('html').width() * 0.5,
 
838
                modal     : true,
 
839
                resizable : false,
 
840
                title     : "Helioviewer.org Movie BBCode",
 
841
                open      : function (e) {
 
842
                    //$('.ui-widget-overlay').hide().fadeIn();
 
843
                    $(this).find('input').attr('value', bbcode).select();
 
844
                }
 
845
            });
 
846
    },
 
847
    
818
848
    /**
819
849
     * Refreshes status information for movies in the history
820
850
     */
821
851
    _refresh: function () {
822
852
        var status, elapsedTime;
823
 
 
 
853
        
824
854
        // Update the status information for each row in the history
825
855
        $.each(this._manager.toArray(), function (i, item) {
826
856
            status = $("#movie-" + item.id).find(".status");
829
859
            if (item.status === 2) {
830
860
                elapsedTime = Date.parseUTCDate(item.dateRequested)
831
861
                                  .getElapsedTime();
832
 
                status.html(elapsedTime);
 
862
                status.html(elapsedTime);                
833
863
            // For failed movie requests, display an error
834
864
            } else if (item.status === 3) {
835
865
                status.html("<span style='color:LightCoral;'>Error</span>");
839
869
            }
840
870
        });
841
871
    },
842
 
 
 
872
    
843
873
    /**
844
 
     * Validates the request and returns false if any of the requirements are
 
874
     * Validates the request and returns false if any of the requirements are 
845
875
     * not met
846
876
     */
847
877
    _validateRequest: function (roi, layerString) {
848
878
        var layers, visibleLayers, message;
849
 
 
 
879
        
850
880
        layers = layerStringToLayerArray(layerString);
851
881
        visibleLayers = $.grep(layers, function (layer, i) {
852
882
            var parts = layer.split(",");