pageflow-vr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.eslintrc +38 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +5 -0
  6. data/CHANGELOG.md +7 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +4 -0
  9. data/README.md +60 -0
  10. data/Rakefile +9 -0
  11. data/app/assets/images/pageflow/vr/themes/default/pictograms/sprite.png +0 -0
  12. data/app/assets/images/pageflow/vr/themes/default/pictograms/wide.png +0 -0
  13. data/app/assets/images/pageflow/vr_pictogram_small.png +0 -0
  14. data/app/assets/javascripts/pageflow/vr/browser/vr_view_cardboard_support.js +3 -0
  15. data/app/assets/javascripts/pageflow/vr/browser/vr_view_support.js +4 -0
  16. data/app/assets/javascripts/pageflow/vr/components/icons/cardboard.jsx +10 -0
  17. data/app/assets/javascripts/pageflow/vr/components/icons/play.jsx +11 -0
  18. data/app/assets/javascripts/pageflow/vr/components/no_vr_view.jsx +34 -0
  19. data/app/assets/javascripts/pageflow/vr/components/page.jsx +201 -0
  20. data/app/assets/javascripts/pageflow/vr/components/vr_view.jsx +160 -0
  21. data/app/assets/javascripts/pageflow/vr/components.js +7 -0
  22. data/app/assets/javascripts/pageflow/vr/editor/views/configuration_editor_view.js +65 -0
  23. data/app/assets/javascripts/pageflow/vr/editor/views/fallback_preview_mode_view.js +9 -0
  24. data/app/assets/javascripts/pageflow/vr/editor.js +73 -0
  25. data/app/assets/javascripts/pageflow/vr/page_type.js +2 -0
  26. data/app/assets/javascripts/pageflow/vr/player/media_events.js +36 -0
  27. data/app/assets/javascripts/pageflow/vr/player.js +93 -0
  28. data/app/assets/javascripts/pageflow/vr.js +7 -0
  29. data/app/assets/stylesheets/pageflow/vr/themes/default.scss +85 -0
  30. data/app/controllers/pageflow/vr/static_files_controller.rb +13 -0
  31. data/app/views/pageflow/vr/static_files/vrview.css +55 -0
  32. data/app/views/pageflow/vr/static_files/vrview.html +22 -0
  33. data/app/views/pageflow/vr/static_files/vrview.js +44415 -0
  34. data/bin/console +14 -0
  35. data/bin/setup +8 -0
  36. data/config/locales/de.yml +94 -0
  37. data/config/locales/en.yml +94 -0
  38. data/config/routes.rb +3 -0
  39. data/lib/pageflow/vr/engine.rb +14 -0
  40. data/lib/pageflow/vr/plugin.rb +10 -0
  41. data/lib/pageflow/vr/projection_auto_detection.rb +42 -0
  42. data/lib/pageflow/vr/version.rb +5 -0
  43. data/lib/pageflow-vr.rb +31 -0
  44. data/pageflow-vr.gemspec +27 -0
  45. metadata +171 -0
