← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~alisonken1/openlp/strings-templates2 into lp:openlp

 

Ken Roberts has proposed merging lp:~alisonken1/openlp/strings-templates2 into lp:openlp.

Commit message:
Convert htmlbuilder.py to use Template() strings for css

Requested reviews:
  Tim Bentley (trb143)

For more details, see:
https://code.launchpad.net/~alisonken1/openlp/strings-templates2/+merge/296870

Convert htmlbuilder.py to use Template() strings for css

- Changed strings to use Template() instead of format() for css building
- Fix htmlbuilder strings tests
- Cleanup template variables and fix tests to match
- bug 1590386 - string format error in editbibledialog.py
- bug 1590475 - string format error in mediaitem.py
- Function call error in firsttimeform.py (missed label text variable)

--------------------------------
lp:~alisonken1/openlp/strings-templates2 (revision 2679)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1609/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1520/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1458/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/1230/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Windows_Interface_Tests/820/
[SUCCESS] https://ci.openlp.io/job/Branch-05a-Code_Analysis/888/
[SUCCESS] https://ci.openlp.io/job/Branch-05b-Test_Coverage/756/

-- 
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/htmlbuilder.py'
--- openlp/core/lib/htmlbuilder.py	2016-05-17 13:21:29 +0000
+++ openlp/core/lib/htmlbuilder.py	2016-06-08 20:47:35 +0000
@@ -390,163 +390,207 @@
 import logging
 
 from PyQt5 import QtWebKit
+from string import Template
 
 from openlp.core.common import Settings
 from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType
 
 log = logging.getLogger(__name__)
 
