pageflow-linkmap-page 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.jshintignore +1 -0
  4. data/.jshintrc +23 -0
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +18 -0
  7. data/README.md +62 -0
  8. data/Rakefile +11 -0
  9. data/app/assets/images/pageflow/linkmap_page/text_only_area_type_pictogram.png +0 -0
  10. data/app/assets/images/pageflow/linkmap_page/themes/default/pictograms/sprite.png +0 -0
  11. data/app/assets/images/pageflow/linkmap_page/themes/default/pictograms/wide.png +0 -0
  12. data/app/assets/images/pageflow/linkmap_page_pictogram.png +0 -0
  13. data/app/assets/images/pageflow/linkmap_page_pictogram_small.png +0 -0
  14. data/app/assets/javascript/pageflow/linkmap_page.js +9 -0
  15. data/app/assets/javascript/pageflow/linkmap_page/editor.js +25 -0
  16. data/app/assets/javascript/pageflow/linkmap_page/editor/collections/areas_collection.js +57 -0
  17. data/app/assets/javascript/pageflow/linkmap_page/editor/collections/page_links_collection.js +29 -0
  18. data/app/assets/javascript/pageflow/linkmap_page/editor/config.js +51 -0
  19. data/app/assets/javascript/pageflow/linkmap_page/editor/controllers/side_bar_controller.js +15 -0
  20. data/app/assets/javascript/pageflow/linkmap_page/editor/models/area.js +79 -0
  21. data/app/assets/javascript/pageflow/linkmap_page/editor/models/area_file_selection_handler.js +12 -0
  22. data/app/assets/javascript/pageflow/linkmap_page/editor/models/audio_file_area_type.js +17 -0
  23. data/app/assets/javascript/pageflow/linkmap_page/editor/models/external_link_area_type.js +19 -0
  24. data/app/assets/javascript/pageflow/linkmap_page/editor/models/new_area_file_selection_handler.js +11 -0
  25. data/app/assets/javascript/pageflow/linkmap_page/editor/models/page_configuration_mixin.js +33 -0
  26. data/app/assets/javascript/pageflow/linkmap_page/editor/models/page_link_area_type.js +29 -0
  27. data/app/assets/javascript/pageflow/linkmap_page/editor/models/text_only_area_type.js +15 -0
  28. data/app/assets/javascript/pageflow/linkmap_page/editor/routers/side_bar_router.js +5 -0
  29. data/app/assets/javascript/pageflow/linkmap_page/editor/templates/edit_area.jst.ejs +3 -0
  30. data/app/assets/javascript/pageflow/linkmap_page/editor/templates/embedded/area_item.jst.ejs +11 -0
  31. data/app/assets/javascript/pageflow/linkmap_page/editor/views/areas_list_view.js +44 -0
  32. data/app/assets/javascript/pageflow/linkmap_page/editor/views/configuration_editor_view.js +90 -0
  33. data/app/assets/javascript/pageflow/linkmap_page/editor/views/edit_area_view.js +103 -0
  34. data/app/assets/javascript/pageflow/linkmap_page/editor/views/editable_areas_mode_view.js +10 -0
  35. data/app/assets/javascript/pageflow/linkmap_page/editor/views/embedded/area_item_embedded_view.js +190 -0
  36. data/app/assets/javascript/pageflow/linkmap_page/editor/views/embedded/areas_embedded_view.js +26 -0
  37. data/app/assets/javascript/pageflow/linkmap_page/editor/views/embedded/panorama_embedded_view.js +14 -0
  38. data/app/assets/javascript/pageflow/linkmap_page/features.js +9 -0
  39. data/app/assets/javascript/pageflow/linkmap_page/page_type.js +302 -0
  40. data/app/assets/javascript/pageflow/linkmap_page/vendor/gyro.js +3 -0
  41. data/app/assets/javascript/pageflow/linkmap_page/widgets/hover_video.js +77 -0
  42. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap.js +112 -0
  43. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap_audio_player_controls.js +132 -0
  44. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap_audio_players_controller.js +48 -0
  45. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap_lookaround.js +154 -0
  46. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap_lookaround_strategies/target_speed.js +99 -0
  47. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap_panorama.js +421 -0
  48. data/app/assets/javascript/pageflow/linkmap_page/widgets/linkmap_scroll_indicators.js +17 -0
  49. data/app/assets/stylesheets/pageflow/linkmap_page.css.scss +266 -0
  50. data/app/assets/stylesheets/pageflow/linkmap_page/animations/rotate.css.scss +8 -0
  51. data/app/assets/stylesheets/pageflow/linkmap_page/audio_player_controls.scss +134 -0
  52. data/app/assets/stylesheets/pageflow/linkmap_page/editor.css.scss +122 -0
  53. data/app/assets/stylesheets/pageflow/linkmap_page/editor/areas_list.scss +15 -0
  54. data/app/assets/stylesheets/pageflow/linkmap_page/editor/file_areas.css.scss +70 -0
  55. data/app/assets/stylesheets/pageflow/linkmap_page/editor/resizable.css.scss +80 -0
  56. data/app/assets/stylesheets/pageflow/linkmap_page/themes/default.css.scss +341 -0
  57. data/app/assets/stylesheets/pageflow/linkmap_page/themes/default/scroll_indicators.scss +82 -0
  58. data/app/controllers/pageflow/external_links/sites_controller.rb +40 -0
  59. data/app/helpers/pageflow/linkmap_page/areas_helper.rb +93 -0
  60. data/app/models/pageflow/linkmap_page/site.rb +11 -0
  61. data/app/views/pageflow/linkmap_page/areas/_div.html.erb +33 -0
  62. data/app/views/pageflow/linkmap_page/page.html +51 -0
  63. data/config/locales/de.yml +119 -0
  64. data/config/locales/en.yml +119 -0
  65. data/config/locales/new/inverted.de.yml +10 -0
  66. data/config/locales/new/inverted.en.yml +10 -0
  67. data/config/locales/new/text_only_area_type.de.yml +11 -0
  68. data/config/locales/new/text_only_area_type.en.yml +11 -0
  69. data/config/routes.rb +5 -0
  70. data/config/spring.rb +1 -0
  71. data/exec/rspec +15 -0
  72. data/exec/spring +18 -0
  73. data/lib/pageflow-linkmap-page.rb +13 -0
  74. data/lib/pageflow/linkmap_page/engine.rb +17 -0
  75. data/lib/pageflow/linkmap_page/page_type.rb +19 -0
  76. data/lib/pageflow/linkmap_page/plugin.rb +9 -0
  77. data/lib/pageflow/linkmap_page/version.rb +5 -0
  78. data/pageflow-linkmap-page.gemspec +37 -0
  79. data/spec/helpers/pageflow/linkmap_page/areas_helper_spec.rb +68 -0
  80. data/spec/spec_helper.rb +15 -0
  81. metadata +265 -0