@@ -0,0 +1,65 @@
1
+ pageflow.vr.ConfigurationEditorView = pageflow.ConfigurationEditorView.extend({
2
+ configure: function() {
3
+ this.tab('general', function() {
4
+ this.group('general');
5
+
6
+ this.input('additional_title', pageflow.TextInputView);
7
+ this.input('additional_description', pageflow.TextAreaInputView, {size: 'short'});
8
+
9
+ this.input('control_bar_text', pageflow.TextInputView, {
10
+ placeholder: I18n.t('pageflow.public.vr.start', {
11
+ locale: pageflow.entry.configuration.get('locale')
12
+ })
13
+ });
14
+ });
15
+
16
+ this.tab('files', function() {
17
+ this.input('video_id', pageflow.FileInputView, {
18
+ collection: 'video_files',
19
+ filter: 'with_projection',
20
+ positioning: false
21
+ });
22
+ this.input('autoplay', pageflow.CheckBoxInputView);
23
+ this.input('start_yaw', pageflow.SliderInputView, {
24
+ unit: '°',
25
+ maxValue: 360
26
+ });
27
+ this.input('poster_id', pageflow.FileInputView, {
28
+ collection: 'image_files',
29
+ filter: 'with_projection',
30
+ positioning: false
31
+ });
32
+ this.input('thumbnail_image_id', pageflow.FileInputView, {
33
+ collection: 'image_files',
34
+ positioning: false
35
+ });
36
+ });
37
+
38
+ this.tab('vr_fallback', function() {
39
+ this.input('preview_vr_fallback', pageflow.CheckBoxInputView);
40
+
41
+ this.group('background', {
42
+ propertyNamePrefix: 'fallback'
43
+ });
44
+
45
+ this.input('fallback_text', pageflow.TextAreaInputView, {
46
+ size: 'short'
47
+ });
48
+ this.input('fallback_you_tube_url', pageflow.UrlInputView, {
49
+ permitHttps: true,
50
+ displayPropertyName: 'display_fallback_you_tube_url',
51
+ supportedHosts: [
52
+ 'http://youtube.com',
53
+ 'https://youtube.com'
54
+ ]
55
+ });
56
+ this.view(pageflow.vr.FallbackPreviewModeView, {
57
+ model: this.model
58
+ });
59
+ });
60
+
61
+ this.tab('options', function() {
62
+ this.group('options');
63
+ });
64
+ }
65
+ });
@@ -0,0 +1,9 @@
1
+ pageflow.vr.FallbackPreviewModeView = Backbone.Marionette.View.extend({
2
+ render: function() {
3
+ return this;
4
+ },
5
+
6
+ onClose: function() {
7
+ this.model.unset('preview_vr_fallback');
8
+ }
9
+ });
@@ -0,0 +1,73 @@
1
+ //= require_tree ./editor/
2
+ //= require_self
3
+
4
+ pageflow.vr = pageflow.vr || {};
5
+
6
+ pageflow.editor.pageTypes.register('vr', {
7
+ configurationEditorView: pageflow.vr.ConfigurationEditorView,
8
+ });
9
+
10
+ _(['video_files', 'image_files']).each(function(collectionName) {
11
+ pageflow.editor.fileTypes.modify(collectionName, {
12
+ configurationEditorInputs: function(model) {
13
+ var values = ['equirectangular_mono', 'equirectangular_stereo'];
14
+ var valuesWithAutoDetect = ['auto_detect'].concat(values);
15
+
16
+ var options = {
17
+ includeBlank: true,
18
+ values: values
19
+ };
20
+
21
+ if (collectionName == 'video_files') {
22
+ if (model.isNew()) {
23
+ options = {
24
+ includeBlank: true,
25
+ ensureValueDefined: true,
26
+ values: valuesWithAutoDetect
27
+ };
28
+ }
29
+ else if (!model.isReady() && model.configuration.get('projection') == 'auto_detect') {
30
+ options = {
31
+ includeBlank: true,
32
+ disabled: true,
33
+ values: valuesWithAutoDetect
34
+ };
35
+ }
36
+ }
37
+
38
+ return [
39
+ {
40
+ name: 'projection',
41
+ inputView: pageflow.SelectInputView,
42
+ inputViewOptions: options
43
+ }
44
+ ];
45
+ },
46
+
47
+ configurationUpdaters: [
48
+ function(configuration, newAttributes) {
49
+ if (configuration.get('projection') == 'auto_detect' &&
50
+ newAttributes.projection != 'auto_detect') {
51
+ configuration.set('projection', newAttributes.projection);
52
+ }
53
+ }
54
+ ],
55
+
56
+ confirmUploadTableColumns: [
57
+ {
58
+ name: 'projection',
59
+ cellView: pageflow.EnumTableCellView
60
+ }
61
+ ],
62
+
63
+ filters: [
64
+ {
65
+ name: 'with_projection',
66
+ matches: function(file) {
67
+ return !!file.configuration.get('projection') &&
68
+ file.configuration.get('projection') != 'auto_detect';
69
+ }
70
+ }
71
+ ]
72
+ });
73
+ });
@@ -0,0 +1,2 @@
1
+ pageflow.pageType.register('vr',
2
+ pageflow.react.createPageType(pageflow.vr.Page));
@@ -0,0 +1,36 @@
1
+ pageflow.vr.Player.mediaEvents = function(player, context) {
2
+ function triggerMediaEvent(name, event) {
3
+ pageflow.events.trigger('media:' + name, {
4
+ fileName: event.currentSrc,
5
+ context: context,
6
+ currentTime: event.currentTime,
7
+ duration: event.duration,
8
+ volume: player.volume(),
9
+ bitrate: getBitrate(event.currentSrc)
10
+ });
11
+ }
12
+
13
+ player.on('play', function(event) {
14
+ triggerMediaEvent('play', event);
15
+ });
16
+
17
+ player.on('timeupdate', function(event) {
18
+ triggerMediaEvent('timeupdate', event);
19
+ });
20
+
21
+ player.on('pause', function(event) {
22
+ triggerMediaEvent('pause', event);
23
+ });
24
+
25
+ function getBitrate(src) {
26
+ if (src.match(/4k/)) {
27
+ return 22000000;
28
+ }
29
+ else if (src.match(/fullhd/)) {
30
+ return 6000000;
31
+ }
32
+ else {
33
+ return 3500000;
34
+ }
35
+ }
36
+ };
@@ -0,0 +1,93 @@
1
+ //= require_self
2
+ //= require ./player/media_events
3
+
4
+ pageflow.vr.Player = pageflow.Object.extend({
5
+ initialize: function(iframe) {
6
+ this.iframe = iframe;
7
+ this.cachedVolume = 1;
8
+ this.cachedPaused = iframe.src.indexOf('no_autoplay=true') >= 0;
9
+ this.id = getIdParam(iframe.src);
10
+
11
+ this.messageListener = this.onMessage.bind(this);
12
+
13
+ if (this.id) {
14
+ window.addEventListener('message', this.messageListener);
15
+ }
16
+ },
17
+
18
+ dispose: function() {
19
+ this.off(null, null, null);
20
+ window.removeEventListener('message', this.messageListener);
21
+ },
22
+
23
+ play: function() {
24
+ this.cachedPaused = false;
25
+ this.sendCommand('play');
26
+ },
27
+
28
+ pause: function() {
29
+ this.cachedPaused = true;
30
+ this.sendCommand('pause');
31
+ },
32
+
33
+ paused: function() {
34
+ return this.cachedPaused;
35
+ },
36
+
37
+ volume: function(value) {
38
+ if (typeof value === 'undefined') {
39
+ return this.cachedVolume;
40
+ }
41
+ else {
42
+ this.cachedVolume = value;
43
+ this.sendCommand('volume', Math.max(0, Math.min(1, value)));
44
+ }
45
+ },
46
+
47
+ enterVRMode: function() {
48
+ this.sendCommand('enterVRMode');
49
+ },
50
+
51
+ sendCommand: function(name, value) {
52
+ this.iframe.contentWindow.postMessage({
53
+ type: 'PlayerCommand',
54
+ command: name,
55
+ value: value
56
+ }, '*');
57
+ },
58
+
59
+ onMessage: function(event) {
60
+ var message = event.data;
61
+
62
+ if (message.type == 'VrViewEvent' &&
63
+ message.vrViewId == this.id) {
64
+
65
+ this.trigger(message.event, message.eventData);
66
+ }
67
+ },
68
+
69
+ one: function() {
70
+ this.once.apply(this, arguments);
71
+ }
72
+ });
73
+
74
+ function getIdParam(url) {
75
+ var result = url.match(/id=(\d+)/);
76
+ return result && result[1];
77
+ }
78
+
79
+ pageflow.vr.Player.create = function(iframe, options) {
80
+ if (!iframe) {
81
+ return null;
82
+ }
83
+
84
+ var player = new pageflow.vr.Player(iframe);
85
+
86
+ pageflow.mediaPlayer.enhance(player, {
87
+ volumeFading: true
88
+ });
89
+
90
+ pageflow.vr.Player.mediaEvents(player, options.context);
91
+
92
+ return player;
93
+ };
@@ -0,0 +1,7 @@
1
+ //= require_self
2
+
3
+ //= require_tree ./vr/browser
4
+ //= require ./vr/components
5
+ //= require ./vr/player
6
+
7
+ pageflow.vr = pageflow.vr || {};
@@ -0,0 +1,85 @@
1
+ @include pageflow-page-type("vr");
2
+ @include pageflow-page-type-pictograms("vr");
3
+
4
+ $vr-loading-indicator-color: #fff !default;
5
+
6
+ $vr-no-vr-background-color: rgba(17, 17, 17, 0.9) !default;
7
+
8
+ $vr-no-vr-icon-color: #555 !default;
9
+
10
+ .pageflow_vr-vr_view {
11
+ pointer-events: all;
12
+ position: absolute;
13
+ height: 100%;
14
+ width: 100%;
15
+ }
16
+
17
+ .pageflow_vr-vr_view-frame {
18
+ position: absolute;
19
+ top: 0;
20
+ left: 0;
21
+ height: 100%;
22
+ width: 100%;;
23
+ }
24
+
25
+ .pageflow_vr-page_loading_indicator {
26
+ position: absolute;
27
+ top: 50%;
28
+ left: 50%;
29
+ @include transform(translate(-50%, -50%));
30
+ @include transition(opacity 0.5s, visibility 0.5s);
31
+ }
32
+
33
+ .pageflow_vr-page_loading_indicator_icon {
34
+ display: block;
35
+ width: 200px;
36
+ height: 200px;
37
+ fill: $vr-loading-indicator-color;
38
+ @include animation(pageflow_vr-blink 3s infinite ease);
39
+ }
40
+
41
+ .pageflow_vr-page_loading_indicator-hidden {
42
+ opacity: 0;
43
+ visibility: hidden;
44
+ pointer-events: none;
45
+ }
46
+
47
+ .pageflow_vr-no_vr_view {
48
+ margin: 5em auto 0 auto;
49
+ padding: 20px;
50
+ background-color: $vr-no-vr-background-color;
51
+ max-width: 500px;
52
+ border-radius: 2px;
53
+
54
+ a {
55
+ @extend %anchor;
56
+ color: $main-color;
57
+ pointer-events: all;
58
+ }
59
+
60
+ p {
61
+ width: auto;
62
+ }
63
+
64
+ svg {
65
+ width: 100px;
66
+ height: 100px;
67
+ fill: $vr-no-vr-icon-color;
68
+ margin: 0 auto 20px auto;
69
+ display: block;
70
+ }
71
+ }
72
+
73
+ @include keyframes(pageflow_vr-blink) {
74
+ 0% {
75
+ opacity: 0.7;
76
+ }
77
+
78
+ 50% {
79
+ opacity: 0.3;
80
+ }
81
+
82
+ 100% {
83
+ opacity: 0.7;
84
+ }
85
+ }
@@ -0,0 +1,13 @@
1
+ module Pageflow
2
+ module Vr
3
+ class StaticFilesController < ActionController::Base
4
+ def vrview
5
+ respond_to do |format|
6
+ format.html
7
+ format.css
8
+ format.js
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ html, body {
2
+ background-color: #000;
3
+ color: #eee;
4
+ margin: 0px;
5
+ padding: 0px;
6
+ position: fixed;
7
+ overflow: hidden;
8
+ top: 0;
9
+ bottom: 0;
10
+ left: 0;
11
+ right: 0;
12
+ }
13
+ .dialog {
14
+ display: none;
15
+ align-items: center;
16
+ justify-content: center;
17
+ font-family: sans-serif;
18
+ font-size: 170%;
19
+ position: absolute;
20
+ width: 100%;
21
+ height: 100%;
22
+ background: rgba(255, 255, 255, 0.2);
23
+ -webkit-user-select: none;
24
+ -moz-user-select: none;
25
+ user-select: none;
26
+ }
27
+ .dialog.visible {
28
+ display: flex;
29
+ }
30
+ .dialog .wrap {
31
+ padding: 30px 60px;
32
+ margin: 20px auto;
33
+ width: 60%;
34
+ background: rgba(0, 0, 0, 0.8);
35
+ border-radius: 5px;
36
+ }
37
+ .dialog h1 {
38
+ margin: 0;
39
+ }
40
+
41
+ a, a:visited {
42
+ color: skyblue;
43
+ }
44
+
45
+ canvas {
46
+ cursor: -webkit-grab;
47
+ }
48
+ canvas:active {
49
+ cursor: -webkit-grabbing;
50
+ }
51
+
52
+ .webvr-button:first-of-type,
53
+ .webvr-button:nth-of-type(2) {
54
+ display: none !important;
55
+ }
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>VR</title>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7
+ <meta name="mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-capable" content="yes" />
9
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
10
+ <link rel="stylesheet" href="/pageflow_vr/vrview.css">
11
+ </head>
12
+ <body>
13
+ <div id="error" class="dialog">
14
+ <div class="wrap">
15
+ <h1 class="title">Error</h1>
16
+ <p class="message">An unknown error occurred.</p>
17
+ </div>
18
+ </div>
19
+
20
+ <script src="/pageflow_vr/vrview.js"></script>
21
+ </body>
22
+ </html>