|
2510 | 2510 | //Btw, you can also launch console automatically:
|
2511 | 2511 | //<div id="impress" data-console-autolaunch="true">
|
2512 | 2512 | if ( root.dataset.consoleAutolaunch === 'true' ) {
|
2513 |
| - window.open(); |
| 2513 | + open(); |
2514 | 2514 | }
|
2515 | 2515 | };
|
2516 | 2516 |
|
|
2706 | 2706 |
|
2707 | 2707 | } )( document, window );
|
2708 | 2708 |
|
| 2709 | +/** |
| 2710 | + * Media Plugin |
| 2711 | + * |
| 2712 | + * This plugin will do the following things: |
| 2713 | + * |
| 2714 | + * - Add a special class when playing (body.impress-media-video-playing |
| 2715 | + * and body.impress-media-video-playing) and pausing media (body.impress-media-video-paused |
| 2716 | + * and body.impress-media-audio-paused) (removing them when ending). |
| 2717 | + * This can be useful for example for darkening the background or fading out other elements |
| 2718 | + * while a video is playing. |
| 2719 | + * Only media at the current step are taken into account. All classes are removed when leaving |
| 2720 | + * a step. |
| 2721 | + * |
| 2722 | + * - Introduce the following new data attributes: |
| 2723 | + * |
| 2724 | + * - data-media-autoplay="true": Autostart media when entering its step. |
| 2725 | + * - data-media-autostop="true": Stop media (= pause and reset to start), when leaving its |
| 2726 | + * step. |
| 2727 | + * - data-media-autopause="true": Pause media but keep current time when leaving its step. |
| 2728 | + * |
| 2729 | + * When these attributes are added to a step they are inherited by all media on this step. |
| 2730 | + * Of course this setting can be overwritten by adding different attributes to inidvidual |
| 2731 | + * media. |
| 2732 | + * |
| 2733 | + * The same rule applies when this attributes is added to the root element. Settings can be |
| 2734 | + * overwritten for individual steps and media. |
| 2735 | + * |
| 2736 | + * Examples: |
| 2737 | + * - data-media-autostart="true" data-media-autostop="true": start media on enter, stop on |
| 2738 | + * leave, restart from beginning when re-entering the step. |
| 2739 | + * |
| 2740 | + * - data-media-autostart="true" data-media-autopause="true": start media on enter, pause on |
| 2741 | + * leave, resume on re-enter |
| 2742 | + * |
| 2743 | + * - data-media-autostart="true" data-media-autostop="true" data-media-autopause="true": start |
| 2744 | + * media on enter, stop on leave (stop overwrites pause). |
| 2745 | + * |
| 2746 | + * - data-media-autostart="true" data-media-autopause="false": let media start automatically |
| 2747 | + * when entering a step and let it play when leaving the step. |
| 2748 | + * |
| 2749 | + * - <div id="impress" data-media-autostart="true"> ... <div class="step" |
| 2750 | + * data-media-autostart="false"> |
| 2751 | + * All media is startet automatically on all steps except the one that has the |
| 2752 | + * data-media-autostart="false" attribute. |
| 2753 | + * |
| 2754 | + * - Pro tip: Use <audio onended="impress().next()"> or <video onended="impress().next()"> to |
| 2755 | + * proceed to the next step automatically, when the end of the media is reached. |
| 2756 | + * |
| 2757 | + * |
| 2758 | + * Copyright 2018 Holger Teichert (@complanar) |
| 2759 | + * Released under the MIT license. |
| 2760 | + */ |
| 2761 | +/* global window, document */ |
| 2762 | + |
| 2763 | +( function( document, window ) { |
| 2764 | + "use strict"; |
| 2765 | + var root, api, gc, attributeTracker; |
| 2766 | + |
| 2767 | + attributeTracker = []; |
| 2768 | + |
| 2769 | + // Function names |
| 2770 | + var enhanceMediaNodes, |
| 2771 | + enhanceMedia, |
| 2772 | + removeMediaClasses, |
| 2773 | + onStepenterDetectImpressConsole, |
| 2774 | + onStepenter, |
| 2775 | + onStepleave, |
| 2776 | + onPlay, |
| 2777 | + onPause, |
| 2778 | + onEnded, |
| 2779 | + getMediaAttribute, |
| 2780 | + teardown; |
| 2781 | + |
| 2782 | + document.addEventListener( "impress:init", function( event ) { |
| 2783 | + root = event.target; |
| 2784 | + api = event.detail.api; |
| 2785 | + gc = api.lib.gc; |
| 2786 | + |
| 2787 | + enhanceMedia(); |
| 2788 | + |
| 2789 | + gc.pushCallback( teardown ); |
| 2790 | + }, false ); |
| 2791 | + |
| 2792 | + teardown = function() { |
| 2793 | + var el, i; |
| 2794 | + removeMediaClasses(); |
| 2795 | + for ( i = 0; i < attributeTracker.length; i += 1 ) { |
| 2796 | + el = attributeTracker[ i ]; |
| 2797 | + el.node.removeAttribute( el.attr ); |
| 2798 | + } |
| 2799 | + attributeTracker = []; |
| 2800 | + }; |
| 2801 | + |
| 2802 | + getMediaAttribute = function( attributeName, nodes ) { |
| 2803 | + var attrName, attrValue, i, node; |
| 2804 | + attrName = "data-media-" + attributeName; |
| 2805 | + |
| 2806 | + // Look for attributes in all nodes |
| 2807 | + for ( i = 0; i < nodes.length; i += 1 ) { |
| 2808 | + node = nodes[ i ]; |
| 2809 | + |
| 2810 | + // First test, if the attribute exists, because some browsers may return |
| 2811 | + // an empty string for non-existing attributes - specs are not clear at that point |
| 2812 | + if ( node.hasAttribute( attrName ) ) { |
| 2813 | + |
| 2814 | + // Attribute found, return their parsed boolean value, empty strings count as true |
| 2815 | + // to enable empty value booleans (common in html5 but not allowed in well formed |
| 2816 | + // xml). |
| 2817 | + attrValue = node.getAttribute( attrName ); |
| 2818 | + if ( attrValue === "" || attrValue === "true" ) { |
| 2819 | + return true; |
| 2820 | + } else { |
| 2821 | + return false; |
| 2822 | + } |
| 2823 | + } |
| 2824 | + |
| 2825 | + // No attribute found at current node, proceed with next round |
| 2826 | + } |
| 2827 | + |
| 2828 | + // Last resort: no attribute found - return undefined to distiguish from false |
| 2829 | + return undefined; |
| 2830 | + }; |
| 2831 | + |
| 2832 | + onPlay = function( event ) { |
| 2833 | + var type = event.target.nodeName.toLowerCase(); |
| 2834 | + document.body.classList.add( "impress-media-" + type + "-playing" ); |
| 2835 | + document.body.classList.remove( "impress-media-" + type + "-paused" ); |
| 2836 | + }; |
| 2837 | + |
| 2838 | + onPause = function( event ) { |
| 2839 | + var type = event.target.nodeName.toLowerCase(); |
| 2840 | + document.body.classList.add( "impress-media-" + type + "-paused" ); |
| 2841 | + document.body.classList.remove( "impress-media-" + type + "-playing" ); |
| 2842 | + }; |
| 2843 | + |
| 2844 | + onEnded = function( event ) { |
| 2845 | + var type = event.target.nodeName.toLowerCase(); |
| 2846 | + document.body.classList.remove( "impress-media-" + type + "-playing" ); |
| 2847 | + document.body.classList.remove( "impress-media-" + type + "-paused" ); |
| 2848 | + }; |
| 2849 | + |
| 2850 | + removeMediaClasses = function() { |
| 2851 | + var type, types; |
| 2852 | + types = [ "video", "audio" ]; |
| 2853 | + for ( type in types ) { |
| 2854 | + document.body.classList.remove( "impress-media-" + types[ type ] + "-playing" ); |
| 2855 | + document.body.classList.remove( "impress-media-" + types[ type ] + "-paused" ); |
| 2856 | + } |
| 2857 | + }; |
| 2858 | + |
| 2859 | + enhanceMediaNodes = function() { |
| 2860 | + var i, id, media, mediaElement, type; |
| 2861 | + |
| 2862 | + media = root.querySelectorAll( "audio, video" ); |
| 2863 | + for ( i = 0; i < media.length; i += 1 ) { |
| 2864 | + type = media[ i ].nodeName.toLowerCase(); |
| 2865 | + |
| 2866 | + // Set an id to identify each media node - used e.g. for cross references by |
| 2867 | + // the consoleMedia plugin |
| 2868 | + mediaElement = media[ i ]; |
| 2869 | + id = mediaElement.getAttribute( "id" ); |
| 2870 | + if ( id === undefined || id === null ) { |
| 2871 | + mediaElement.setAttribute( "id", "media-" + type + "-" + i ); |
| 2872 | + attributeTracker.push( { "node": mediaElement, "attr": "id" } ); |
| 2873 | + } |
| 2874 | + gc.addEventListener( mediaElement, "play", onPlay ); |
| 2875 | + gc.addEventListener( mediaElement, "playing", onPlay ); |
| 2876 | + gc.addEventListener( mediaElement, "pause", onPause ); |
| 2877 | + gc.addEventListener( mediaElement, "ended", onEnded ); |
| 2878 | + } |
| 2879 | + }; |
| 2880 | + |
| 2881 | + enhanceMedia = function() { |
| 2882 | + var steps, stepElement, i; |
| 2883 | + enhanceMediaNodes(); |
| 2884 | + steps = document.getElementsByClassName( "step" ); |
| 2885 | + for ( i = 0; i < steps.length; i += 1 ) { |
| 2886 | + stepElement = steps[ i ]; |
| 2887 | + |
| 2888 | + gc.addEventListener( stepElement, "impress:stepenter", onStepenter ); |
| 2889 | + gc.addEventListener( stepElement, "impress:stepleave", onStepleave ); |
| 2890 | + } |
| 2891 | + }; |
| 2892 | + |
| 2893 | + onStepenterDetectImpressConsole = function() { |
| 2894 | + return { |
| 2895 | + "preview": ( window.frameElement !== null && window.frameElement.id === "preView" ), |
| 2896 | + "slideView": ( window.frameElement !== null && window.frameElement.id === "slideView" ) |
| 2897 | + }; |
| 2898 | + }; |
| 2899 | + |
| 2900 | + onStepenter = function( event ) { |
| 2901 | + var stepElement, media, mediaElement, i, onConsole, autoplay; |
| 2902 | + if ( ( !event ) || ( !event.target ) ) { |
| 2903 | + return; |
| 2904 | + } |
| 2905 | + |
| 2906 | + stepElement = event.target; |
| 2907 | + removeMediaClasses(); |
| 2908 | + |
| 2909 | + media = stepElement.querySelectorAll( "audio, video" ); |
| 2910 | + for ( i = 0; i < media.length; i += 1 ) { |
| 2911 | + mediaElement = media[ i ]; |
| 2912 | + |
| 2913 | + // Autoplay when (maybe inherited) autoplay setting is true, |
| 2914 | + // but only if not on preview of the next step in impressConsole |
| 2915 | + onConsole = onStepenterDetectImpressConsole(); |
| 2916 | + autoplay = getMediaAttribute( "autoplay", [ mediaElement, stepElement, root ] ); |
| 2917 | + if ( autoplay && !onConsole.preview ) { |
| 2918 | + if ( onConsole.slideView ) { |
| 2919 | + mediaElement.muted = true; |
| 2920 | + } |
| 2921 | + mediaElement.play(); |
| 2922 | + } |
| 2923 | + } |
| 2924 | + }; |
| 2925 | + |
| 2926 | + onStepleave = function( event ) { |
| 2927 | + var stepElement, media, i, mediaElement, autoplay, autopause, autostop; |
| 2928 | + if ( ( !event || !event.target ) ) { |
| 2929 | + return; |
| 2930 | + } |
| 2931 | + |
| 2932 | + stepElement = event.target; |
| 2933 | + media = event.target.querySelectorAll( "audio, video" ); |
| 2934 | + for ( i = 0; i < media.length; i += 1 ) { |
| 2935 | + mediaElement = media[ i ]; |
| 2936 | + |
| 2937 | + autoplay = getMediaAttribute( "autoplay", [ mediaElement, stepElement, root ] ); |
| 2938 | + autopause = getMediaAttribute( "autopause", [ mediaElement, stepElement, root ] ); |
| 2939 | + autostop = getMediaAttribute( "autostop", [ mediaElement, stepElement, root ] ); |
| 2940 | + |
| 2941 | + // If both autostop and autopause are undefined, set it to the value of autoplay |
| 2942 | + if ( autostop === undefined && autopause === undefined ) { |
| 2943 | + autostop = autoplay; |
| 2944 | + } |
| 2945 | + |
| 2946 | + if ( autopause || autostop ) { |
| 2947 | + mediaElement.pause(); |
| 2948 | + if ( autostop ) { |
| 2949 | + mediaElement.currentTime = 0; |
| 2950 | + } |
| 2951 | + } |
| 2952 | + } |
| 2953 | + removeMediaClasses(); |
| 2954 | + }; |
| 2955 | + |
| 2956 | +} )( document, window ); |
| 2957 | + |
2709 | 2958 | /**
|
2710 | 2959 | * Mobile devices support
|
2711 | 2960 | *
|
|
0 commit comments