{"id":4252,"date":"2010-03-02T21:22:12","date_gmt":"2010-03-02T20:22:12","guid":{"rendered":"http:\/\/www.chipwreck.de\/blog\/?p=4252"},"modified":"2018-01-10T13:42:50","modified_gmt":"2018-01-10T12:42:50","slug":"html-5-video-mootools","status":"publish","type":"post","link":"https:\/\/www.chipwreck.de\/blog\/2010\/03\/02\/html-5-video-mootools\/","title":{"rendered":"HTML 5 video &#038; Mootools"},"content":{"rendered":"<p>This is about scripting the HTML 5 video-tag with Mootools 1.2.<\/p>\n<p>One might ask, what is special about the video tag? At the first look nothing, it&#8217;s a HTML-element like any other. But if you try to use some mootools functions (adding events or querying properties) you soon realize that it doesn&#8217;t work out of the box in the current release. But there&#8217;s a workaround.<!--more--><\/p>\n<p class=\"small\">Read more about it at <a href=\"https:\/\/mootools.lighthouseapp.com\/projects\/2706\/tickets\/731-addeventtimeupdate-to-audio-elements-doesnt-work\" class=\"external\">Lighthouse (mootools bugtracking) &raquo;<\/a><\/p>\n<p>To put it briefly: To access video properties (like $(&#8216;myvid&#8217;).currentTime) or to attach new events (like volumechange) via mootools, you can use this workaround:<\/p>\n<pre lang=\"javascript\">\r\nvar media_events = {\r\n    loadstart: 2, progress: 2, suspend: 2, abort: 2,\r\n    error: 2, emptied: 2, stalled: 2, play: 2, pause: 2,\r\n    loadedmetadata: 2, loadeddata: 2, waiting: 2, playing: 2,\r\n    canplay: 2, canplaythrough: 2, seeking: 2, seeked: 2,\r\n    timeupdate: 2, ended: 2, ratechange: 2, durationchange: 2, volumechange: 2\r\n}\r\nElement.NativeEvents = $merge(Element.NativeEvents, media_events);\r\n\t\r\nvar media_properties = [\r\n\t'videoWidth', 'videoHeight', 'readyState', 'autobuffer',\r\n\t'error', 'networkState', 'currentTime', 'duration', 'paused', 'seeking',\r\n\t'ended', 'autoplay', 'loop',  'controls', 'volume', 'muted',\r\n\t'startTime', 'buffered', 'defaultPlaybackRate', 'playbackRate', 'played', 'seekable' \/\/ these 6 properties currently don't work in firefox\t\t\r\n];\t\r\nmedia_properties.each(function(prop){\r\n\tElement.Properties.set(prop, {\r\n\t\tset: function(value){\r\n\t\t\tthis[prop] = value;\r\n\t\t},\r\n\t\tget: function(){\r\n\t\t\treturn this[prop];\r\n\t\t}\r\n\t})\r\n});\r\n<\/pre>\n<p>Please note: This is only a quick workaround until it&#8217;s implemented in mootools-core.<\/p>\n<h4>How to realize video playback controls via mootools<\/h4>\n<p class=\"small\">You can see everything in the demo below<\/p>\n<h6>Play\/Pause\/Rewind<\/h6>\n<p>This is quite straightforward (using above workaround):<\/p>\n<pre lang=\"javascript\">\r\n&lt;button onclick=\"$('myvid').set('currentTime', 0)\"&gt;rewind&lt;\/button&gt;\r\n&lt;button onclick=\"$('myvid').play()\"&gt;play&lt;\/button&gt;\r\n&lt;button onclick=\"$('myvid').pause()\"&gt;pause&lt;\/button&gt;\r\n<\/pre>\n<h6>Showing the current playback time<\/h6>\n<p>We use a span to display the current time of the video playback and to update it continously we use the timeupdate-Event like this:<\/p>\n<pre lang=\"javascript\">\r\n$('myvid').addEvent('timeupdate', function(an_event){\r\n\t$('timemeter').set('html', this.get('currentTime').toFixed(1));\r\n});\r\n<\/pre>\n<h6>Controlling volume via buttons and displaying it<\/h6>\n<p>Also no rocket science, but we have to look at not exceeding the limits for volume (from 0.0 up to 1.0):<\/p>\n<pre lang=\"javascript\">\r\n&lt;button onclick=\"$('myvid').set('volume', $('myvid').get('volume') &lt; .1 ? 0.0 : $('myvid').get('volume') - 0.10)\"&gt;vol-&lt;\/button&gt;\r\n&lt;button onclick=\"$('myvid').set('volume', $('myvid').get('volume') &gt; .9 ? 1.0 : $('myvid').get('volume') + 0.10)\"&gt;vol+&lt;\/button&gt;\r\n&lt;span id=\"volmeter\"&gt;1.0&lt;\/span&gt;\r\n<\/pre>\n<p>And we want to show the current volume as a number. Therefore we use the &#8220;volumechange&#8221;-event and update a simple SPAN every time this event is being fired:<\/p>\n<pre lang=\"javascript\">\r\n$('myvid').addEvent('volumechange', function(an_event){\r\n\t$('volmeter').set('html', this.get('volume').toFixed(1));\r\n});\r\n<\/pre>\n<h6>Adding a <q>mute<\/q>-button<\/h6>\n<p>The <q>muted<\/q>-state of the video overrides the current volume setting. It&#8217;s a toggle-button (on\/off), so the implementation is also quite straightforward:<\/p>\n<pre lang=\"javascript\">\r\n&lt;button id=\"mutebutton\" onclick=\"$('myvid').set('muted', !$('myvid').get('muted'))\"&gt;mute&lt;\/button&gt;\r\n<\/pre>\n<h6>Controlling volume via a slider<\/h6>\n<p>Buttons are okay, but usually we&#8217;re used to control the volume via a slider or knob. Therefore let&#8217;s use the mootools Slider (from mootools.more) to control the volume in ten steps.<\/p>\n<p>First we need a container and a knob, here quickly done with a paragraph and a button:<\/p>\n<pre lang=\"html\">\r\n&lt;p id=\"volSliderBg\" style=\"width: 100px; border: 1px solid #75838a;\"&gt;\r\n\t&lt;button id=\"volSlider\"&gt;vol&lt;\/button&gt;\r\n&lt;\/p&gt;\r\n<\/pre>\n<p>And now we setup the mootools Slider and use its onComplete event to change the volume accordingly:<\/p>\n<pre lang=\"javascript\">\r\nvar mySlider = new Slider('volSliderBg', 'volSlider', {\r\n    range: [0, 100],\r\n    wheel: true,\r\n    snap: true,\r\n    steps: 10,\r\n    initialStep: 100,\r\n    onComplete: function(step){\r\n\t$('myvid').set('volume', (step.toInt()\/100));\r\n    }\r\n});\r\n<\/pre>\n<p>As you can see by applying the above workaround we can very naturally use the new attributes and events to build our custom video controls.<\/p>\n<h4>All together now &#8211; Demo<\/h4>\n<p>\t<script type=\"text\/javascript\" src=\"\/blog\/wp-content\/themes\/chipwreck\/js\/mootools-1.2.5-core-yc.js\"><\/script><br \/>\n\t<script type=\"text\/javascript\" src=\"\/blog\/wp-content\/themes\/chipwreck\/js\/mootools-1.2.4.4-more-all-yc.js\"><\/script><br \/>\n\t<script type=\"text\/javascript\" src=\"\/blog\/wp-content\/themes\/chipwreck\/js\/chipwreck.js\"><\/script><\/p>\n<h6>Embedded video without controls<\/h6>\n<p><video id=\"myvid\" width=\"320\" height=\"240\" poster=\"\/videos\/daftpunk\/DaftPunk_Poster.jpg\"><source src=\"\/videos\/daftpunk\/DaftPunk_Concert.ogg\" type='video\/ogg; codecs=\"theora, vorbis\"'><source src=\"\/videos\/daftpunk\/DaftPunk_Concert.mp4\" type='video\/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'><\/video><\/p>\n<h6>Scripted playback controls and time display<\/h6>\n<p style=\"border: 1px dotted #acb5b9; padding: 0.25em;\">\n\t<button onclick=\"$('myvid').set('currentTime', 0)\">&laquo; rewind<\/button><br \/>\n\t&nbsp;<br \/>\n\t<a class=\"download\" id=\"playbutton\" onclick=\"$('myvid').play()\">play<\/a><br \/>\n\t<a class=\"download\" id=\"pausebutton\" onclick=\"$('myvid').pause()\">pause<\/a><br \/>\n\t<kbd id=\"timemeter\" style=\"width: 3em; display: inline-block; text-align: right;\">0.0<\/kbd>s\n<\/p>\n<h6>Scripted volume\/mute controls and volume display<\/h6>\n<p style=\"border: 1px dotted #acb5b9; padding: 0.5em;\">\n\t<button onclick=\"$('myvid').set('volume', $('myvid').get('volume') < .1 ? 0.0 : $('myvid').get('volume') - 0.10)\">vol-<\/button><br \/>\n\t<kbd id=\"volmeter\" style=\"width: 2em; display: inline-block; text-align: right;\">1.0<\/kbd><br \/>\n\t<button onclick=\"$('myvid').set('volume', $('myvid').get('volume') > .9 ? 1.0 : $(&#8216;myvid&#8217;).get(&#8216;volume&#8217;) + 0.10)&#8221;>vol+<\/button><br \/>\n\t<button id=\"mutebutton\" onclick=\"$('myvid').set('muted', !$('myvid').get('muted'))\">mute<\/button>\n<\/p>\n<h6>Alternative volume control (via slider)<\/h6>\n<div style=\"border: 1px dotted #acb5b9; padding: 0.25em;\">\n<p id=\"volSliderBg\" style=\"width: 110px; border: 1px solid #75838a;\"><button id=\"volSlider\" style=\"margin: 0; -moz-border-radius: 0; -webkit-border-radius: 0; border: none;\">vol<\/button><\/p>\n<\/div>\n<h6>Event and attribute information<\/h6>\n<div style=\"border: 1px dotted #acb5b9; padding: 0.25em;\">\n<p class=\"small\"><span class=\"grey\">Current Event:<\/span> <span id=\"videvent\"><\/span><\/p>\n<p class=\"small\"><span class=\"grey\">Ready State:<\/span> <span id=\"vidreadystate\"><\/span><\/p>\n<p class=\"small\"><span class=\"grey\">Network State:<\/span> <span id=\"vidnetworkstate\"><\/span><\/p>\n<\/div>\n<p><script type=\"text\/javascript\">\nwindow.addEvent('domready', function() {<\/p>\n<p>\tvar readyState = function(el)\n\t{\n\t\tswitch (el.get('readyState')) {\n\t\t \t\tcase el.HAVE_NOTHING: return 'have nothing';\n\t \t\tcase el.HAVE_METADATA: return 'have meta';\n\t \t\tcase el.HAVE_CURRENT_DATA: return 'have current';\n\t \t\tcase el.HAVE_FUTURE_DATA: return 'have future data';\n\t \t\tcase el.HAVE_ENOUGH_DATA: return 'have enough data';\n\t \t\tdefault: return 'unknown state';\n\t\t}\n\t}\n\tvar networkState = function(el)\n\t{\n\t\tswitch (el.get('networkState')) {\t\t\t\n\t\t\tcase el.NETWORK_EMPTY: return 'empty';\n\t\t\tcase el.NETWORK_IDLE: return 'idle';\n\t\t\tcase el.NETWORK_LOADING: return 'loading';\n\t\t\tcase el.NETWORK_LOADED: return 'loaded';\n\t\t\tcase el.NETWORK_NO_SOURCE: return 'no source';\n\t\t\tdefault: return 'unknown state';\n\t\t}\n\t}<\/p>\n<p>\tvar media_events = {\n\t    loadstart: 2,\n\t    progress: 2,\n\t    suspend: 2,\n\t    abort: 2,\n\t    error: 2,\n\t    emptied: 2,\n\t    stalled: 2,\n\t    play: 2,\n\t    pause: 2,\n\t    loadedmetadata: 2,\n\t    loadeddata: 2,\n\t    waiting: 2,\n\t    playing: 2,\n\t    canplay: 2,\n\t    canplaythrough: 2,    \n\t\tseeking: 2,\n\t\tseeked: 2,\n\t    timeupdate: 2,\n\t    ended: 2,\n\t    ratechange: 2,\n\t    durationchange: 2,\n\t    volumechange: 2\n\t}\n\tElement.NativeEvents = $merge(Element.NativeEvents, media_events);<\/p>\n<p>\tvar media_properties = [\n\t\t'videoWidth',\n\t\t'videoHeight',\n\t\t'readyState',\n\t\t'autobuffer',\n\t\t'buffered', \/\/ no ff\n\t\t'error', \n\t\t'networkState',\n\t\t'currentTime',\n\t\t'startTime', \/\/ no ff\n\t\t'duration',\n\t\t'paused',\n\t\t'defaultPlaybackRate', \/\/ no ff\n\t\t'playbackRate', \/\/ no ff\n\t\t'played', \/\/ no ff\n\t\t'seeking',\n\t\t'seekable', \/\/ no ff\n\t\t'ended',\n\t\t'autoplay',\n\t\t'loop', \n\t\t'controls',\n\t\t'volume',\n\t\t'muted'\n\t];<\/p>\n<p>\tmedia_properties.each(function(prop){\n\t\tElement.Properties.set(prop, {\n\t\t\tset: function(value){\n\t\t\t\tthis[prop] = value;\n\t\t\t},\n\t\t\tget: function(){\n\t\t\t\treturn this[prop];\n\t\t\t}\n\t\t})\n\t});<\/p>\n<p>var mySlider = new Slider('volSliderBg', 'volSlider', {\n    range: [0, 100],\n    wheel: true,\n    snap: true,\n    steps: 10,\n    initialStep: 100,\n    onComplete: function(step){\n\t\t$('myvid').set('volume', (step.toInt()\/100));\n    }\n});<\/p>\n<p>$('myvid').addEvent('timeupdate', function(an_event){\n\t$('timemeter').set('html', this.get('currentTime').toFixed(1));\n});\n$('myvid').addEvent('volumechange', function(an_event){\n\t$('volmeter').set('html', this.get('volume').toFixed(1));\n});<\/p>\n<p>$each(media_events, function(event_key, an_event){\n\t$('myvid').addEvent(an_event, function(an_event){\n\t\t$('videvent').set('html', an_event.type);\n\t\t$('vidreadystate').set('html', readyState(this));\n\t\t$('vidnetworkstate').set('html', networkState(this));\t\t<\/p>\n<p>\t\tif (an_event.type == 'volumechange') {\n\t\t\tif (this.get('muted')) {\n\t\t\t\t$('mutebutton').setStyle('color', '#181719');\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$('mutebutton').setStyle('color', '#fff');\n\t\t\t}\n\t\t}\t\t<\/p>\n<p>\t\tif (an_event.type == 'pause' || an_event.type == 'play') {\n\t\t\tif (this.get('paused')) {\n\t\t\t\t$('pausebutton').setStyle('color', '#f257af');\n\t\t\t\t$('playbutton').setStyle('color', '#181719');\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$('pausebutton').setStyle('color', '#181719');\n\t\t\t\t$('playbutton').setStyle('color', '#f257af');\n\t\t\t}\n\t\t}\n\t});\n});<\/p>\n<p>});\n<\/script><\/p>\n<p>&nbsp;<\/p>\n<p>Next post will be showing how to realize a time slider (a movable bar showing the current position in the video which can be dragged to jump to a specific position).<\/p>\n<p><em>And as always: Criticism, feedback etc. are very welcome.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is about scripting the HTML 5 video-tag with Mootools 1.2. One might ask, what is special about the video tag? At the first look nothing, it&#8217;s a HTML-element like any other. But if you try to use some mootools functions (adding events or querying properties) you soon realize that it doesn&#8217;t work out of &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.chipwreck.de\/blog\/2010\/03\/02\/html-5-video-mootools\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;HTML 5 video &#038; Mootools&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":4280,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[14],"tags":[55,10,54,79],"class_list":["post-4252","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-javascript","tag-javascript-mootools","tag-mootools","tag-track","tag-webdesign"],"jetpack_featured_media_url":"https:\/\/www.chipwreck.de\/blog\/wp-content\/uploads\/2010\/03\/Bildschirmfoto-2010-03-02-um-22.08.24.png","jetpack_shortlink":"https:\/\/wp.me\/paPEN-16A","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/posts\/4252","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/comments?post=4252"}],"version-history":[{"count":1,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/posts\/4252\/revisions"}],"predecessor-version":[{"id":8157,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/posts\/4252\/revisions\/8157"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/media\/4280"}],"wp:attachment":[{"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/media?parent=4252"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/categories?post=4252"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.chipwreck.de\/blog\/wp-json\/wp\/v2\/tags?post=4252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}