From 5e6c20486a7d04661557e070d634326ba39cabf3 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 3 Mar 2020 21:26:16 +0000 Subject: [PATCH] Update Reveal.js to 3.9.2 --- openlp/core/display/html/reveal.css | 40 +-- openlp/core/display/html/reveal.js | 387 ++++++++++++++++++++-------- openlp/core/lib/db.py | 3 +- 3 files changed, 301 insertions(+), 129 deletions(-) diff --git a/openlp/core/display/html/reveal.css b/openlp/core/display/html/reveal.css index 1b9651b58..0ca77e77a 100644 --- a/openlp/core/display/html/reveal.css +++ b/openlp/core/display/html/reveal.css @@ -3,7 +3,7 @@ * http://revealjs.com * MIT licensed * - * Copyright (C) 2019 Hakim El Hattab, http://hakim.se + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se */ /********************************************* * GLOBAL STYLES @@ -77,29 +77,29 @@ body { text-decoration: line-through; } .reveal .slides section .fragment.fade-up { - -webkit-transform: translate(0, 20%); - transform: translate(0, 20%); } + -webkit-transform: translate(0, 40px); + transform: translate(0, 40px); } .reveal .slides section .fragment.fade-up.visible { -webkit-transform: translate(0, 0); transform: translate(0, 0); } .reveal .slides section .fragment.fade-down { - -webkit-transform: translate(0, -20%); - transform: translate(0, -20%); } + -webkit-transform: translate(0, -40px); + transform: translate(0, -40px); } .reveal .slides section .fragment.fade-down.visible { -webkit-transform: translate(0, 0); transform: translate(0, 0); } .reveal .slides section .fragment.fade-right { - -webkit-transform: translate(-20%, 0); - transform: translate(-20%, 0); } + -webkit-transform: translate(-40px, 0); + transform: translate(-40px, 0); } .reveal .slides section .fragment.fade-right.visible { -webkit-transform: translate(0, 0); transform: translate(0, 0); } .reveal .slides section .fragment.fade-left { - -webkit-transform: translate(20%, 0); - transform: translate(20%, 0); } + -webkit-transform: translate(40px, 0); + transform: translate(40px, 0); } .reveal .slides section .fragment.fade-left.visible { -webkit-transform: translate(0, 0); transform: translate(0, 0); } @@ -227,7 +227,7 @@ body { bottom: 12px; right: 12px; left: auto; - z-index: 1; + z-index: 11; color: #000; pointer-events: none; font-size: 10px; } @@ -312,7 +312,8 @@ body { transform: rotate(90deg); } .reveal .controls .navigate-down { right: 3.2em; - bottom: 0; + bottom: -1.4em; + padding-bottom: 1.4em; -webkit-transform: translateY(10px); transform: translateY(10px); } .reveal .controls .navigate-down .controls-arrow { @@ -395,18 +396,18 @@ body { right: auto; } .reveal .controls[data-controls-layout="edges"] .navigate-left { top: 50%; - left: 8px; + left: 0.8em; margin-top: -1.8em; } .reveal .controls[data-controls-layout="edges"] .navigate-right { top: 50%; - right: 8px; + right: 0.8em; margin-top: -1.8em; } .reveal .controls[data-controls-layout="edges"] .navigate-up { - top: 8px; + top: 0.8em; left: 50%; margin-left: -1.8em; } .reveal .controls[data-controls-layout="edges"] .navigate-down { - bottom: 8px; + bottom: -0.3em; left: 50%; margin-left: -1.8em; } } @@ -1453,9 +1454,16 @@ body { text-align: right; vertical-align: top; } -.reveal .hljs[data-line-numbers]:not([data-line-numbers=""]) tr:not(.highlight-line) { +.reveal .hljs.has-highlights tr:not(.highlight-line) { opacity: 0.4; } +.reveal .hljs:not(:first-child).fragment { + position: absolute; + top: 0; + left: 0; + width: 100%; + box-sizing: border-box; } + /********************************************* * ROLLING LINKS *********************************************/ diff --git a/openlp/core/display/html/reveal.js b/openlp/core/display/html/reveal.js index 5c026db71..a1357a662 100644 --- a/openlp/core/display/html/reveal.js +++ b/openlp/core/display/html/reveal.js @@ -3,7 +3,7 @@ * http://revealjs.com * MIT licensed * - * Copyright (C) 2019 Hakim El Hattab, http://hakim.se + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se */ (function( root, factory ) { if( typeof define === 'function' && define.amd ) { @@ -26,14 +26,18 @@ var Reveal; // The reveal.js version - var VERSION = '3.8.0'; + var VERSION = '3.9.2'; var SLIDES_SELECTOR = '.slides section', HORIZONTAL_SLIDES_SELECTOR = '.slides>section', VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section', HOME_SLIDE_SELECTOR = '.slides>section:first-of-type', + UA = navigator.userAgent, + // Methods that may not be invoked via the postMessage API + POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/, + // Configuration defaults, can be overridden at initialization time config = { @@ -77,9 +81,9 @@ // - "c/t": Flattened slide number / total slides // // Alternatively, you can provide a function that returns the slide - // number for the current slide. The function needs to return an array - // with one string [slideNumber] or three strings [n1,delimiter,n2]. - // See #formatSlideNumber(). + // number for the current slide. The function should take in a slide + // object and return an array with one string [slideNumber] or + // three strings [n1,delimiter,n2]. See #formatSlideNumber(). slideNumber: false, // Can be used to limit the contexts in which the slide number appears @@ -270,6 +274,11 @@ // Number of slides away from the current that are visible viewDistance: 3, + // Number of slides away from the current that are visible on mobile + // devices. It is advisable to set this to a lower number than + // viewDistance in order to save resources. + mobileViewDistance: 2, + // The display mode that will be used to show slides display: 'block', @@ -447,7 +456,8 @@ */ function checkCapabilities() { - isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ); + isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ) || + ( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); var testElement = document.createElement( 'div' ); @@ -849,17 +859,10 @@ // Make sure stretch elements fit on slide layoutSlideContents( slideWidth, slideHeight ); - // Add each slide's index as attributes on itself, we need these - // indices to generate slide numbers below - toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { - hslide.setAttribute( 'data-index-h', h ); - - if( hslide.classList.contains( 'stack' ) ) { - toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) { - vslide.setAttribute( 'data-index-h', h ); - vslide.setAttribute( 'data-index-v', v ); - } ); - } + // Compute slide numbers now, before we start duplicating slides + var doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber ); + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + slide.setAttribute( 'data-slide-number', getSlideNumber( slide ) ); } ); // Slide and slide background layout @@ -930,14 +933,11 @@ } // Inject slide numbers if `slideNumbers` are enabled - if( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) { - var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1, - slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1; - + if( doingSlideNumbers ) { var numberElement = document.createElement( 'div' ); numberElement.classList.add( 'slide-number' ); numberElement.classList.add( 'slide-number-pdf' ); - numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV ); + numberElement.innerHTML = slide.getAttribute( 'data-slide-number' ); page.appendChild( numberElement ); } @@ -1217,6 +1217,8 @@ if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); + if( slide.hasAttribute( 'data-preload' ) ) element.setAttribute( 'data-preload', '' ); + // Background image options are set on the content wrapper if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize; if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat; @@ -1276,7 +1278,20 @@ // Check if the requested method can be found if( data.method && typeof Reveal[data.method] === 'function' ) { - Reveal[data.method].apply( Reveal, data.args ); + + if( POST_MESSAGE_METHOD_BLACKLIST.test( data.method ) === false ) { + + var result = Reveal[data.method].apply( Reveal, data.args ); + + // Dispatch a postMessage event with the returned value from + // our method invocation for getter functions + dispatchPostMessage( 'callback', { method: data.method, result: result } ); + + } + else { + console.warn( 'reveal.js: "'+ data.method +'" is is blacklisted from the postMessage API' ); + } + } } }, false ); @@ -1447,8 +1462,8 @@ keyboardShortcuts['↓ , J'] = 'Navigate down'; } - keyboardShortcuts['Home , ⌘/CTRL ←'] = 'First slide'; - keyboardShortcuts['End , ⌘/CTRL →'] = 'Last slide'; + keyboardShortcuts['Home , Shift ←'] = 'First slide'; + keyboardShortcuts['End , Shift →'] = 'Last slide'; keyboardShortcuts['B , .'] = 'Pause'; keyboardShortcuts['F'] = 'Fullscreen'; keyboardShortcuts['ESC, O'] = 'Slide overview'; @@ -1981,8 +1996,25 @@ // If we're in an iframe, post each reveal.js event to the // parent window. Used by the notes plugin + dispatchPostMessage( type ); + + } + + /** + * Dispatched a postMessage of the given type from our window. + */ + function dispatchPostMessage( type, data ) { + if( config.postMessageEvents && window.parent !== window.self ) { - window.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' ); + var message = { + namespace: 'reveal', + eventName: type, + state: getState() + }; + + extend( message, data ); + + window.parent.postMessage( JSON.stringify( message ), '*' ); } } @@ -2243,10 +2275,12 @@ transformSlides( { layout: '' } ); } else { - // Prefer zoom for scaling up so that content remains crisp. - // Don't use zoom to scale down since that can lead to shifts - // in text layout/line breaks. - if( scale > 1 && features.zoom ) { + // Zoom Scaling + // Content remains crisp no matter how much we scale. Side + // effects are minor differences in text layout and iframe + // viewports changing size. A 200x200 iframe viewport in a + // 2x zoomed presentation ends up having a 400x400 viewport. + if( scale > 1 && features.zoom && window.devicePixelRatio < 2 ) { dom.slides.style.zoom = scale; dom.slides.style.left = ''; dom.slides.style.top = ''; @@ -2254,7 +2288,10 @@ dom.slides.style.right = ''; transformSlides( { layout: '' } ); } - // Apply scale transform as a fallback + // Transform Scaling + // Content layout remains the exact same when scaled up. + // Side effect is content becoming blurred, especially with + // high scale values on ldpi screens. else { dom.slides.style.zoom = ''; dom.slides.style.left = '50%'; @@ -2623,34 +2660,37 @@ } /** - * Return a hash URL that will resolve to the current slide location. + * Return a hash URL that will resolve to the given slide location. + * + * @param {HTMLElement} [slide=currentSlide] The slide to link to */ - function locationHash() { + function locationHash( slide ) { var url = '/'; // Attempt to create a named link based on the slide's ID - var id = currentSlide ? currentSlide.getAttribute( 'id' ) : null; + var s = slide || currentSlide; + var id = s ? s.getAttribute( 'id' ) : null; if( id ) { id = encodeURIComponent( id ); } - var indexf; - if( config.fragmentInURL ) { - indexf = getIndices().f; + var index = getIndices( slide ); + if( !config.fragmentInURL ) { + index.f = undefined; } // If the current slide has an ID, use that as a named link, // but we don't support named links with a fragment index - if( typeof id === 'string' && id.length && indexf === undefined ) { + if( typeof id === 'string' && id.length && index.f === undefined ) { url = '/' + id; } // Otherwise use the /h/v index else { var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; - if( indexh > 0 || indexv > 0 || indexf !== undefined ) url += indexh + hashIndexBase; - if( indexv > 0 || indexf !== undefined ) url += '/' + (indexv + hashIndexBase ); - if( indexf !== undefined ) url += '/' + indexf; + if( index.h > 0 || index.v > 0 || index.f !== undefined ) url += index.h + hashIndexBase; + if( index.v > 0 || index.f !== undefined ) url += '/' + (index.v + hashIndexBase ); + if( index.f !== undefined ) url += '/' + index.f; } return url; @@ -3039,11 +3079,11 @@ syncBackground( slide ); syncFragments( slide ); + loadSlide( slide ); + updateBackground(); updateNotes(); - loadSlide( slide ); - } /** @@ -3255,9 +3295,10 @@ // be visible var viewDistance = isOverview() ? 10 : config.viewDistance; - // Limit view distance on weaker devices + // Shorten the view distance on devices that typically have + // less resources if( isMobileDevice ) { - viewDistance = isOverview() ? 6 : 2; + viewDistance = isOverview() ? 6 : config.mobileViewDistance; } // All slides need to be visible when exporting to PDF @@ -3309,7 +3350,7 @@ } // Flag if there are ANY vertical slides, anywhere in the deck - if( dom.wrapper.querySelectorAll( '.slides>section>section' ).length ) { + if( hasVerticalSlides() ) { dom.wrapper.classList.add( 'has-vertical-slides' ); } else { @@ -3317,7 +3358,7 @@ } // Flag if there are ANY horizontal slides, anywhere in the deck - if( dom.wrapper.querySelectorAll( '.slides>section' ).length > 1 ) { + if( hasHorizontalSlides() ) { dom.wrapper.classList.add( 'has-horizontal-slides' ); } else { @@ -3393,48 +3434,58 @@ // Update slide number if enabled if( config.slideNumber && dom.slideNumber ) { - - var value; - var format = 'h.v'; - - if( typeof config.slideNumber === 'function' ) { - value = config.slideNumber(); - } - else { - // Check if a custom number format is available - if( typeof config.slideNumber === 'string' ) { - format = config.slideNumber; - } - - // If there are ONLY vertical slides in this deck, always use - // a flattened slide number - if( !/c/.test( format ) && dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length === 1 ) { - format = 'c'; - } - - value = []; - switch( format ) { - case 'c': - value.push( getSlidePastCount() + 1 ); - break; - case 'c/t': - value.push( getSlidePastCount() + 1, '/', getTotalSlides() ); - break; - case 'h/v': - value.push( indexh + 1 ); - if( isVerticalSlide() ) value.push( '/', indexv + 1 ); - break; - default: - value.push( indexh + 1 ); - if( isVerticalSlide() ) value.push( '.', indexv + 1 ); - } - } - - dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] ); + dom.slideNumber.innerHTML = getSlideNumber(); } } + /** + * Returns the HTML string corresponding to the current slide number, + * including formatting. + */ + function getSlideNumber( slide ) { + + var value; + var format = 'h.v'; + if( slide === undefined ) { + slide = currentSlide; + } + + if ( typeof config.slideNumber === 'function' ) { + value = config.slideNumber( slide ); + } else { + // Check if a custom number format is available + if( typeof config.slideNumber === 'string' ) { + format = config.slideNumber; + } + + // If there are ONLY vertical slides in this deck, always use + // a flattened slide number + if( !/c/.test( format ) && dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length === 1 ) { + format = 'c'; + } + + value = []; + switch( format ) { + case 'c': + value.push( getSlidePastCount( slide ) + 1 ); + break; + case 'c/t': + value.push( getSlidePastCount( slide ) + 1, '/', getTotalSlides() ); + break; + default: + var indices = getIndices( slide ); + value.push( indices.h + 1 ); + var sep = format === 'h/v' ? '/' : '.'; + if( isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 ); + } + } + + var url = '#' + locationHash( slide ); + return formatSlideNumber( value[0], value[1], value[2], url ); + + } + /** * Applies HTML formatting to a slide number before it's * written to the DOM. @@ -3442,11 +3493,14 @@ * @param {number} a Current slide * @param {string} delimiter Character to separate slide numbers * @param {(number|*)} b Total slides + * @param {HTMLElement} [url='#'+locationHash()] The url to link to * @return {string} HTML string fragment */ - function formatSlideNumber( a, delimiter, b ) { + function formatSlideNumber( a, delimiter, b, url ) { - var url = '#' + locationHash(); + if( url === undefined ) { + url = '#' + locationHash(); + } if( typeof b === 'number' && !isNaN( b ) ) { return '' + ''+ a +'' + @@ -3599,7 +3653,7 @@ // Stop content inside of previous backgrounds if( previousBackground ) { - stopEmbeddedContent( previousBackground ); + stopEmbeddedContent( previousBackground, { unloadIframes: !shouldPreload( previousBackground ) } ); } @@ -3778,6 +3832,7 @@ background.style.display = 'block'; var backgroundContent = slide.slideBackgroundContentElement; + var backgroundIframe = slide.getAttribute( 'data-background-iframe' ); // If the background contains media, load it if( background.hasAttribute( 'data-loaded' ) === false ) { @@ -3786,8 +3841,7 @@ var backgroundImage = slide.getAttribute( 'data-background-image' ), backgroundVideo = slide.getAttribute( 'data-background-video' ), backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ), - backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ), - backgroundIframe = slide.getAttribute( 'data-background-iframe' ); + backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ); // Images if( backgroundImage ) { @@ -3827,15 +3881,9 @@ iframe.setAttribute( 'allowfullscreen', '' ); iframe.setAttribute( 'mozallowfullscreen', '' ); iframe.setAttribute( 'webkitallowfullscreen', '' ); + iframe.setAttribute( 'allow', 'autoplay' ); - // Only load autoplaying content when the slide is shown to - // avoid having it play in the background - if( /autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { - iframe.setAttribute( 'data-src', backgroundIframe ); - } - else { - iframe.setAttribute( 'src', backgroundIframe ); - } + iframe.setAttribute( 'data-src', backgroundIframe ); iframe.style.width = '100%'; iframe.style.height = '100%'; @@ -3846,6 +3894,19 @@ } } + // Start loading preloadable iframes + var backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' ); + if( backgroundIframeElement ) { + + // Check if this iframe is eligible to be preloaded + if( shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { + if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) { + backgroundIframeElement.setAttribute( 'src', backgroundIframe ); + } + } + + } + } } @@ -3865,6 +3926,11 @@ var background = getSlideBackground( slide ); if( background ) { background.style.display = 'none'; + + // Unload any background iframes + toArray( background.querySelectorAll( 'iframe[src]' ) ).forEach( function( element ) { + element.removeAttribute( 'src' ); + } ); } // Reset lazy-loaded media elements with src attributes @@ -4185,9 +4251,15 @@ * Returns the number of past slides. This can be used as a global * flattened index for slides. * + * @param {HTMLElement} [slide=currentSlide] The slide we're counting before + * * @return {number} Past slide count */ - function getSlidePastCount() { + function getSlidePastCount( slide ) { + + if( slide === undefined ) { + slide = currentSlide; + } var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); @@ -4203,7 +4275,7 @@ for( var j = 0; j < verticalSlides.length; j++ ) { // Stop as soon as we arrive at the present - if( verticalSlides[j].classList.contains( 'present' ) ) { + if( verticalSlides[j] === slide ) { break mainLoop; } @@ -4212,7 +4284,7 @@ } // Stop as soon as we arrive at the present - if( horizontalSlide.classList.contains( 'present' ) ) { + if( horizontalSlide === slide ) { break; } @@ -4429,7 +4501,44 @@ */ function getSlides() { - return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' )); + return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ) ); + + } + + /** + * Returns a list of all horizontal slides in the deck. Each + * vertical stack is included as one horizontal slide in the + * resulting array. + */ + function getHorizontalSlides() { + + return toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + } + + /** + * Returns all vertical slides that exist within this deck. + */ + function getVerticalSlides() { + + return toArray( dom.wrapper.querySelectorAll( '.slides>section>section' ) ); + + } + + /** + * Returns true if there are at least two horizontal slides. + */ + function hasHorizontalSlides() { + + return getHorizontalSlides().length > 1; + } + + /** + * Returns true if there are at least two vertical slides. + */ + function hasVerticalSlides() { + + return getVerticalSlides().length > 1; } @@ -4667,6 +4776,8 @@ if( fragments.length ) { + var maxIndex = 0; + if( typeof index !== 'number' ) { var currentFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop(); if( currentFragment ) { @@ -4680,6 +4791,8 @@ i = parseInt( el.getAttribute( 'data-fragment-index' ), 10 ); } + maxIndex = Math.max( maxIndex, i ); + // Visible fragments if( i <= index ) { if( !el.classList.contains( 'visible' ) ) changedFragments.shown.push( el ); @@ -4703,6 +4816,13 @@ } ); + // Write the current fragment index to the slide
. + // This can be used by end users to apply styles based on + // the current fragment index. + index = typeof index === 'number' ? index : -1; + index = Math.max( Math.min( index, maxIndex ), -1 ); + currentSlide.setAttribute( 'data-fragment', index ); + } } @@ -5116,8 +5236,8 @@ // Whitelist specific modified + keycode combinations var prevSlideShortcut = event.shiftKey && event.keyCode === 32; - var firstSlideShortcut = ( event.metaKey || event.ctrlKey ) && keyCode === 37; - var lastSlideShortcut = ( event.metaKey || event.ctrlKey ) && keyCode === 39; + var firstSlideShortcut = event.shiftKey && keyCode === 37; + var lastSlideShortcut = event.shiftKey && keyCode === 39; // Prevent all other events when a modifier is pressed var unusedModifier = !prevSlideShortcut && !firstSlideShortcut && !lastSlideShortcut && @@ -5144,6 +5264,10 @@ return false; } + // Use linear navigation if we're configured to OR if + // the presentation is one-dimensional + var useLinearMode = config.navigationMode === 'linear' || !hasHorizontalSlides() || !hasVerticalSlides(); + var triggered = false; // 1. User defined key bindings @@ -5216,7 +5340,7 @@ if( firstSlideShortcut ) { slide( 0 ); } - else if( !isOverview() && config.navigationMode === 'linear' ) { + else if( !isOverview() && useLinearMode ) { navigatePrev(); } else { @@ -5228,7 +5352,7 @@ if( lastSlideShortcut ) { slide( Number.MAX_VALUE ); } - else if( !isOverview() && config.navigationMode === 'linear' ) { + else if( !isOverview() && useLinearMode ) { navigateNext(); } else { @@ -5237,7 +5361,7 @@ } // K, UP else if( keyCode === 75 || keyCode === 38 ) { - if( !isOverview() && config.navigationMode === 'linear' ) { + if( !isOverview() && useLinearMode ) { navigatePrev(); } else { @@ -5246,7 +5370,7 @@ } // J, DOWN else if( keyCode === 74 || keyCode === 40 ) { - if( !isOverview() && config.navigationMode === 'linear' ) { + if( !isOverview() && useLinearMode ) { navigateNext(); } else { @@ -5356,19 +5480,49 @@ if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { touch.captured = true; - navigateLeft(); + if( config.navigationMode === 'linear' ) { + if( config.rtl ) { + navigateNext(); + } + else { + navigatePrev(); + } + } + else { + navigateLeft(); + } } else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { touch.captured = true; - navigateRight(); + if( config.navigationMode === 'linear' ) { + if( config.rtl ) { + navigatePrev(); + } + else { + navigateNext(); + } + } + else { + navigateRight(); + } } else if( deltaY > touch.threshold ) { touch.captured = true; - navigateUp(); + if( config.navigationMode === 'linear' ) { + navigatePrev(); + } + else { + navigateUp(); + } } else if( deltaY < -touch.threshold ) { touch.captured = true; - navigateDown(); + if( config.navigationMode === 'linear' ) { + navigateNext(); + } + else { + navigateDown(); + } } // If we're embedded, only block touch events if they have @@ -5905,6 +6059,15 @@ // Returns the speaker notes string for a slide, or null getSlideNotes: getSlideNotes, + // Returns an array with all horizontal/vertical slides in the deck + getHorizontalSlides: getHorizontalSlides, + getVerticalSlides: getVerticalSlides, + + // Checks if the presentation contains two or more + // horizontal/vertical slides + hasHorizontalSlides: hasHorizontalSlides, + hasVerticalSlides: hasVerticalSlides, + // Returns the previous slide element, may be null getPreviousSlide: function() { return previousSlide; diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 4bd4269dd..95e87550b 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -173,6 +173,7 @@ def init_url(plugin_name, db_file_name=None): settings.beginGroup(plugin_name) db_type = settings.value('db type') if db_type == 'sqlite': + settings.endGroup() db_url = get_db_path(plugin_name, db_file_name) else: db_url = '{type}://{user}:{password}@{host}/{db}'.format(type=db_type, @@ -180,7 +181,7 @@ def init_url(plugin_name, db_file_name=None): password=urlquote(settings.value('db password')), host=urlquote(settings.value('db hostname')), db=urlquote(settings.value('db database'))) - settings.endGroup() + settings.endGroup() return db_url