-# TODO: Verify where this is used before converting to python3
-HTMLSRC = """
-<!DOCTYPE html>
-<html>
-<head>
-<title>OpenLP Display</title>
-<style>
-*{
+HTML_SRC = Template("""
+    <!DOCTYPE html>
+    <html>
+    <head>
+    <title>OpenLP Display</title>
+    <style>
+    *{
+        margin: 0;
+        padding: 0;
+        border: 0;
+        overflow: hidden;
+        -webkit-user-select: none;
+    }
+    body {
+        ${bg_css};
+    }
+    .size {
+        position: absolute;
+        left: 0px;
+        top: 0px;
+        width: 100%;
+        height: 100%;
+    }
+    #black {
+        z-index: 8;
+        background-color: black;
+        display: none;
+    }
+    #bgimage {
+        z-index: 1;
+    }
+    #image {
+        z-index: 2;
+    }
+    ${css_additions}
+    #footer {
+        position: absolute;
+        z-index: 6;
+        ${footer_css}
+    }
+    /* lyric css */${lyrics_css}
+    sup {
+        font-size: 0.6em;
+        vertical-align: top;
+        position: relative;
+        top: -0.3em;
+    }
+    </style>
+    <script>
+        var timer = null;
+        var transition = ${transitions};
+        ${js_additions}
+
+        function show_image(src){
+            var img = document.getElementById('image');
+            img.src = src;
+            if(src == '')
+                img.style.display = 'none';
+            else
+                img.style.display = 'block';
+        }
+
+        function show_blank(state){
+            var black = 'none';
+            var lyrics = '';
+            switch(state){
+                case 'theme':
+                    lyrics = 'hidden';
+                    break;
+                case 'black':
+                    black = 'block';
+                    break;
+                case 'desktop':
+                    break;
+            }
+            document.getElementById('black').style.display = black;
+            document.getElementById('lyricsmain').style.visibility = lyrics;
+            document.getElementById('image').style.visibility = lyrics;
+            document.getElementById('footer').style.visibility = lyrics;
+        }
+
+        function show_footer(footertext){
+            document.getElementById('footer').innerHTML = footertext;
+        }
+
+        function show_text(new_text){
+            var match = /-webkit-text-fill-color:[^;\"]+/gi;
+            if(timer != null)
+                clearTimeout(timer);
+            /*
+            QtWebkit bug with outlines and justify causing outline alignment
+            problems. (Bug 859950) Surround each word with a <span> to workaround,
+            but only in this scenario.
+            */
+            var txt = document.getElementById('lyricsmain');
+            if(window.getComputedStyle(txt).textAlign == 'justify'){
+                if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
+                    new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
+                        function(match) {
+                            return '</span>' + match + '<span>';
+                        });
+                    new_text = '<span>' + new_text + '</span>';
+                }
+            }
+            text_fade('lyricsmain', new_text);
+        }
+
+        function text_fade(id, new_text){
+            /*
+            Show the text.
+            */
+            var text = document.getElementById(id);
+            if(text == null) return;
+            if(!transition){
+                text.innerHTML = new_text;
+                return;
+            }
+            // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
+            text.style.opacity = '0.1';
+            // Fade new text in after the old text has finished fading out.
+            timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
+        }
+
+        function _show_text(text, new_text) {
+            /*
+            Helper function to show the new_text delayed.
+            */
+            text.innerHTML = new_text;
+            text.style.opacity = '1';
+            // Wait until the text is completely visible. We want to save the timer id, to be able to call
+            // clearTimeout(timer) when the text has changed before finishing fading.
+            timer = window.setTimeout(function(){timer = null;}, 400);
+        }
+
+        function show_text_completed(){
+            return (timer == null);
+        }
+    </script>
+    </head>
+    <body>
+    <img id="bgimage" class="size" ${bg_image} />
+    <img id="image" class="size" ${image} />
+    ${html_additions}
+    <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
+    <div id="footer" class="footer"></div>
+    <div id="black" class="size"></div>
+    </body>
+    </html>
+    """)
+
+LYRICS_SRC = Template("""
+    .lyricstable {
+        z-index: 5;
+        position: absolute;
+        display: table;
+        ${stable}
+    }
+    .lyricscell {
+        display: table-cell;
+        word-wrap: break-word;
+        -webkit-transition: opacity 0.4s ease;
+        ${lyrics}
+    }
+    .lyricsmain {
+       ${main}
+    }
+    """)
+
+FOOTER_SRC = Template("""
+    left: ${left}px;
+    bottom: ${bottom}px;
+    width: ${width}px;
+    font-family: ${family};
+    font-size: ${size}pt;
+    color: ${color};
+    text-align: left;
+    white-space: ${space};
+    """)
+
+LYRICS_FORMAT_SRC = Template("""
+    ${justify}word-wrap: break-word;
+    text-align: ${align};
+    vertical-align: ${valign};
+    font-family: ${font};
+    font-size: ${size}pt;
+    color: ${color};
+    line-height: ${line}%;
     margin: 0;
     padding: 0;
