##// END OF EJS Templates
Add ambiplayer
Bohdan Horbeshko -
r2148:06e50007 ambiplayer
parent child Browse files
Show More
@@ -0,0 +1,125 b''
1 div.ambiplayer {
2 position: relative;
3 }
4
5 div.ambiplayer > * {
6 position: absolute;
7 }
8
9 div.ambiplayer, .ambiplayer-background {
10 width: 200px;
11 height: 131px;
12 }
13
14 .ambiplayer-background {
15 filter: brightness(.6);
16 }
17
18 .ambiplayer-input-playpause {
19 background: #333;
20 width: 50px;
21 height: 50px;
22 border-radius: 25px;
23 top: calc(50% - 25px);
24 left: calc(50% - 25px);
25 border: 1px solid #ffd37d;
26 cursor: pointer;
27 opacity: .75;
28 }
29
30 .ambiplayer-input-playpause:before, .ambiplayer-input-playpause:after {
31 content: "";
32 color: #ffd37d;
33 position: absolute;
34 display: block;
35 transition: border-width .1s ease, width .5s ease, height .5s ease;
36 }
37
38 .ambiplayer-input-playpause.ambiplayer-state-play:before, .ambiplayer-input-playpause.ambiplayer-state-play:after {
39 width: 6px;
40 height: 19px;
41 background-color: #ffd37d;
42 top: 14px;
43 }
44
45 .ambiplayer-input-playpause.ambiplayer-state-play:before {
46 left: 15px;
47 }
48
49 .ambiplayer-input-playpause.ambiplayer-state-play:after {
50 left: 27px;
51 }
52
53 .ambiplayer-input-playpause.ambiplayer-state-pause:before {
54 left: 18px;
55 border-style: solid;
56 border-color: transparent;
57 border-width: 13px 18px;
58 border-left-color: #ffd37d;
59 top: 11px;
60 background: transparent;
61 }
62
63 .ambiplayer-input-playpause.ambiplayer-state-pause:before, .ambiplayer-input-playpause.ambiplayer-state-pause:after {
64 width: 0px;
65 height: 0px;
66 }
67
68 .ambiplayer-input-volume {
69 transform: rotateZ(270deg);
70 width: 80px;
71 left: -15px;
72 top: 50px;
73 }
74
75 .ambiplayer-input-track {
76 width: 100px;
77 left: 50px;
78 bottom: calc(1em - 3px);
79 }
80
81 .ambiplayer-input-volume, .ambiplayer-input-track {
82 background: #333;
83 opacity: .75;
84 cursor: pointer;
85 height: 7px;
86 -webkit-appearance: none;
87 appearance: none;
88 border: 0;
89 }
90
91 .ambiplayer-input-volume::-webkit-progress-bar, .ambiplayer-input-track::-webkit-progress-bar {
92 background: #333;
93 }
94
95 .ambiplayer-input-volume:hover, .ambiplayer-input-track:hover, .ambiplayer-input-playpause:hover {
96 opacity: 1;
97 }
98
99 .ambiplayer-input-volume::-webkit-progress-value, .ambiplayer-input-track::-webkit-progress-value {
100 background-color: #ffd37d;
101 }
102
103 .ambiplayer-input-volume::-moz-progress-bar, .ambiplayer-input-track::-moz-progress-bar {
104 background-color: #ffd37d;
105 }
106
107 .ambiplayer-input-volume::-ms-fill, .ambiplayer-input-track::-ms-fill {
108 background-color: #ffd37d;
109 }
110
111 .ambiplayer-output-currenttime {
112 left: .5em;
113 }
114
115 .ambiplayer-output-duration {
116 right: .5em;
117 }
118
119 .ambiplayer > output {
120 bottom: .5em;
121 background-color: #333;
122 color: #ffd37d;
123 padding: 2px;
124 border-radius: 3px;
125 }
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,145 b''
1 var ambiplayer_init_hook = function() {
2 $('audio.ambiplayer').each(function() {
3 $(this).data('ambiplayer-instance', new Ambiplayer(this));
4 })
5 }
6
7 var ambiplayer_delete_hook = function(node) {
8 $('.ambiplayer audio', node).each(function() {
9 $(this).data('ambiplayer-instance').destroy();
10 });
11 }
12
13 var _AMBIPLAYER_HTML_TEMPLATE =
14 '<div class="ambiplayer">'+
15 ' <div class="ambiplayer-background"></div>'+
16 ' <button class="ambiplayer-input-playpause ambiplayer-state-pause"></button>'+
17 ' <progress class="ambiplayer-input-volume"/>'+
18 ' <output class="ambiplayer-output-currenttime">0:00</output>'+
19 ' <progress class="ambiplayer-input-track"/>'+
20 ' <output class="ambiplayer-output-duration">0:00</output>'+
21 '</div>';
22
23 var _timeformatter = function(seconds) {
24 var minutes = (seconds / 60)|0;
25 seconds = (seconds % 60)|0;
26 return minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
27 }
28
29 var ambiplayer_ctx;
30
31 var Ambiplayer = function(audioNode) {
32 var mediaNode = new Audio(audioNode.src);
33 this.$audioNode = $(audioNode);
34 this._state = 'pause';
35
36 this.destroy = function() {
37 mediaNode.pause();
38 }
39
40 this.$playerNode = $(_AMBIPLAYER_HTML_TEMPLATE);
41
42 audioNode.parentNode.replaceChild(this.$playerNode.get(0), audioNode);
43 this.$playerNode.get(0).appendChild(audioNode);
44 this.$audioNode.removeClass('ambiplayer').hide();
45
46 this.$background = $('.ambiplayer-background', this.$playerNode);
47 this.$playPauseButton = $('.ambiplayer-input-playpause', this.$playerNode);
48 this.$volumeControl = $('.ambiplayer-input-volume', this.$playerNode);
49 this.$trackControl = $('.ambiplayer-input-track', this.$playerNode);
50 this.$outputCurrentTime = $('.ambiplayer-output-currenttime', this.$playerNode);
51 this.$outputDuration = $('.ambiplayer-output-duration', this.$playerNode);
52
53 this.$background.css('background-image', 'url('+this.$audioNode.data('bgimage')+')');
54
55 var playpause_state_hook = function() {
56 if (this._state == 'pause') {
57 this.$playPauseButton.removeClass('ambiplayer-state-play').addClass('ambiplayer-state-pause');
58 } else {
59 this.$playPauseButton.removeClass('ambiplayer-state-pause').addClass('ambiplayer-state-play');
60 }
61 }.bind(this);
62 var audionode_state_hook = function() {
63 if (this._state == 'pause') {
64 mediaNode.pause();
65 } else {
66 mediaNode.play();
67 }
68 }.bind(this);
69
70 this.$volumeControl.attr({
71 min: 0,
72 max: 1,
73 step: 0.01
74 }).val(mediaNode.volume);
75
76 this.$trackControl.attr({
77 min: 0,
78 max: 1,
79 step: 0.001
80 }).val(0);
81
82 this.$playPauseButton.on('click', function(e) {
83 this._state = this._state == 'play' ? 'pause' : 'play';
84 playpause_state_hook();
85 audionode_state_hook();
86 return false;
87 }.bind(this));
88 this.$volumeControl.on('click', function(e) {
89 var $this = $(this);
90 mediaNode.volume = 1 - (e.pageY - $this.offset().top) / $this.width();
91 $this.val(mediaNode.volume);
92 });
93 this.$trackControl.on('click', function(e) {
94 var $this = $(this);
95 mediaNode.currentTime = mediaNode.duration * (e.pageX - $this.offset().left) / $this.width();
96 });
97
98 mediaNode.addEventListener('canplay', function() {
99 this.$outputDuration.val(_timeformatter(mediaNode.duration));
100 }.bind(this));
101 mediaNode.addEventListener('timeupdate', function() {
102 this.$trackControl.val(mediaNode.currentTime/mediaNode.duration);
103 this.$outputCurrentTime.val(_timeformatter(mediaNode.currentTime));
104 }.bind(this));
105 mediaNode.addEventListener('ended', function(e) {
106 this._state = 'pause';
107 playpause_state_hook();
108 audionode_state_hook();
109 }.bind(this));
110
111 if (!ambiplayer_ctx) {
112 ambiplayer_ctx = new AudioContext();
113 }
114 var source = ambiplayer_ctx.createMediaElementSource(mediaNode);
115 var analyser = ambiplayer_ctx.createAnalyser();
116
117 source.connect(analyser);
118 analyser.connect(ambiplayer_ctx.destination);
119
120 var frequencyBuffer = new Uint8Array(analyser.frequencyBinCount);
121 var prevAver = 0;
122
123 var ambilight_update = function() {
124 requestAnimationFrame(ambilight_update);
125 analyser.getByteFrequencyData(frequencyBuffer);
126
127 var aver = 0;
128 for (var i=0; i<analyser.frequencyBinCount; i++) {
129 aver += frequencyBuffer[i];
130 }
131 aver /= analyser.frequencyBinCount;
132
133 if (Math.abs(prevAver-aver) >=5) {
134 this.$playPauseButton.css('box-shadow', '#ffd37d 0 0 30px ' + (aver/5 |0) + 'px');
135 prevAver = aver;
136 }
137 }.bind(this)
138 ambilight_update();
139 }
140
141 $(function() {
142 if (AudioContext) {
143 ambiplayer_init_hook();
144 }
145 })
@@ -29,6 +29,7 b' FILE_TYPES_AUDIO = ('
29 'ogg',
29 'ogg',
30 'mp3',
30 'mp3',
31 'opus',
31 'opus',
32 'flac',
32 )
33 )
33 FILE_TYPES_IMAGE = (
34 FILE_TYPES_IMAGE = (
34 'jpeg',
35 'jpeg',
@@ -65,7 +66,7 b' ABSTRACT_FORMAT_VIEW = \'<a href="{}">\'\\'
65 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
66 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
66 '</a>'
67 '</a>'
67 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
68 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
68 AUDIO_FORMAT_VIEW = '<audio controls src="{}"></audio>'
69 AUDIO_FORMAT_VIEW = '<audio controls src="{}" class="ambiplayer" data-bgimage="{}"></audio>'
69 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
70 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
70 '<img class="post-image-preview"' \
71 '<img class="post-image-preview"' \
71 ' src="{}"' \
72 ' src="{}"' \
@@ -121,7 +122,7 b' class AbstractViewer:'
121 self.file_type, filesizeformat(self.file.size),
122 self.file_type, filesizeformat(self.file.size),
122 self.file_type, search_url, self.file.name)
123 self.file_type, search_url, self.file.name)
123
124
124 def get_format_view(self):
125 def get_format_image(self):
125 image_name = PLAIN_FILE_FORMATS.get(self.file_type, self.file_type)
126 image_name = PLAIN_FILE_FORMATS.get(self.file_type, self.file_type)
126 file_name = FILE_FILEFORMAT.format(image_name)
127 file_name = FILE_FILEFORMAT.format(image_name)
127
128
@@ -130,6 +131,10 b' class AbstractViewer:'
130 else:
131 else:
131 image = FILE_STUB_IMAGE
132 image = FILE_STUB_IMAGE
132
133
134 return image
135
136 def get_format_view(self):
137 image = self.get_format_image()
133 w, h = get_static_dimensions(image)
138 w, h = get_static_dimensions(image)
134
139
135 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
140 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
@@ -149,8 +154,20 b' class AudioViewer(AbstractViewer):'
149 def supports(file_type):
154 def supports(file_type):
150 return file_type in FILE_TYPES_AUDIO
155 return file_type in FILE_TYPES_AUDIO
151
156
157 def get_format_image(self):
158 file_name = FILE_FILEFORMAT.format(self.file_type)
159
160 if file_exists(file_name):
161 image = file_name
162 else:
163 image = FILE_STUB_IMAGE
164
165 return image
166
152 def get_format_view(self):
167 def get_format_view(self):
153 return AUDIO_FORMAT_VIEW.format(self.file.url)
168 image = self.get_format_image()
169
170 return AUDIO_FORMAT_VIEW.format(self.file.url, static(image))
154
171
155
172
156 class SvgViewer(AbstractViewer):
173 class SvgViewer(AbstractViewer):
@@ -10,7 +10,10 b' function $x(path, root) {'
10 }
10 }
11
11
12 function $del(el) {
12 function $del(el) {
13 if(el) el.parentNode.removeChild(el);
13 if (el) {
14 ambiplayer_delete_hook && ambiplayer_delete_hook(el);
15 el.parentNode.removeChild(el);
16 }
14 }
17 }
15
18
16 function $each(list, fn) {
19 function $each(list, fn) {
@@ -22,6 +25,9 b' function $each(list, fn) {'
22 function mkPreview(cln, html) {
25 function mkPreview(cln, html) {
23 cln.innerHTML = html;
26 cln.innerHTML = html;
24
27
28 setTimeout(function() {
29 ambiplayer_init_hook && ambiplayer_init_hook();
30 }, 0);
25 addScriptsToPost($(cln));
31 addScriptsToPost($(cln));
26 }
32 }
27
33
@@ -78,7 +84,7 b' function showPostPreview(e) {'
78
84
79 if (post.length > 0) {
85 if (post.length > 0) {
80 // If post is on the same page but not visible, generate preview from it
86 // If post is on the same page but not visible, generate preview from it
81 var postClone = post.clone();
87 var postClone = post.clone(true);
82 postClone.removeAttr('style');
88 postClone.removeAttr('style');
83 var postdata = postClone.wrap("<div/>").parent().html();
89 var postdata = postClone.wrap("<div/>").parent().html();
84
90
@@ -118,7 +118,8 b' function scrollToBottom() {'
118 }
118 }
119
119
120 function showQuoteButton() {
120 function showQuoteButton() {
121 var selection = window.getSelection().getRangeAt(0).getBoundingClientRect();
121 var windowSelection = window.getSelection();
122 var selection = windowSelection.rangeCount && windowSelection.getRangeAt(0).getBoundingClientRect();
122 var quoteButton = $("#quote-button");
123 var quoteButton = $("#quote-button");
123 if (selection.width > 0) {
124 if (selection.width > 0) {
124 // quoteButton.offset({ top: selection.top - selection.height, left: selection.left });
125 // quoteButton.offset({ top: selection.top - selection.height, left: selection.left });
@@ -151,6 +151,10 b' function updatePost(postHtml) {'
151
151
152 processNewPost(post);
152 processNewPost(post);
153
153
154 setTimeout(function() {
155 ambiplayer_init_hook && ambiplayer_init_hook();
156 }, 0);
157
154 return type;
158 return type;
155 }
159 }
156
160
@@ -10,6 +10,7 b''
10 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/highlight.css' %}" media="all"/>
10 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/highlight.css' %}" media="all"/>
11 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/jquery-ui.min.css' %}" media="all"/>
11 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/jquery-ui.min.css' %}" media="all"/>
12 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/jquery.contextMenu.min.css' %}" media="all"/>
12 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/jquery.contextMenu.min.css' %}" media="all"/>
13 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/ambiplayer.css' %}" media="all"/>
13 <link rel="stylesheet" type="text/css" href="{% static theme_css %}" media="all"/>
14 <link rel="stylesheet" type="text/css" href="{% static theme_css %}" media="all"/>
14
15
15 {% if rss_url %}
16 {% if rss_url %}
@@ -71,6 +72,7 b''
71
72
72 <script src="{% static 'js/jquery.mousewheel.js' %}"></script>
73 <script src="{% static 'js/jquery.mousewheel.js' %}"></script>
73 <script src="{% static 'js/3party/highlight.min.js' %}"></script>
74 <script src="{% static 'js/3party/highlight.min.js' %}"></script>
75 <script src="{% static 'js/3party/ambiplayer.js' %}"></script>
74
76
75 <script src="{% url 'js_info_dict' %}"></script>
77 <script src="{% url 'js_info_dict' %}"></script>
76
78
General Comments 0
You need to be logged in to leave comments. Login now