@@ -0,0 +1,132 @@
1
+ (function($) {
2
+ $.widget('pageflow.linkmapAudioPlayerControls', {
3
+ _create: function() {
4
+ var widget = this;
5
+ var that = this;
6
+
7
+ this.wrapper = $('<div />')
8
+ .addClass('linkmap_audio_player_controls')
9
+ .appendTo(this.element.find('.linkmap_marker'));
10
+
11
+ this.playButton = $('<div />')
12
+ .addClass('play')
13
+ .appendTo(this.wrapper)
14
+ ;
15
+
16
+ this.pauseButton = $('<div />')
17
+ .addClass('pause')
18
+ .appendTo(this.wrapper)
19
+ ;
20
+
21
+ this.progressElement = $('<div />')
22
+ .addClass('progress')
23
+ .appendTo(this.wrapper)
24
+ ;
25
+
26
+ this.progressElement.html('<div class="audio_inline_progress"><div class="progress_inner"><div class="left_circle"><div class="circle_inner"><div class="circle_innerst"></div></div></div><div class="right_circle"><div class="circle_inner"><div class="circle_innerst"></div></div></div><div class="audio_inline_loading_spinner"><div class="circle_inner"><div class="circle_innerst"></div></div></div></div></div>');
27
+
28
+ this.currentTimeElement = $('<div />')
29
+ .addClass('current_time')
30
+ .appendTo(this.wrapper)
31
+ ;
32
+
33
+ this.durationElement = $('<div />')
34
+ .addClass('duration')
35
+ .appendTo(this.wrapper)
36
+ ;
37
+
38
+ this.playButton.on('click', function(e) {
39
+ widget.element.addClass('loading');
40
+ widget._trigger('play', null, {
41
+ audioFileId: widget.element.data('audioFile')
42
+ });
43
+ e.preventDefault();
44
+ e.stopPropagation();
45
+ });
46
+
47
+ this.pauseButton.on('click', function(e) {
48
+ widget._trigger('pause', null, {
49
+ audioFileId: widget.element.data('audioFile')
50
+ });
51
+
52
+ e.preventDefault();
53
+ e.stopPropagation();
54
+ });
55
+
56
+ this.element.on('click', function() {
57
+ if (!widget.element.hasClass('dynamic_marker')) {
58
+ widget._trigger('play', null, {
59
+ audioFileId: widget.element.data('audioFile')
60
+ });
61
+ }
62
+ });
63
+
64
+ this.progressElement.on('click', function(e) {
65
+
66
+ var elementCenter = {
67
+ x: $(this).offset().left + $(this).width() / 2,
68
+ y: $(this).offset().top + $(this).height() / 2,
69
+ };
70
+
71
+ var angleDeg = Math.atan2(e.pageY - elementCenter.y, e.pageX - elementCenter.x) * 180 / Math.PI;
72
+ var circlePercentage = (angleDeg + 90) / 360;
73
+
74
+ circlePercentage = circlePercentage < 0 ? 1 + circlePercentage : circlePercentage;
75
+
76
+ widget._trigger('seek', null, {
77
+ positionInPercent: circlePercentage,
78
+ audioFileId: widget.element.data('audioFile')
79
+ });
80
+
81
+ e.stopPropagation();
82
+ });
83
+ },
84
+
85
+ drawCircle: function(percentage) {
86
+ var rightCircle = this.element.find('.right_circle .circle_inner');
87
+ var leftCircle = this.element.find('.left_circle .circle_inner');
88
+ var rightCircleInner = this.element.find('.right_circle .circle_innerst');
89
+ var leftCircleInner = this.element.find('.left_circle .circle_innerst');
90
+
91
+
92
+
93
+ var rotationRight = Math.min(percentage/100 * 360, 180);
94
+ var rotationLeft = Math.max(percentage/100 * 360 - 180, 0);
95
+ var rotation;
96
+
97
+ rotation = rotationRight;
98
+ rightCircle.attr("style", "-webkit-transform: rotate(" + rotation + "deg); -moz-transform: rotate(" + rotation + "deg); -ms-transform: rotate(" + rotation + "deg); -o-transform: rotate(" + rotation + "deg); transform: rotate(" + rotation + "deg)");
99
+ rightCircleInner.attr("style", "-webkit-transform: rotate(" + -rotation + "deg); -moz-transform: rotate(" + -rotation + "deg); -ms-transform: rotate(" + -rotation + "deg); -o-transform: rotate(" + -rotation + "deg); transform: rotate(" + -rotation + "deg)");
100
+
101
+ rotation = rotationLeft;
102
+ leftCircle.attr("style", "-webkit-transform: rotate(" + rotation + "deg); -moz-transform: rotate(" + rotation + "deg); -ms-transform: rotate(" + rotation + "deg); -o-transform: rotate(" + rotation + "deg); transform: rotate(" + rotation + "deg)");
103
+ leftCircleInner.attr("style", "-webkit-transform: rotate(" + -rotation + "deg); -moz-transform: rotate(" + -rotation + "deg); -ms-transform: rotate(" + -rotation + "deg); -o-transform: rotate(" + -rotation + "deg); transform: rotate(" + -rotation + "deg)");
104
+ },
105
+
106
+ playing: function() {
107
+ this.element.addClass('loading');
108
+ },
109
+
110
+ notPlaying: function() {
111
+ this.element.removeClass('playing loading');
112
+ },
113
+
114
+ cancelLoading: function() {
115
+ this.element.removeClass('loading');
116
+ },
117
+
118
+ updateProgress: function(player) {
119
+ this.element.removeClass('loading');
120
+ this.element.addClass('playing');
121
+
122
+ this.currentTimeElement.text(player.formatTime(player.position()));
123
+ this.durationElement.text(player.formatTime(player.duration()));
124
+
125
+ var percent = player.duration() > 0 ? player.position() / player.duration() * 100 : 0;
126
+ this.drawCircle(percent);
127
+ },
128
+
129
+ refresh: function() {
130
+ }
131
+ });
132
+ }(jQuery));
@@ -0,0 +1,48 @@
1
+ (function($) {
2
+ $.widget('pageflow.linkmapAudioPlayersController', {
3
+ _create: function() {
4
+ var player = this.options.player;
5
+ var element = this.element;
6
+
7
+ this.delegatePlayerEvent('play', 'playing');
8
+ this.delegatePlayerEvent('pause ended', 'notPlaying');
9
+ this.delegatePlayerEvent('timeupdate', 'updateProgress');
10
+
11
+ player.on('play', function(options) {
12
+ var playerElements = element.find('[data-target-type="audio_file"]');
13
+
14
+ playerElements.each(function() {
15
+ var playerElement = $(this);
16
+
17
+ if (playerElement.data('audioFile') !== options.audioFileId) {
18
+ playerElement.linkmapAudioPlayerControls('cancelLoading');
19
+ }
20
+ });
21
+ });
22
+
23
+ this.element.on('linkmapaudioplayercontrolsplay', function(event, options) {
24
+ player.play(options.audioFileId);
25
+ });
26
+
27
+ this.element.on('linkmapaudioplayercontrolspause', function(event, options) {
28
+ player.pause();
29
+ });
30
+
31
+ this.element.on('linkmapaudioplayercontrolsseek', function(event, options) {
32
+ player.seek(player.duration() * options.positionInPercent);
33
+ });
34
+ },
35
+
36
+ delegatePlayerEvent: function (event, method) {
37
+ var element = this.element;
38
+ var player = this.options.player;
39
+
40
+ player.on(event, function(options) {
41
+ var selector = '[data-audio-file="' + options.audioFileId + '"]';
42
+ var playerElement = element.find(selector);
43
+
44
+ playerElement.linkmapAudioPlayerControls(method, player);
45
+ });
46
+ }
47
+ });
48
+ }(jQuery));
@@ -0,0 +1,154 @@
1
+ (function($) {
2
+ $.widget('pageflow.linkmapLookaround', {
3
+ speedUp : 10,
4
+ drag : false,
5
+
6
+ _create: function() {
7
+ this.disabled = false;
8
+ this.marginScrolling = !this.options.marginScrollingDisabled;
9
+
10
+ var scroller = this.scroller = this.options.scroller;
11
+ var that = this;
12
+
13
+ this.scrollStrategyX = new pageflow.linkmapPage.TargetSpeedStrategy({
14
+ scrollerPosition: function() {
15
+ return scroller.positionX();
16
+ },
17
+
18
+ scrollerMax: function() {
19
+ return scroller.maxX();
20
+ }
21
+ }, 'x');
22
+
23
+ this.scrollStrategyY = new pageflow.linkmapPage.TargetSpeedStrategy({
24
+ scrollerPosition: function() {
25
+ return scroller.positionY();
26
+ },
27
+
28
+ scrollerMax: function() {
29
+ return scroller.maxY();
30
+ }
31
+ }, 'y');
32
+
33
+ this.element.on('mousedown touchstart', function (event) {
34
+ that.drag = true;
35
+
36
+ $(window).one('mouseup touchend', function() {
37
+ that.drag = false;
38
+ that.initialBeta = null;
39
+ that.initialGamma = null;
40
+ });
41
+ });
42
+
43
+ this.element.on('mousedown', function (event) {
44
+ // prevent dragging
45
+ event.originalEvent.preventDefault();
46
+ });
47
+
48
+ this.element.on('mouseenter', function() {
49
+ that.scrollStrategyX.reset();
50
+ that.scrollStrategyY.reset();
51
+ });
52
+
53
+ this.element.on('mouseleave', function() {
54
+ that.scrollStrategyX.reset();
55
+ that.scrollStrategyY.reset();
56
+ });
57
+
58
+ $(window).on('orientationchange', function( event ) {
59
+ that.drag = false;
60
+ that.initialBeta = null;
61
+ that.initialGamma = null;
62
+ });
63
+
64
+ this.element.on('mousemove', function(e) {
65
+ var containerWidth = that.element.width();
66
+ var containerHeight = that.element.height();
67
+
68
+ that.scrollStrategyX.updateMouse(e.pageX / containerWidth);
69
+ that.scrollStrategyY.updateMouse(e.pageY / containerHeight);
70
+ });
71
+ },
72
+
73
+ enable: function() {
74
+ this.disabled = false;
75
+ },
76
+
77
+ disable: function() {
78
+ this.disabled = true;
79
+ },
80
+
81
+ activate: function() {
82
+ this.startInterval();
83
+
84
+ if (pageflow.browser.has('mobile platform')) {
85
+ this.startGyro();
86
+ }
87
+ },
88
+
89
+ deactivate: function() {
90
+ this.stopInterval();
91
+
92
+ if (pageflow.browser.has('mobile platform')) {
93
+ this.stopGyro();
94
+ }
95
+ },
96
+
97
+ startInterval: function() {
98
+ if (!this.interval) {
99
+ this.interval = setInterval(_.bind(this.onTick, this), 25);
100
+ }
101
+ },
102
+
103
+ stopInterval: function() {
104
+ if (this.interval) {
105
+ clearInterval(this.interval);
106
+ this.interval = null;
107
+ }
108
+ },
109
+
110
+ onTick: function() {
111
+ if (!this.drag && this.marginScrolling && !this.disabled) {
112
+ this.scroller.scrollBy(this.scrollStrategyX.getNextScrollDelta(),
113
+ this.scrollStrategyY.getNextScrollDelta());
114
+ }
115
+ },
116
+
117
+ update: function(marginScrollingDisabled) {
118
+ this.marginScrolling = !marginScrollingDisabled;
119
+ },
120
+
121
+ startGyro: function() {
122
+ var that = this;
123
+ var limit = 8;
124
+
125
+ gyro.startTracking(function(o) {
126
+ if (!that.initialBeta) {
127
+ that.initialBeta = o.beta;
128
+ }
129
+
130
+ if (!that.initialGamma) {
131
+ that.initialGamma = o.gamma;
132
+ }
133
+
134
+ if (!this.drag) {
135
+ var deltaGamma = o.gamma - that.initialGamma;
136
+ var deltaBeta = o.beta - that.initialBeta;
137
+
138
+ if ($(window).height() >= $(window).width()) {
139
+ that.scrollStrategyX.updateGyro(deltaGamma);
140
+ that.scrollStrategyY.updateGyro(-deltaBeta);
141
+ }
142
+ else {
143
+ that.scrollStrategyX.updateGyro(-deltaBeta);
144
+ that.scrollStrategyY.updateGyro(deltaGamma);
145
+ }
146
+ }
147
+ });
148
+ },
149
+
150
+ stopGyro: function() {
151
+ gyro.stopTracking();
152
+ }
153
+ });
154
+ }(jQuery));
@@ -0,0 +1,99 @@
1
+ pageflow.linkmapPage.TargetSpeedStrategy = function(options, name) {
2
+ var speedUp = 10;
3
+
4
+ var targetSpeed = 0;
5
+ var speed = 0;
6
+ var realSpeed = 0;
7
+
8
+ var stopFactor = 1;
9
+ var isStopping = false;
10
+ var wasStopping = false;
11
+
12
+ var activeMargin = 0.4;
13
+
14
+ var scrollerPosition = options.scrollerPosition;
15
+ var scrollerMax = options.scrollerMax;
16
+
17
+ this.reset = function() {
18
+ targetSpeed = 0;
19
+ };
20
+
21
+ this.updateGyro = function(delta) {
22
+ this.updateTargetSpeed(Math.abs(delta) < 8 ? 0 : delta / 90);
23
+ };
24
+
25
+ this.updateMouse = function(fraction) {
26
+ var delta = fraction * 2 - 1;
27
+ var onOuterAreas = Math.abs(delta) > 1 - activeMargin;
28
+
29
+ this.updateTargetSpeed(onOuterAreas ?
30
+ (delta - ((1 - activeMargin) * sign(delta))) / activeMargin :
31
+ 0);
32
+ };
33
+
34
+ this.updateTargetSpeed = function(value) {
35
+ targetSpeed = value;
36
+ };
37
+
38
+ this.getNextScrollDelta = function() {
39
+ convergeSpeed();
40
+ updateStopFactor();
41
+ updateSpeedIfStoppingAborted();
42
+
43
+ realSpeed = speed * stopFactor;
44
+
45
+ return -realSpeed * speedUp;
46
+ };
47
+
48
+ var i = 0;
49
+
50
+ function convergeSpeed() {
51
+ if (Math.abs(speed - targetSpeed) > 0.01) {
52
+ var d = Math.abs(targetSpeed) > Math.abs(speed) ? 0.02 : 0.04;
53
+ speed = limit(speed + d * sign(targetSpeed - speed), targetSpeed);
54
+ }
55
+ else {
56
+ speed = targetSpeed;
57
+ }
58
+ }
59
+
60
+ function limit(speed, targetSpeed) {
61
+ if (speed > targetSpeed) {
62
+ return Math.max(speed, targetSpeed);
63
+ }
64
+ else {
65
+ return Math.min(speed, targetSpeed);
66
+ }
67
+ }
68
+
69
+ function updateStopFactor() {
70
+ isStopping = false;
71
+ stopFactor = 1;
72
+
73
+ if (scrollerPosition() < scrollerMax() * 0.8 && targetSpeed >= 0) {
74
+ stopFactor = (scrollerMax() - scrollerPosition()) / (scrollerMax() * 0.2);
75
+ isStopping = true;
76
+ }
77
+
78
+ if (scrollerPosition() > scrollerMax() * 0.2 && targetSpeed <= 0) {
79
+ stopFactor = scrollerPosition() / (scrollerMax() * 0.2);
80
+ isStopping = true;
81
+ }
82
+
83
+ if (stopFactor > 1) {
84
+ stopFactor = stopFactor;
85
+ }
86
+ }
87
+
88
+ function updateSpeedIfStoppingAborted() {
89
+ if (wasStopping && !isStopping) {
90
+ speed = realSpeed;
91
+ }
92
+
93
+ wasStopping = isStopping;
94
+ }
95
+
96
+ function sign(value) {
97
+ return value > 0 ? 1 : -1;
98
+ }
99
+ };
@@ -0,0 +1,421 @@
1
+ (function($) {
2
+ $.widget('pageflow.linkmapPanorama', {
3
+ scrollHoverMargin : 0.2,
4
+ environmentMargin : 0.2,
5
+ minScaling: true,
6
+ minScalingSize: 80,
7
+ lastMouseMoveEvent: null,
8
+
9
+ _create: function() {
10
+ var that = this,
11
+ pageElement = this.options.page;
12
+
13
+ this.addEnvironment = this.options.addEnvironment;
14
+ this.panorama = this.options.panorama();
15
+ this.limitScrolling = this.options.limitScrolling;
16
+ this.scroller = this.options.scroller;
17
+
18
+ this.activeAreas = pageElement.find(this.options.activeAreasSelector);
19
+ this.panoramaWrapper = pageElement.find('.panorama_wrapper');
20
+ this.innerScrollerElement = pageElement.find('.linkmap');
21
+ this.overlayBox = pageElement.find('.description_overlay');
22
+ this.overlayInnerBox = pageElement.find('.description_overlay_wrapper');
23
+ this.overlayTitle = pageElement.find('.description_overlay .link_title');
24
+ this.overlayDescription = pageElement.find('.description_overlay .link_description');
25
+
26
+ this.startScrollPosition = _.clone(this.options.startScrollPosition);
27
+
28
+ this.currentScrollPosition = null;
29
+
30
+ this.refresh();
31
+
32
+ this.scroller.onScrollEnd(function() {
33
+ that.updateScrollPosition();
34
+ });
35
+
36
+ $(window).on('resize', function () {
37
+ that.centerToPoint(null, 0);
38
+ });
39
+
40
+ this.element.on('mousemove', function(e) {
41
+ that.lastMouseMoveEvent = e;
42
+ that.calcAreaOpacity(that.activeAreas, e.pageX, e.pageY);
43
+ });
44
+
45
+ pageElement.on('mouseenter', '.hover_area', function() {
46
+ positionOverlay($(this));
47
+ });
48
+
49
+ pageElement.on('click', function() {
50
+ that.overlayBox.removeClass('active');
51
+ that.activeAreas.removeClass('hover');
52
+ });
53
+
54
+ $('body').on('mouseleave', '.hover_area', function() {
55
+ that.overlayBox.removeClass('active');
56
+ });
57
+
58
+ pageElement.on('dragstart resizestart', '.hover_area', function() {
59
+ that.overlayBox.removeClass('active');
60
+ });
61
+
62
+ that.activeAreas.each(function() {
63
+ $(this).on('click touchstart', function(e) {
64
+ if (pageflow.browser.has('mobile platform')) {
65
+ if($(this).hasClass('hover')) {
66
+ that.activeAreas.removeClass('active');
67
+ $(this).addClass('active');
68
+ return;
69
+ }
70
+ that.activeAreas.removeClass('hover');
71
+ $(this).addClass('hover');
72
+ positionOverlay($(this));
73
+ return false;
74
+ }
75
+ else {
76
+ that.activeAreas.removeClass('active');
77
+ $(this).addClass('active');
78
+ }
79
+ })
80
+ });
81
+
82
+ var positionOverlay = function(area) {
83
+ if (area.is('.editing')) {
84
+ return;
85
+ }
86
+
87
+ var linkTitle = area.find('.link_title').html();
88
+ var linkDescription = area.find('.link_description').html();
89
+
90
+ if (linkTitle || linkDescription) {
91
+ that.overlayTitle.html(linkTitle);
92
+ that.overlayDescription.html(linkDescription);
93
+
94
+ that.overlayBox.addClass('active');
95
+
96
+
97
+
98
+ if(that.panorama.width() - (area.position().left + area.outerWidth()) < that.overlayBox.outerWidth()) {
99
+ var overlayAlignmentDirection = "left";
100
+ that.overlayBox.addClass('left_aligned');
101
+ }
102
+ else {
103
+ var spaceLeftOfArea = area.offset().left;
104
+ var spaceRightOfArea = $(window).width() - area.offset().left - area.outerWidth();
105
+
106
+ if(spaceLeftOfArea < spaceRightOfArea || spaceLeftOfArea < that.overlayBox.outerWidth()) {
107
+ var overlayAlignmentDirection = "right";
108
+ that.overlayBox.removeClass('left_aligned');
109
+ }
110
+ else {
111
+ var overlayAlignmentDirection = "left";
112
+ that.overlayBox.addClass('left_aligned');
113
+ }
114
+ }
115
+
116
+ if(overlayAlignmentDirection == "right") {
117
+ that.overlayBox.removeClass('left_aligned');
118
+ that.overlayBox.css({
119
+ 'left': area.position().left + area.width(),
120
+ 'top': area.position().top,
121
+ 'margin-top': area.height() / 2
122
+ });
123
+ }
124
+ if(overlayAlignmentDirection == "left") {
125
+ that.overlayBox.addClass('left_aligned');
126
+ that.overlayBox.css({
127
+ 'left': area.position().left - that.overlayBox.outerWidth(),
128
+ 'top': area.position().top,
129
+ 'margin-top': area.height() / 2
130
+ });
131
+ }
132
+
133
+ var spaceToBottom = that.panorama.height() - area.position().top;
134
+ var spaceToViewportBottom = $(window).height() - area.offset().top - area.height() / 2;
135
+ var spaceToViewportTop = $(window).height() - 50;
136
+ var minMargin = 40;
137
+
138
+ if(that.overlayBox.outerHeight() > spaceToBottom) {
139
+ that.overlayInnerBox.css('top', (spaceToBottom - that.overlayInnerBox.outerHeight() - minMargin - area.height() / 2) + 'px');
140
+ }
141
+ else {
142
+ that.overlayInnerBox.css('top', '0px');
143
+ }
144
+ var additionalMargin = 10;
145
+
146
+ if(spaceToViewportBottom < that.overlayBox.outerHeight() && that.overlayBox.outerHeight() + additionalMargin < spaceToViewportTop) {
147
+ additionalMargin = spaceToViewportBottom - that.overlayBox.outerHeight() - additionalMargin;
148
+ that.overlayInnerBox.css('top', additionalMargin + 'px');
149
+ }
150
+ else {
151
+ that.overlayInnerBox.css('top', '0px');
152
+ }
153
+ }
154
+ }
155
+
156
+ this.refresh();
157
+ },
158
+
159
+ calcAreaOpacity: function(activeAreas, mX, mY) {
160
+ if (pageflow.browser.has('mobile platform')) {
161
+ return;
162
+ }
163
+
164
+ var pageElement = this.options.page;
165
+ var distanceLimit = pageElement.width() > pageElement.height() ? pageElement.height() : pageElement.width();
166
+ var minOpacity = 0.4;
167
+ activeAreas.each(function() {
168
+ var distance = calculateDistance($(this), mX, mY);
169
+
170
+ if(distance <= distanceLimit) {
171
+ var opacity = 1 + minOpacity - Math.sqrt(distance / distanceLimit);
172
+ $(this).find('.linkmap_marker.no_transition').css('opacity', opacity);
173
+ }
174
+ else {
175
+ $(this).find('.linkmap_marker.no_transition').css('opacity', minOpacity);
176
+ }
177
+
178
+
179
+ });
180
+
181
+ function calculateDistance(elem, mouseX, mouseY) {
182
+ return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.offset().left+(elem.width()/2)), 2) + Math.pow(mouseY - (elem.offset().top+(elem.height()/2)), 2)));
183
+ }
184
+ },
185
+
186
+ highlightAreas: function() {
187
+ var element = this.element;
188
+ element.find('.linkmap_marker').addClass('teasing');
189
+
190
+ setTimeout(function() {
191
+ element.find('.linkmap_marker').removeClass('teasing');
192
+ }, 1000);
193
+
194
+ setTimeout(function() {
195
+ element.find('.linkmap_marker').addClass('no_transition');
196
+ }, 2000);
197
+ },
198
+
199
+ resetAreaHighlighting: function() {
200
+ var element = this.element;
201
+
202
+ element.find('.linkmap_marker').removeClass('no_transition teasing');
203
+ element.find('.linkmap_marker').css('opacity', pageflow.browser.has('mobile platform') ? '0.8' : '0.4');
204
+ },
205
+
206
+ getScrollArea: function(activeAreas) {
207
+ var panorama = this.panorama;
208
+ var pageElement = this.options.page;
209
+ var startScrollPosition = this.startScrollPosition;
210
+ var scrollArea;
211
+
212
+ if (activeAreas.length && this.limitScrolling) {
213
+ scrollArea = {
214
+ top: this.startScrollPosition.y * panorama.height(),
215
+ left: this.startScrollPosition.x * panorama.width(),
216
+ bottom: this.startScrollPosition.y * panorama.height(),
217
+ right: this.startScrollPosition.x * panorama.width(),
218
+ };
219
+
220
+ activeAreas.each(function() {
221
+ var el = $(this);
222
+
223
+ scrollArea.top = scrollArea.top > el.position().top ? el.position().top : scrollArea.top;
224
+ scrollArea.left = scrollArea.left > el.position().left ? el.position().left : scrollArea.left;
225
+ scrollArea.bottom = scrollArea.bottom < el.position().top + el.height() ? el.position().top + el.height() : scrollArea.bottom;
226
+ scrollArea.right = scrollArea.right < el.position().left + el.width() ? el.position().left + el.width() : scrollArea.right;
227
+ });
228
+
229
+ scrollArea.top = Math.max(0, scrollArea.top - pageElement.height() * this.scrollHoverMargin);
230
+ scrollArea.left = Math.max(0, scrollArea.left - pageElement.width() * this.scrollHoverMargin);
231
+ scrollArea.bottom = Math.min(panorama.height(), scrollArea.bottom + pageElement.height() * this.scrollHoverMargin);
232
+ scrollArea.right = Math.min(panorama.width(), scrollArea.right + pageElement.width() * this.scrollHoverMargin);
233
+ }
234
+ else {
235
+ scrollArea = {
236
+ top: panorama.position().top,
237
+ left: panorama.position().left,
238
+ bottom: (panorama.position().top + panorama.height()),
239
+ right: (panorama.position().left + panorama.width()),
240
+ };
241
+ }
242
+
243
+ return scrollArea;
244
+
245
+ },
246
+
247
+ getMinScale: function(activeAreas) {
248
+ var smallestScale;
249
+ var that = this;
250
+ var minimumSize = this.minScalingSize;
251
+ var smallestSize = Math.min(this.panorama.attr('data-width'), this.panorama.attr('data-height'));
252
+
253
+ if(this.minScaling) {
254
+
255
+ for (var i = 0; i < activeAreas.length; i++) {
256
+ var el = $(activeAreas[i]);
257
+
258
+ if(el.attr('data-height') / 100 * that.panorama.attr('data-height') < smallestSize) {
259
+ smallestSize = el.attr('data-height') / 100 * that.panorama.attr('data-height');
260
+ }
261
+ if(el.attr('data-width') / 100 * that.panorama.attr('data-width') < smallestSize) {
262
+ smallestSize = el.attr('data-width') / 100 * that.panorama.attr('data-width');
263
+ }
264
+ smallestScale = minimumSize / smallestSize;
265
+ }
266
+ } else {
267
+ smallestScale = 0;
268
+ }
269
+
270
+ return smallestScale;
271
+ },
272
+
273
+ update: function(addEnvironment, limitScrolling, startScrollPosition, minScaling) {
274
+ this.addEnvironment = addEnvironment;
275
+ this.limitScrolling = limitScrolling;
276
+ this.startScrollPosition = _.clone(startScrollPosition);
277
+ this.minScaling = minScaling;
278
+
279
+ this.refresh();
280
+ },
281
+
282
+ refresh: function() {
283
+ this.keepingScrollPosition(function() {
284
+ var pageElement = this.options.page;
285
+
286
+ this.panorama = this.options.panorama();
287
+
288
+ this.panoramaSize = this.getPanoramaSize(pageElement);
289
+ this.panorama.width(this.panoramaSize.width);
290
+ this.panorama.height(this.panoramaSize.height);
291
+
292
+ this.activeAreas = pageElement.find(this.options.activeAreasSelector);
293
+ this.scrollArea = this.getScrollArea(this.activeAreas);
294
+
295
+ this.innerScrollerElement.addClass('measuring');
296
+
297
+ this.innerScrollerElement.width(this.scrollArea.right - this.scrollArea.left);
298
+ this.innerScrollerElement.height(this.scrollArea.bottom - this.scrollArea.top);
299
+
300
+ var maxTranslateX = this.panoramaSize.width - pageElement.width();
301
+ var maxTranslateY = this.panoramaSize.height - pageElement.height();
302
+
303
+ this.panoramaWrapper.css({
304
+ left: -Math.min(maxTranslateX, this.scrollArea.left) +'px',
305
+ top: -Math.min(maxTranslateY, this.scrollArea.top) + 'px'
306
+ });
307
+
308
+ this.innerScrollerElement.removeClass('measuring');
309
+ this.scroller.refresh();
310
+
311
+ var leftToCenterInnerScroller = (pageElement.width() - (this.scrollArea.right - this.scrollArea.left)) / 2;
312
+ var topToCenterInnerScroller = (pageElement.height() - (this.scrollArea.bottom - this.scrollArea.top)) / 2;
313
+
314
+ this.innerScrollerElement.css('left', (this.scroller.maxX() == 0 && this.panoramaSize.width > this.scrollArea.right - this.scrollArea.left ? Math.min(leftToCenterInnerScroller, this.scrollArea.left) : 0) + "px");
315
+ this.innerScrollerElement.css('top', (this.scroller.maxY() == 0 && this.panoramaSize.height > pageElement.height() ? Math.min(topToCenterInnerScroller, this.scrollArea.top) : 0) + "px");
316
+ });
317
+ },
318
+
319
+ getPanoramaSize: function(pageElement) {
320
+ var result = {};
321
+ var windowRatio = pageElement.width() / pageElement.height();
322
+ var environmentMargin = this.addEnvironment ? (1 + this.environmentMargin) : 1;
323
+ var smallestScale = this.getMinScale(this.activeAreas);
324
+ var imageRatio;
325
+
326
+ if (this.panorama.attr('data-height') > 0) {
327
+ imageRatio = this.panorama.attr('data-width') / this.panorama.attr('data-height');
328
+ }
329
+ else {
330
+ imageRatio = 1;
331
+ }
332
+
333
+ if(imageRatio > windowRatio) {
334
+ result.height = pageElement.height() * environmentMargin;
335
+ result.width = result.height * imageRatio;
336
+ }
337
+ else {
338
+ result.width = pageElement.width() * environmentMargin;
339
+ result.height = result.width / imageRatio;
340
+ }
341
+
342
+ if (result.width < this.panorama.attr('data-width') * smallestScale) {
343
+ result.width = this.panorama.attr('data-width') * smallestScale;
344
+ result.height = this.panorama.attr('data-height') * smallestScale;
345
+ }
346
+
347
+ return result;
348
+ },
349
+
350
+ resetScrollPosition: function() {
351
+ this.centerToPoint(this.panoramaToScroller(this.startScrollPosition), 0);
352
+ },
353
+
354
+ centerToPoint: function(point, time) {
355
+ point = point || this.currentScrollPosition;
356
+
357
+ var absoluteX = this.scroller.maxX() * point.x;
358
+ var absoluteY = this.scroller.maxY() * point.y;
359
+
360
+ this.scroller.scrollTo(absoluteX, absoluteY, time);
361
+
362
+ this.currentScrollPosition = this.currentScrollPosition || point;
363
+ },
364
+
365
+ keepingScrollPosition: function(fn) {
366
+ var panoramaPosition;
367
+
368
+ if (this.currentScrollPosition) {
369
+ panoramaPosition = this.scrollerToPanorama(this.currentScrollPosition);
370
+ }
371
+ else {
372
+ panoramaPosition = this.startScrollPosition;
373
+ }
374
+
375
+ fn.call(this);
376
+
377
+ this.centerToPoint(this.panoramaToScroller(panoramaPosition));
378
+ },
379
+
380
+ scrollerToPanorama: function(point) {
381
+ var scrollAreaWidth = (this.scrollArea.right - this.scrollArea.left);
382
+ var scrollAreaHeight = (this.scrollArea.bottom - this.scrollArea.top);
383
+
384
+ return {
385
+ x: this.panoramaSize.width === 0 ?
386
+ 0 :
387
+ (this.scrollArea.left + point.x * scrollAreaWidth) / this.panoramaSize.width,
388
+ y: this.panoramaSize.height === 0?
389
+ 0 :
390
+ (this.scrollArea.top + point.y * scrollAreaHeight) / this.panoramaSize.height
391
+ };
392
+ },
393
+
394
+ panoramaToScroller: function(point) {
395
+ var scrollAreaWidth = (this.scrollArea.right - this.scrollArea.left);
396
+ var scrollAreaHeight = (this.scrollArea.bottom - this.scrollArea.top);
397
+
398
+ return {
399
+ x: scrollAreaWidth === 0 ?
400
+ 0 :
401
+ (point.x * this.panoramaSize.width - this.scrollArea.left) / scrollAreaWidth ,
402
+ y: scrollAreaHeight === 0 ?
403
+ 0 :
404
+ (point.y * this.panoramaSize.height - this.scrollArea.top) / scrollAreaHeight
405
+ };
406
+ },
407
+
408
+ updateScrollPosition: function() {
409
+ var that = this;
410
+
411
+ setTimeout(function() {
412
+ that.currentScrollPosition.x = that.scroller.maxX() !== 0 ? that.scroller.positionX() / that.scroller.maxX() : 0;
413
+ that.currentScrollPosition.y = that.scroller.maxY() !== 0 ? that.scroller.positionY() / that.scroller.maxY() : 0;
414
+ }, 10);
415
+
416
+ if (this.lastMouseMoveEvent) {
417
+ this.calcAreaOpacity(this.activeAreas, this.lastMouseMoveEvent.pageX, this.lastMouseMoveEvent.pageY);
418
+ }
419
+ }
420
+ });
421
+ }(jQuery));