-    border: 0;
-    overflow: hidden;
-    -webkit-user-select: none;
-}
-body {
-    %s;
-}
-.size {
-    position: absolute;
-    left: 0px;
-    top: 0px;
-    width: 100%%;
-    height: 100%%;
-}
-#black {
-    z-index: 8;
-    background-color: black;
-    display: none;
-}
-#bgimage {
-    z-index: 1;
-}
-#image {
-    z-index: 2;
-}
-%s
-#footer {
-    position: absolute;
-    z-index: 6;
-    %s
-}
-/* lyric css */
-%s
-sup {
-    font-size: 0.6em;
-    vertical-align: top;
-    position: relative;
-    top: -0.3em;
-}
-</style>
-<script>
-    var timer = null;
-    var transition = %s;
-    %s
-
-    function show_image(src){
-        var img = document.getElementById('image');
-        img.src = src;
-        if(src == '')
-            img.style.display = 'none';
-        else
-            img.style.display = 'block';
-    }
-
-    function show_blank(state){
-        var black = 'none';
-        var lyrics = '';
-        switch(state){
-            case 'theme':
-                lyrics = 'hidden';
-                break;
-            case 'black':
-                black = 'block';
-                break;
-            case 'desktop':
-                break;
-        }
-        document.getElementById('black').style.display = black;
-        document.getElementById('lyricsmain').style.visibility = lyrics;
-        document.getElementById('image').style.visibility = lyrics;
-        document.getElementById('footer').style.visibility = lyrics;
-    }
-
-    function show_footer(footertext){
-        document.getElementById('footer').innerHTML = footertext;
-    }
-
-    function show_text(new_text){
-        var match = /-webkit-text-fill-color:[^;\"]+/gi;
-        if(timer != null)
-            clearTimeout(timer);
-        /*
-        QtWebkit bug with outlines and justify causing outline alignment
-        problems. (Bug 859950) Surround each word with a <span> to workaround,
-        but only in this scenario.
-        */
-        var txt = document.getElementById('lyricsmain');
-        if(window.getComputedStyle(txt).textAlign == 'justify'){
-            if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
-                new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
-                    function(match) {
-                        return '</span>' + match + '<span>';
-                    });
-                new_text = '<span>' + new_text + '</span>';
-            }
-        }
-        text_fade('lyricsmain', new_text);
-    }
-
-    function text_fade(id, new_text){
-        /*
-        Show the text.
-        */
-        var text = document.getElementById(id);
-        if(text == null) return;
-        if(!transition){
-            text.innerHTML = new_text;
-            return;
-        }
-        // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
-        text.style.opacity = '0.1';
-        // Fade new text in after the old text has finished fading out.
-        timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
-    }
-
-    function _show_text(text, new_text) {
-        /*
-        Helper function to show the new_text delayed.
-        */
-        text.innerHTML = new_text;
-        text.style.opacity = '1';
-        // Wait until the text is completely visible. We want to save the timer id, to be able to call
-        // clearTimeout(timer) when the text has changed before finishing fading.
-        timer = window.setTimeout(function(){timer = null;}, 400);
-    }
-
-    function show_text_completed(){
-        return (timer == null);
-    }
-</script>
-</head>
-<body>
-<img id="bgimage" class="size" %s />
-<img id="image" class="size" %s />
-%s
-<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
-<div id="footer" class="footer"></div>
-<div id="black" class="size"></div>
-</body>
-</html>
-"""
+    padding-bottom: ${bottom};
+    padding-left: ${left}px;
+    width: ${width}px;
+    height: ${height}px;${font_style}${font_weight}
+    """)
 
 
 def build_html(item, screen, is_live, background, image=None, plugins=None):
@@ -582,18 +626,17 @@
             css_additions += plugin.get_display_css()
             js_additions += plugin.get_display_javascript()
             html_additions += plugin.get_display_html()
-    html = HTMLSRC % (
-        build_background_css(item, width),
-        css_additions,
-        build_footer_css(item, height),
-        build_lyrics_css(item),
-        'true' if theme_data and theme_data.display_slide_transition and is_live else 'false',
-        js_additions,
-        bgimage_src,
-        image_src,
-        html_additions
-    )
-    return html
+    return HTML_SRC.substitute(bg_css=build_background_css(item, width),
+                               css_additions=css_additions,
+                               footer_css=build_footer_css(item, height),
+                               lyrics_css=build_lyrics_css(item),
+                               transitions='true' if (theme_data and
+                                                      theme_data.display_slide_transition and
+                                                      is_live) else 'false',
+                               js_additions=js_additions,
+                               bg_image=bgimage_src,
+                               image=image_src,
+                               html_additions=html_additions)
 
 
 def webkit_version():
@@ -650,24 +693,6 @@
 
     :param item: Service Item containing theme and location information
     """
-    # TODO: Verify this before converting to python3
-    style = """
-.lyricstable {
-    z-index: 5;
-    position: absolute;
-    display: table;
-    %s
-}
-.lyricscell {
-    display: table-cell;
-    word-wrap: break-word;
-    -webkit-transition: opacity 0.4s ease;
-    %s
-}
-.lyricsmain {
-    %s
-}
-"""
     theme_data = item.theme_data
     lyricstable = ''
     lyrics = ''
@@ -680,8 +705,7 @@
             lyricsmain += ' text-shadow: {theme} {shadow}px ' \
                 '{shadow}px;'.format(theme=theme_data.font_main_shadow_color,
                                      shadow=theme_data.font_main_shadow_size)
-    lyrics_css = style % (lyricstable, lyrics, lyricsmain)
-    return lyrics_css
+    return LYRICS_SRC.substitute(stable=lyricstable, lyrics=lyrics, main=lyricsmain)
 
 
 def build_lyrics_outline_css(theme_data):
@@ -710,38 +734,23 @@
     """
     align = HorizontalType.Names[theme_data.display_horizontal_align]
     valign = VerticalType.Names[theme_data.display_vertical_align]
-    if theme_data.font_main_outline:
-        left_margin = int(theme_data.font_main_outline_size) * 2
-    else:
-        left_margin = 0
-    justify = 'white-space:pre-wrap;'
+    left_margin = (int(theme_data.font_main_outline_size) * 2) if theme_data.font_main_outline else 0
     # fix tag incompatibilities
-    if theme_data.display_horizontal_align == HorizontalType.Justify:
-        justify = ''
-    if theme_data.display_vertical_align == VerticalType.Bottom:
-        padding_bottom = '0.5em'
-    else:
-        padding_bottom = '0'
-    lyrics = '{justify} word-wrap: break-word; ' \
-        'text-align: {align}; vertical-align: {valign}; font-family: {font}; ' \
-        'font-size: {size}pt; color: {color}; line-height: {line:d}%; margin: 0;' \
-        'padding: 0; padding-bottom: {bottom}; padding-left: {left}px; width: {width}px; ' \
-        'height: {height}px; '.format(justify=justify,
-                                      align=align,
-                                      valign=valign,
-                                      font=theme_data.font_main_name,
-                                      size=theme_data.font_main_size,
-                                      color=theme_data.font_main_color,
-                                      line=100 + int(theme_data.font_main_line_adjustment),
-                                      bottom=padding_bottom,
-                                      left=left_margin,
-                                      width=width,
-                                      height=height)
-    if theme_data.font_main_italics:
-        lyrics += 'font-style:italic; '
-    if theme_data.font_main_bold:
-        lyrics += 'font-weight:bold; '
-    return lyrics
+    justify = '' if (theme_data.display_horizontal_align == HorizontalType.Justify) else '    white-space: pre-wrap;\n'
+    padding_bottom = '0.5em' if (theme_data.display_vertical_align == VerticalType.Bottom) else '0'
+    return LYRICS_FORMAT_SRC.substitute(justify=justify,
+                                        align=align,
+                                        valign=valign,
+                                        font=theme_data.font_main_name,
+                                        size=theme_data.font_main_size,
+                                        color=theme_data.font_main_color,
+                                        line='{line:d}'.format(line=100 + int(theme_data.font_main_line_adjustment)),
+                                        bottom=padding_bottom,
+                                        left=left_margin,
+                                        width=width,
+                                        height=height,
+                                        font_style='\n    font-style: italic;' if theme_data.font_main_italics else '',
+                                        font_weight='\n    font-weight: bold;' if theme_data.font_main_bold else '')
 
 
 def build_footer_css(item, height):
@@ -751,22 +760,11 @@
     :param item: Service Item to be processed.
     :param height:
     """
-    style = """
-    left: {left}px;
-    bottom: {bottom}px;
-    width: {width}px;
-    font-family: {family};
-    font-size: {size}pt;
-    color: {color};
-    text-align: left;
-    white-space: {space};
-    """
     theme = item.theme_data
     if not theme or not item.footer:
         return ''
     bottom = height - int(item.footer.y()) - int(item.footer.height())
     whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
-    lyrics_html = style.format(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
-                               family=theme.font_footer_name, size=theme.font_footer_size,
-                               color=theme.font_footer_color, space=whitespace)
-    return lyrics_html
+    return FOOTER_SRC.substitute(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
+                                 family=theme.font_footer_name, size=theme.font_footer_size,
+                                 color=theme.font_footer_color, space=whitespace)

=== modified file 'openlp/core/ui/firsttimeform.py'
--- openlp/core/ui/firsttimeform.py	2016-05-20 16:22:06 +0000
+++ openlp/core/ui/firsttimeform.py	2016-06-08 20:47:35 +0000
@@ -571,7 +571,7 @@
                 text = translate('OpenLP.FirstTimeWizard',
                                  'Download complete. Click the {button} button to start OpenLP.'
                                  ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
-                self.progress_label.setText()
+                self.progress_label.setText(text)
         else:
             if self.has_run_wizard:
                 text = translate('OpenLP.FirstTimeWizard',
@@ -582,7 +582,7 @@
                 text = translate('OpenLP.FirstTimeWizard',
                                  'Click the {button} button to start OpenLP.'
                                  ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
-                self.progress_label.setText()
+                self.progress_label.setText(text)
         self.finish_button.setVisible(True)
         self.finish_button.setEnabled(True)
         self.cancel_button.setVisible(False)

=== modified file 'openlp/plugins/bibles/forms/editbibledialog.py'
--- openlp/plugins/bibles/forms/editbibledialog.py	2016-05-21 08:31:24 +0000
+++ openlp/plugins/bibles/forms/editbibledialog.py	2016-06-08 20:47:35 +0000
@@ -104,7 +104,7 @@
         for book in BiblesResourcesDB.get_books():
             self.book_name_label[book['abbreviation']] = QtWidgets.QLabel(self.book_name_widget)
             self.book_name_label[book['abbreviation']].setObjectName(
-                'book_name_label[{name}]'.format(book=book['abbreviation']))
+                'book_name_label[{book}]'.format(book=book['abbreviation']))
             self.book_name_edit[book['abbreviation']] = QtWidgets.QLineEdit(self.book_name_widget)
             self.book_name_edit[book['abbreviation']].setObjectName(
                 'book_name_edit[{name}]'.format(name=book['abbreviation']))

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2016-05-27 08:13:14 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2016-06-08 20:47:35 +0000
@@ -603,7 +603,7 @@
                         else:
                             verse_index = VerseType.from_tag(verse[0]['type'])
                         verse_tag = VerseType.translated_tags[verse_index]
-                        verse_def = '{tag}{label}'.format(tzg=verse_tag, text=verse[0]['label'])
+                        verse_def = '{tag}{text}'.format(tag=verse_tag, text=verse[0]['label'])
                         service_item.add_from_text(verse[1], verse_def)
         service_item.title = song.title
         author_list = self.generate_footer(service_item, song)

=== modified file 'tests/functional/openlp_core_lib/test_htmlbuilder.py'
--- tests/functional/openlp_core_lib/test_htmlbuilder.py	2016-05-31 21:40:13 +0000
+++ tests/functional/openlp_core_lib/test_htmlbuilder.py	2016-06-08 20:47:35 +0000
@@ -14,177 +14,190 @@
 from tests.helpers.testmixin import TestMixin
 
 HTML = """
-<!DOCTYPE html>
-<html>
-<head>
-<title>OpenLP Display</title>
-<style>
-*{
-    margin: 0;
-    padding: 0;
-    border: 0;
-    overflow: hidden;
-    -webkit-user-select: none;
-}
-body {
-    ;
-}
-.size {
-    position: absolute;
-    left: 0px;
-    top: 0px;
-    width: 100%;
-    height: 100%;
-}
-#black {
-    z-index: 8;
-    background-color: black;
-    display: none;
-}
-#bgimage {
-    z-index: 1;
-}
-#image {
-    z-index: 2;
-}
-plugin CSS
-#footer {
-    position: absolute;
-    z-index: 6;
-    dummy: dummy;
-}
-/* lyric css */
-
-sup {
-    font-size: 0.6em;
-    vertical-align: top;
-    position: relative;
-    top: -0.3em;
-}
-</style>
-<script>
-    var timer = null;
-    var transition = false;
-    plugin JS
-
-    function show_image(src){
-        var img = document.getElementById('image');
-        img.src = src;
-        if(src == '')
-            img.style.display = 'none';
-        else
-            img.style.display = 'block';
-    }
-
-    function show_blank(state){
-        var black = 'none';
-        var lyrics = '';
-        switch(state){
-            case 'theme':
-                lyrics = 'hidden';
-                break;
-            case 'black':
-                black = 'block';
-                break;
-            case 'desktop':
-                break;
-        }
-        document.getElementById('black').style.display = black;
-        document.getElementById('lyricsmain').style.visibility = lyrics;
-        document.getElementById('image').style.visibility = lyrics;
-        document.getElementById('footer').style.visibility = lyrics;
-    }
-
-    function show_footer(footertext){
-        document.getElementById('footer').innerHTML = footertext;
-    }
-
-    function show_text(new_text){
-        var match = /-webkit-text-fill-color:[^;"]+/gi;
-        if(timer != null)
-            clearTimeout(timer);
-        /*
-        QtWebkit bug with outlines and justify causing outline alignment
-        problems. (Bug 859950) Surround each word with a <span> to workaround,
-        but only in this scenario.
-        */
-        var txt = document.getElementById('lyricsmain');
-        if(window.getComputedStyle(txt).textAlign == 'justify'){
-            if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
-                new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
-                    function(match) {
-                        return '</span>' + match + '<span>';
-                    });
-                new_text = '<span>' + new_text + '</span>';
-            }
-        }
-        text_fade('lyricsmain', new_text);
-    }
-
-    function text_fade(id, new_text){
-        /*
-        Show the text.
-        */
-        var text = document.getElementById(id);
-        if(text == null) return;
-        if(!transition){
+    <!DOCTYPE html>
+    <html>
+    <head>
+    <title>OpenLP Display</title>
+    <style>
+    *{
+        margin: 0;
+        padding: 0;
+        border: 0;
+        overflow: hidden;
+        -webkit-user-select: none;
+    }
+    body {
+        ;
+    }
+    .size {
+        position: absolute;
+        left: 0px;
+        top: 0px;
+        width: 100%;
+        height: 100%;
+    }
+    #black {
+        z-index: 8;
+        background-color: black;
+        display: none;
+    }
+    #bgimage {
+        z-index: 1;
+    }
+    #image {
+        z-index: 2;
+    }
+    plugin CSS
+    #footer {
+        position: absolute;
+        z-index: 6;
+        dummy: dummy;
+    }
+    /* lyric css */
+    sup {
+        font-size: 0.6em;
+        vertical-align: top;
+        position: relative;
+        top: -0.3em;
+    }
+    </style>
+    <script>
+        var timer = null;
+        var transition = false;
+        plugin JS
+
+        function show_image(src){
+            var img = document.getElementById('image');
+            img.src = src;
+            if(src == '')
+                img.style.display = 'none';
+            else
+                img.style.display = 'block';
+        }
+
+        function show_blank(state){
+            var black = 'none';
+            var lyrics = '';
+            switch(state){
+                case 'theme':
+                    lyrics = 'hidden';
+                    break;
+                case 'black':
+                    black = 'block';
+                    break;
+                case 'desktop':
+                    break;
+            }
+            document.getElementById('black').style.display = black;
+            document.getElementById('lyricsmain').style.visibility = lyrics;
+            document.getElementById('image').style.visibility = lyrics;
+            document.getElementById('footer').style.visibility = lyrics;
+        }
+
+        function show_footer(footertext){
+            document.getElementById('footer').innerHTML = footertext;
+        }
+
+        function show_text(new_text){
+            var match = /-webkit-text-fill-color:[^;"]+/gi;
+            if(timer != null)
+                clearTimeout(timer);
+            /*
+            QtWebkit bug with outlines and justify causing outline alignment
+            problems. (Bug 859950) Surround each word with a <span> to workaround,
+            but only in this scenario.
+            */
+            var txt = document.getElementById('lyricsmain');
+            if(window.getComputedStyle(txt).textAlign == 'justify'){
+                if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
+                    new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
+                        function(match) {
+                            return '</span>' + match + '<span>';
+                        });
+                    new_text = '<span>' + new_text + '</span>';
+                }
+            }
+            text_fade('lyricsmain', new_text);
+        }
+
+        function text_fade(id, new_text){
+            /*
+            Show the text.
+            */
+            var text = document.getElementById(id);
+            if(text == null) return;
+            if(!transition){
+                text.innerHTML = new_text;
+                return;
+            }
+            // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
+            text.style.opacity = '0.1';
+            // Fade new text in after the old text has finished fading out.
+            timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
+        }
+
+        function _show_text(text, new_text) {
+            /*
+            Helper function to show the new_text delayed.
+            */
             text.innerHTML = new_text;
-            return;
-        }
-        // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
-        text.style.opacity = '0.1';
-        // Fade new text in after the old text has finished fading out.
-        timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
-    }
-
-    function _show_text(text, new_text) {
-        /*
-        Helper function to show the new_text delayed.
-        */
-        text.innerHTML = new_text;
-        text.style.opacity = '1';
-        // Wait until the text is completely visible. We want to save the timer id, to be able to call
-        // clearTimeout(timer) when the text has changed before finishing fading.
-        timer = window.setTimeout(function(){timer = null;}, 400);
-    }
-
-    function show_text_completed(){
-        return (timer == null);
-    }
-</script>
-</head>
-<body>
-<img id="bgimage" class="size" style="display:none;" />
-<img id="image" class="size" style="display:none;" />
-plugin HTML
-<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
-<div id="footer" class="footer"></div>
-<div id="black" class="size"></div>
-</body>
-</html>
-"""
+            text.style.opacity = '1';
+            // Wait until the text is completely visible. We want to save the timer id, to be able to call
+            // clearTimeout(timer) when the text has changed before finishing fading.
+            timer = window.setTimeout(function(){timer = null;}, 400);
+        }
+
+        function show_text_completed(){
+            return (timer == null);
+        }
+    </script>
+    </head>
+    <body>
+    <img id="bgimage" class="size" style="display:none;" />
+    <img id="image" class="size" style="display:none;" />
+    plugin HTML
+    <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
+    <div id="footer" class="footer"></div>
+    <div id="black" class="size"></div>
+    </body>
+    </html>
+    """
 BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed'
 LYRICS_CSS = """
-.lyricstable {
-    z-index: 5;
-    position: absolute;
-    display: table;
-    left: 10px; top: 20px;
-}
-.lyricscell {
-    display: table-cell;
+    .lyricstable {
+        z-index: 5;
+        position: absolute;
+        display: table;
+        left: 10px; top: 20px;
+    }
+    .lyricscell {
+        display: table-cell;
+        word-wrap: break-word;
+        -webkit-transition: opacity 0.4s ease;
+        lyrics_format_css
+    }
+    .lyricsmain {
+        text-shadow: #000000 5px 5px;
+    }
+    """
+LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; '
+LYRICS_FORMAT_CSS = """
     word-wrap: break-word;
-    -webkit-transition: opacity 0.4s ease;
-    lyrics_format_css
-}
-.lyricsmain {
-     text-shadow: #000000 5px 5px;
-}
-"""
-LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; '
-LYRICS_FORMAT_CSS = ' word-wrap: break-word; text-align: justify; vertical-align: bottom; ' + \
-    'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 108%; margin: 0;padding: 0; ' + \
-    'padding-bottom: 0.5em; padding-left: 2px; width: 1580px; height: 810px; font-style:italic; font-weight:bold; '
+    text-align: justify;
+    vertical-align: bottom;
+    font-family: Arial;
+    font-size: 40pt;
+    color: #FFFFFF;
+    line-height: 108%;
+    margin: 0;
+    padding: 0;
+    padding-bottom: 0.5em;
+    padding-left: 2px;
+    width: 1580px;
+    height: 810px;
+    font-style: italic;
+    font-weight: bold;
+    """
 FOOTER_CSS_BASE = """
     left: 10px;
     bottom: 0px;


Follow ups