← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~raoul-snyman/openlp/website into lp:openlp/website

 

Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/website into lp:openlp/website.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/website/+merge/274794

Added translation post and screenshots
-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~raoul-snyman/openlp/website into lp:openlp/website.
=== added file 'blog/openlp-22-released.rst'
--- blog/openlp-22-released.rst	1970-01-01 00:00:00 +0000
+++ blog/openlp-22-released.rst	2015-10-16 22:17:24 +0000
@@ -0,0 +1,138 @@
+.. title: OpenLP 2.2 Released!
+.. slug: 2015/10/18/openlp-22-released
+.. date: 2015-10-18 12:00:00 UTC
+.. tags:
+.. category:
+.. link:
+.. description:
+.. type: text
+.. previewimage: /cover-images/openlp-22-released.jpg
+
+It's finally here! After almost three years in development, the next major version of OpenLP has arrived!
+
+A lot of work has gone into making this new version more stable, less buggy and easier to use, resulting almost 300
+bugfixes and improvements. This version also sees some changes under the hood, in terms of moving to Python 3 and adding
+unit testing to increase the quality of the codebase.
+
+New Features
+^^^^^^^^^^^^
+
+But, without further ado, here are the new features you'll find in OpenLP 2.2:
+
+Songs
+-----
+
+* Show active song title in preview and live pane
+* Allow authors to be tagged by their type (words music etc)
+* CCLI SongSelect integration
+* Importers
+
+  * ProPresenter 4
+  * Worship Assistant
+  * PowerPraise
+  * PresentationManager
+  * EasyWorship Service Files (.ews)
+  * WorshipCenter Pro
+
+* Songbook can be displayed in footer
+* Duplicate Songs can be identified and removed manually if required.
+* An improve Formatting Tags user interface
+
+Bibles
+------
+
+* New web bible: Neue evangelistische Übersetzung (German)
+* Importers
+
+  * Zefania XML format
+  * OSIS importer rewritten
+
+Presentations
+-------------
+
+* Support for presentations created in PDF format
+* Support for LibreOffice 4 and 5
+
+Projector Remote Control
+------------------------
+
+* Projectors that support the PJLink protocol can be controlled from OpenLP (On/Off/Blank/Source Select)
+
+Images
+------
+
+* Can be placed in groups and added to services as groups.
+
+Remote
+------
+
+* Supports SSL for all requests. Certificates will need to be created outside of OpenLP
+* Supports Authentication for all update requests
+* Display thumbnails and notes when available in the Web User Interface
+
+Custom
+------
+
+* Auto load items from saved services
+* Items can be created from the Service Manager for items like bibles
+
+Media
+-----
+
+* Support for DVD and CD clipselection and playback (requires VLC).
+
+Services
+--------
+
+* Items can be renamed
+* Custom Items generated from Text items like bibles
+* Media items can be configured to autoload and remove screen blank when added to live display
+* Services can be saved without embedding media files. This will limit the portability of the files but reduces the file size (useful when media files are part of the service).
+
+Internals
+---------
+
+* Upgraded the code to Python 3 from Python 2
+* Refactored and improved the internals to:
+
+  * Improve performance and start faster
+  * Improve reliability of the media functionality
+  * Increase testability and start to introduce a comprehensive test suite
+  * Improve consistency of the settings
+
+Important Notes
+^^^^^^^^^^^^^^^
+
+**OpenLP 2.2 is not backwards compatible with 2.0.5.**
+
+**Service Files created in 2.2 cannot be opened in 2.0.**
+
+Once you have upgraded from 2.0.5, version 2.0.5 cannot read the upgraded files. OpenLP 2.2 will make a backup on
+startup, but it is recommended that you make your own backup too.
+
+Upgrading from versions of OpenLP before 2.0.5 are not supported. You need to upgrade to 2.0.5 first, and make sure
+you run OpenLP at least once before continuing to upgrade to 2.2.
+
+There are some known issues in OpenLP 2.2 that you should know about before upgrading:
+
+1. Live Backgrounds does not work on Windows and Mac OS X
+2. Playback of linked audio does not work on Mac OS X
+
+We plan to fix these issues in OpenLP 2.4. We're moving to a faster release cycle, which means that OpenLP 2.4 should be released in 6 to 12 months from now.
+
+Thanks
+^^^^^^
+
+As leader of the project, I'd like to say a special thank you to the folks involved in this release. A special thank you to Tomas Groth, Jonathan Springer and Tim Bentley for their help, encouragement and preseverence.
+
+    So he got up and went to his father. But while he was still a long way off, his father saw him and was filled with compassion for him; he ran to his son, threw his arms around him and kissed him.
+
+    Luke 15:20, New International Version
+
+.. raw:: html
+
+    <div class="text-center"><p><a class="btn btn-success btn-lg" href="/#download"><strong>Download Now</strong></a></p></div>
+
+[ Image Credit: `Thai Lanterns by Mark Fischer`_ ]
+
+.. _Thai Lanterns by Mark Fischer: https://www.flickr.com/photos/fischerfotos/7455424224/

=== added file 'blog/want-openlp-your-language.rst'
--- blog/want-openlp-your-language.rst	1970-01-01 00:00:00 +0000
+++ blog/want-openlp-your-language.rst	2015-10-16 22:17:24 +0000
@@ -0,0 +1,100 @@
+.. title: Want OpenLP in Your Language?
+.. slug: want-openlp-your-language
+.. date: 2015-10-03 01:17:00 UTC
+.. tags:
+.. category:
+.. link:
+.. description:
+.. type: text
+.. previewimage: /cover-images/want-openlp-your-language.jpg
+
+If you'd like to see OpenLP in your language, now's the time! We are about 2 weeks away from the release of 2.2, and
+now is your opportunity to translate OpenLP's interface into your own language.
+
+In order for your langauge to be included you need to have completed the `translation of OpenLP 2.2`_ (you'll need to be
+logged into `Transifex`_ to see this page) **by midnight UTC (GMT) on Saturday the 10th of October 2015**.
+
+Because we believe strongly in quality over quantity at OpenLP, we will not be including any translations that are
+incomplete into the final build. **This means that if you want your language to be part of the final release of version
+2.2, you need to make sure it is 100% done by the end of Saturday the 10th of October**. This deadline also gives you
+just over a week to complete any outstanding translations.
+
+Languages Needing Work
+^^^^^^^^^^^^^^^^^^^^^^
+
+Currently, the following translations are more than 80% complete, but still need some work (ordered from most complete
+to least complete). My estimate is that none of these should take longer than about 2 hours to complete.
+
+*  Polish
+*  French
+*  Lithuanian
+*  Russian
+*  Portuguese (Brazil)
+*  Bulgarian
+
+The following translations are between 40% and 80% complete. Unless these are worked on this week, they probably won't
+make it into the final release of OpenLP.
+
+*  Thai (Thailand)
+*  Tamil (Sri-Lanka)
+*  Greek
+*  Afrikaans
+*  Chinese (China)
+*  Korean
+
+Lastly, the following translations are less than 40% complete. If any of these translations makes it into the final
+release, I will be very surprised.
+
+*  Slovenian
+*  Italian
+*  Macedonian
+*  Ukrainian
+*  Norweigian Nyorsk
+*  Papiamento
+*  Turkish
+*  Malayalam
+*  Latvian
+*  Spanish (Colombia)
+*  Arabic
+*  Albanian
+*  Korean (Korea)
+*  Ukranian (Ukraine)
+*  Khmer (Cambodia)
+*  Russian (Russia)
+*  Amharic
+*  English (United States)
+*  Klingon
+*  Arabic (Egypt)
+*  Spanish (Chile)
+*  Vietnamese (Viet Nam)
+*  Vietnamese
+
+Get started `translating OpenLP 2.2`_ (you'll need to be logged into `Transifex`_ to see this page).
+
+Testing Out Your Translation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To test your translation out, do the following:
+
+1.  Make sure you are running version 2.1.6
+2.  Install Qt Linguist
+3.  Windows and OS X users need to `download Qt Linguist`_
+4.  Ubuntu, Fedora, and other Linux and Unix users can install Qt Linguist from their package manager
+5.  Download the language file from Transifex
+6.  Open it in Qt Linguist
+7.  Open the File menu, select "*Release*" and save the resulting release file with only your language code as the file name (see Transifex for your language code)
+8.  Copy the release file to OpenLP's i18n directory
+9.  Windows: ``C:\Program Files\OpenLP\i18n or C:\Program Files (x86)\OpenLP\i18n``
+10.  Mac OS X: ``/Applications/OpenLP.app/Contents/MacOS/i18n/``
+11.  Linux: ``/usr/share/openlp/i18n/``
+12.  \*BSD: ``/usr/local/share/openlp/i18n/``
+13.  (Re)start Openlp and select your language
+
+[ Image Credit: `Arddangosfa o gelf Zimbabwe yn newby Hall, Ripon`_ ]
+
+.. _translation of OpenLP 2.2: https://www.transifex.com/openlp/openlp/openlp-22x/
+.. _Transifex: https://www.transifex.com/
+.. _translating OpenLP 2.2: https://www.transifex.com/openlp/openlp/openlp-22x/
+.. _download Qt Linguist: http://code.google.com/p/qtlinguistdownload/downloads/list
+.. _Arddangosfa o gelf Zimbabwe yn newby Hall, Ripon: https://www.flickr.com/photos/benbore/2800370449/
+

=== modified file 'conf.py'
--- conf.py	2015-10-06 22:17:22 +0000
+++ conf.py	2015-10-16 22:17:24 +0000
@@ -677,7 +677,8 @@
         #<link href="css/custom.css" rel="stylesheet">
 #"""
 EXTRA_HEAD_DATA = """
-<script src='https://www.google.com/recaptcha/api.js'></script>
+    <link href="/assets/css/ekko-lightbox.css" rel="stylesheet">
+    <script src='//www.google.com/recaptcha/api.js'></script>
 """
 
 # Google Analytics or whatever else you use. Added to the bottom of <body>
@@ -691,6 +692,9 @@
         #<script type="text/javascript" src="js/template.js"></script>
         #<script type="text/javascript" src="js/custom.js"></script>
 #"""
+BODY_END = """
+    <script type="text/javascript" src="/assets/js/ekko-lightbox.js"></script>
+"""
 
 # The possibility to extract metadata from the filename by using a
 # regular expression.

=== added file 'files/cover-images/openlp-22-released.jpg'
Binary files files/cover-images/openlp-22-released.jpg	1970-01-01 00:00:00 +0000 and files/cover-images/openlp-22-released.jpg	2015-10-16 22:17:24 +0000 differ
=== removed file 'files/cover-images/want-openlp-in-your-language.jpg'
Binary files files/cover-images/want-openlp-in-your-language.jpg	2015-10-14 20:20:09 +0000 and files/cover-images/want-openlp-in-your-language.jpg	1970-01-01 00:00:00 +0000 differ
=== added file 'files/cover-images/want-openlp-your-language.jpg'
Binary files files/cover-images/want-openlp-your-language.jpg	1970-01-01 00:00:00 +0000 and files/cover-images/want-openlp-your-language.jpg	2015-10-16 22:17:24 +0000 differ
=== added directory 'files/files/ftw'
=== added file 'files/files/ftw/do_not_delete.txt'
--- files/files/ftw/do_not_delete.txt	1970-01-01 00:00:00 +0000
+++ files/files/ftw/do_not_delete.txt	2015-10-16 22:17:24 +0000
@@ -0,0 +1,1 @@
+Don't delete this directory! It's used to store the download.cfg file for the First Time Wizard.
\ No newline at end of file

=== added directory 'files/screenshots'
=== added file 'files/screenshots/configuregeneral.png'
Binary files files/screenshots/configuregeneral.png	1970-01-01 00:00:00 +0000 and files/screenshots/configuregeneral.png	2015-10-16 22:17:24 +0000 differ
=== added file 'files/screenshots/mainwindow.png'
Binary files files/screenshots/mainwindow.png	1970-01-01 00:00:00 +0000 and files/screenshots/mainwindow.png	2015-10-16 22:17:24 +0000 differ
=== added file 'files/screenshots/song_edit_verse_in_use.png'
Binary files files/screenshots/song_edit_verse_in_use.png	1970-01-01 00:00:00 +0000 and files/screenshots/song_edit_verse_in_use.png	2015-10-16 22:17:24 +0000 differ
=== added file 'files/screenshots/songimporterchoices.png'
Binary files files/screenshots/songimporterchoices.png	1970-01-01 00:00:00 +0000 and files/screenshots/songimporterchoices.png	2015-10-16 22:17:24 +0000 differ
=== added file 'files/screenshots/stage_view_song_w_note.png'
Binary files files/screenshots/stage_view_song_w_note.png	1970-01-01 00:00:00 +0000 and files/screenshots/stage_view_song_w_note.png	2015-10-16 22:17:24 +0000 differ
=== modified file 'themes/openlp2v2/assets/css/custom.css'
--- themes/openlp2v2/assets/css/custom.css	2015-10-15 21:08:29 +0000
+++ themes/openlp2v2/assets/css/custom.css	2015-10-16 22:17:24 +0000
@@ -318,3 +318,9 @@
 p.indent {
     margin-left: 3em;
 }
+
+a.screenshot-gallery {
+    display: block;
+    border-radius: 6px;
+    border: 4px solid #efefef;
+}

=== added file 'themes/openlp2v2/assets/css/ekko-lightbox.css'
--- themes/openlp2v2/assets/css/ekko-lightbox.css	1970-01-01 00:00:00 +0000
+++ themes/openlp2v2/assets/css/ekko-lightbox.css	2015-10-16 22:17:24 +0000
@@ -0,0 +1,62 @@
+/*!
+ * Lightbox for Bootstrap 3 by @ashleydw
+ * https://github.com/ashleydw/lightbox
+ *
+ * License: https://github.com/ashleydw/lightbox/blob/master/LICENSE
+ */
+
+.ekko-lightbox-container {
+  position: relative;
+}
+
+.ekko-lightbox-nav-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 100;
+  width: 100%;
+  height: 100%;
+}
+
+.ekko-lightbox-nav-overlay a {
+  z-index: 100;
+  display: block;
+  width: 49%;
+  height: 100%;
+  font-size: 30px;
+  color: #fff;
+  text-shadow: 2px 2px 4px #000;
+  opacity: 0;
+  filter: dropshadow(color=#000000, offx=2, offy=2);
+  -webkit-transition: opacity 0.5s;
+     -moz-transition: opacity 0.5s;
+       -o-transition: opacity 0.5s;
+          transition: opacity 0.5s;
+}
+
+.ekko-lightbox-nav-overlay a:empty {
+  width: 49%;
+}
+
+.ekko-lightbox a:hover {
+  text-decoration: none;
+  opacity: 1;
+}
+
+.ekko-lightbox .glyphicon-chevron-left {
+  left: 0;
+  float: left;
+  padding-left: 15px;
+  text-align: left;
+}
+
+.ekko-lightbox .glyphicon-chevron-right {
+  right: 0;
+  float: right;
+  padding-right: 15px;
+  text-align: right;
+}
+
+.ekko-lightbox .modal-footer {
+  text-align: left;
+}
\ No newline at end of file

=== modified file 'themes/openlp2v2/assets/js/custom.js'
--- themes/openlp2v2/assets/js/custom.js	2015-10-06 22:17:22 +0000
+++ themes/openlp2v2/assets/js/custom.js	2015-10-16 22:17:24 +0000
@@ -33,4 +33,8 @@
             $("#error-alert").removeClass("hidden");
         }
     }
+    $(document).delegate('*[data-toggle="lightbox"]', 'click', function(event) {
+        event.preventDefault();
+        $(this).ekkoLightbox();
+    }); 
 });

=== added file 'themes/openlp2v2/assets/js/ekko-lightbox.js'
--- themes/openlp2v2/assets/js/ekko-lightbox.js	1970-01-01 00:00:00 +0000
+++ themes/openlp2v2/assets/js/ekko-lightbox.js	2015-10-16 22:17:24 +0000
@@ -0,0 +1,400 @@
+/*
+Lightbox for Bootstrap 3 by @ashleydw
+https://github.com/ashleydw/lightbox
+
+License: https://github.com/ashleydw/lightbox/blob/master/LICENSE
+*/
+
+
+(function() {
+  "use strict";
+  var $, EkkoLightbox;
+
+  $ = jQuery;
+
+  EkkoLightbox = function(element, options) {
+    var content, footer, header,
+      _this = this;
+    this.options = $.extend({
+      title: null,
+      footer: null,
+      remote: null
+    }, $.fn.ekkoLightbox.defaults, options || {});
+    this.$element = $(element);
+    content = '';
+    this.modal_id = this.options.modal_id ? this.options.modal_id : 'ekkoLightbox-' + Math.floor((Math.random() * 1000) + 1);
+    header = '<div class="modal-header"' + (this.options.title || this.options.always_show_close ? '' : ' style="display:none"') + '><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button><h4 class="modal-title">' + (this.options.title || "&nbsp;") + '</h4></div>';
+    footer = '<div class="modal-footer"' + (this.options.footer ? '' : ' style="display:none"') + '>' + this.options.footer + '</div>';
+    $(document.body).append('<div id="' + this.modal_id + '" class="ekko-lightbox modal fade" tabindex="-1"><div class="modal-dialog"><div class="modal-content">' + header + '<div class="modal-body"><div class="ekko-lightbox-container"><div></div></div></div>' + footer + '</div></div></div>');
+    this.modal = $('#' + this.modal_id);
+    this.modal_dialog = this.modal.find('.modal-dialog').first();
+    this.modal_content = this.modal.find('.modal-content').first();
+    this.modal_body = this.modal.find('.modal-body').first();
+    this.lightbox_container = this.modal_body.find('.ekko-lightbox-container').first();
+    this.lightbox_body = this.lightbox_container.find('> div:first-child').first();
+    this.showLoading();
+    this.modal_arrows = null;
+    this.border = {
+      top: parseFloat(this.modal_dialog.css('border-top-width')) + parseFloat(this.modal_content.css('border-top-width')) + parseFloat(this.modal_body.css('border-top-width')),
+      right: parseFloat(this.modal_dialog.css('border-right-width')) + parseFloat(this.modal_content.css('border-right-width')) + parseFloat(this.modal_body.css('border-right-width')),
+      bottom: parseFloat(this.modal_dialog.css('border-bottom-width')) + parseFloat(this.modal_content.css('border-bottom-width')) + parseFloat(this.modal_body.css('border-bottom-width')),
+      left: parseFloat(this.modal_dialog.css('border-left-width')) + parseFloat(this.modal_content.css('border-left-width')) + parseFloat(this.modal_body.css('border-left-width'))
+    };
+    this.padding = {
+      top: parseFloat(this.modal_dialog.css('padding-top')) + parseFloat(this.modal_content.css('padding-top')) + parseFloat(this.modal_body.css('padding-top')),
+      right: parseFloat(this.modal_dialog.css('padding-right')) + parseFloat(this.modal_content.css('padding-right')) + parseFloat(this.modal_body.css('padding-right')),
+      bottom: parseFloat(this.modal_dialog.css('padding-bottom')) + parseFloat(this.modal_content.css('padding-bottom')) + parseFloat(this.modal_body.css('padding-bottom')),
+      left: parseFloat(this.modal_dialog.css('padding-left')) + parseFloat(this.modal_content.css('padding-left')) + parseFloat(this.modal_body.css('padding-left'))
+    };
+    this.modal.on('show.bs.modal', this.options.onShow.bind(this)).on('shown.bs.modal', function() {
+      _this.modal_shown();
+      return _this.options.onShown.call(_this);
+    }).on('hide.bs.modal', this.options.onHide.bind(this)).on('hidden.bs.modal', function() {
+      if (_this.gallery) {
+        $(document).off('keydown.ekkoLightbox');
+      }
+      _this.modal.remove();
+      return _this.options.onHidden.call(_this);
+    }).modal('show', options);
+    return this.modal;
+  };
+
+  EkkoLightbox.prototype = {
+    modal_shown: function() {
+      var video_id,
+        _this = this;
+      if (!this.options.remote) {
+        return this.error('No remote target given');
+      } else {
+        this.gallery = this.$element.data('gallery');
+        if (this.gallery) {
+          if (this.options.gallery_parent_selector === 'document.body' || this.options.gallery_parent_selector === '') {
+            this.gallery_items = $(document.body).find('*[data-toggle="lightbox"][data-gallery="' + this.gallery + '"]');
+          } else {
+            this.gallery_items = this.$element.parents(this.options.gallery_parent_selector).first().find('*[data-toggle="lightbox"][data-gallery="' + this.gallery + '"]');
+          }
+          this.gallery_index = this.gallery_items.index(this.$element);
+          $(document).on('keydown.ekkoLightbox', this.navigate.bind(this));
+          if (this.options.directional_arrows && this.gallery_items.length > 1) {
+            this.lightbox_container.append('<div class="ekko-lightbox-nav-overlay"><a href="#" class="' + this.strip_stops(this.options.left_arrow_class) + '"></a><a href="#" class="' + this.strip_stops(this.options.right_arrow_class) + '"></a></div>');
+            this.modal_arrows = this.lightbox_container.find('div.ekko-lightbox-nav-overlay').first();
+            this.lightbox_container.find('a' + this.strip_spaces(this.options.left_arrow_class)).on('click', function(event) {
+              event.preventDefault();
+              return _this.navigate_left();
+            });
+            this.lightbox_container.find('a' + this.strip_spaces(this.options.right_arrow_class)).on('click', function(event) {
+              event.preventDefault();
+              return _this.navigate_right();
+            });
+          }
+        }
+        if (this.options.type) {
+          if (this.options.type === 'image') {
+            return this.preloadImage(this.options.remote, true);
+          } else if (this.options.type === 'youtube' && (video_id = this.getYoutubeId(this.options.remote))) {
+            return this.showYoutubeVideo(video_id);
+          } else if (this.options.type === 'vimeo') {
+            return this.showVimeoVideo(this.options.remote);
+          } else if (this.options.type === 'instagram') {
+            return this.showInstagramVideo(this.options.remote);
+          } else if (this.options.type === 'url') {
+            return this.loadRemoteContent(this.options.remote);
+          } else if (this.options.type === 'video') {
+            return this.showVideoIframe(this.options.remote);
+          } else {
+            return this.error("Could not detect remote target type. Force the type using data-type=\"image|youtube|vimeo|instagram|url|video\"");
+          }
+        } else {
+          return this.detectRemoteType(this.options.remote);
+        }
+      }
+    },
+    strip_stops: function(str) {
+      return str.replace(/\./g, '');
+    },
+    strip_spaces: function(str) {
+      return str.replace(/\s/g, '');
+    },
+    isImage: function(str) {
+      return str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i);
+    },
+    isSwf: function(str) {
+      return str.match(/\.(swf)((\?|#).*)?$/i);
+    },
+    getYoutubeId: function(str) {
+      var match;
+      match = str.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/);
+      if (match && match[2].length === 11) {
+        return match[2];
+      } else {
+        return false;
+      }
+    },
+    getVimeoId: function(str) {
+      if (str.indexOf('vimeo') > 0) {
+        return str;
+      } else {
+        return false;
+      }
+    },
+    getInstagramId: function(str) {
+      if (str.indexOf('instagram') > 0) {
+        return str;
+      } else {
+        return false;
+      }
+    },
+    navigate: function(event) {
+      event = event || window.event;
+      if (event.keyCode === 39 || event.keyCode === 37) {
+        if (event.keyCode === 39) {
+          return this.navigate_right();
+        } else if (event.keyCode === 37) {
+          return this.navigate_left();
+        }
+      }
+    },
+    navigateTo: function(index) {
+      var next, src;
+      if (index < 0 || index > this.gallery_items.length - 1) {
+        return this;
+      }
+      this.showLoading();
+      this.gallery_index = index;
+      this.$element = $(this.gallery_items.get(this.gallery_index));
+      this.updateTitleAndFooter();
+      src = this.$element.attr('data-remote') || this.$element.attr('href');
+      this.detectRemoteType(src, this.$element.attr('data-type') || false);
+      if (this.gallery_index + 1 < this.gallery_items.length) {
+        next = $(this.gallery_items.get(this.gallery_index + 1), false);
+        src = next.attr('data-remote') || next.attr('href');
+        if (next.attr('data-type') === 'image' || this.isImage(src)) {
+          return this.preloadImage(src, false);
+        }
+      }
+    },
+    navigate_left: function() {
+      if (this.gallery_items.length === 1) {
+        return;
+      }
+      if (this.gallery_index === 0) {
+        this.gallery_index = this.gallery_items.length - 1;
+      } else {
+        this.gallery_index--;
+      }
+      this.options.onNavigate.call(this, 'left', this.gallery_index);
+      return this.navigateTo(this.gallery_index);
+    },
+    navigate_right: function() {
+      if (this.gallery_items.length === 1) {
+        return;
+      }
+      if (this.gallery_index === this.gallery_items.length - 1) {
+        this.gallery_index = 0;
+      } else {
+        this.gallery_index++;
+      }
+      this.options.onNavigate.call(this, 'right', this.gallery_index);
+      return this.navigateTo(this.gallery_index);
+    },
+    detectRemoteType: function(src, type) {
+      var video_id;
+      type = type || false;
+      if (type === 'image' || this.isImage(src)) {
+        this.options.type = 'image';
+        return this.preloadImage(src, true);
+      } else if (type === 'youtube' || (video_id = this.getYoutubeId(src))) {
+        this.options.type = 'youtube';
+        return this.showYoutubeVideo(video_id);
+      } else if (type === 'vimeo' || (video_id = this.getVimeoId(src))) {
+        this.options.type = 'vimeo';
+        return this.showVimeoVideo(video_id);
+      } else if (type === 'instagram' || (video_id = this.getInstagramId(src))) {
+        this.options.type = 'instagram';
+        return this.showInstagramVideo(video_id);
+      } else if (type === 'video') {
+        this.options.type = 'video';
+        return this.showVideoIframe(video_id);
+      } else {
+        this.options.type = 'url';
+        return this.loadRemoteContent(src);
+      }
+    },
+    updateTitleAndFooter: function() {
+      var caption, footer, header, title;
+      header = this.modal_content.find('.modal-header');
+      footer = this.modal_content.find('.modal-footer');
+      title = this.$element.data('title') || "";
+      caption = this.$element.data('footer') || "";
+      if (title || this.options.always_show_close) {
+        header.css('display', '').find('.modal-title').html(title || "&nbsp;");
+      } else {
+        header.css('display', 'none');
+      }
+      if (caption) {
+        footer.css('display', '').html(caption);
+      } else {
+        footer.css('display', 'none');
+      }
+      return this;
+    },
+    showLoading: function() {
+      this.lightbox_body.html('<div class="modal-loading">' + this.options.loadingMessage + '</div>');
+      return this;
+    },
+    showYoutubeVideo: function(id) {
+      var height, width;
+      width = this.checkDimensions(this.$element.data('width') || 560);
+      height = width / (560 / 315);
+      return this.showVideoIframe('//www.youtube.com/embed/' + id + '?badge=0&autoplay=1&html5=1', width, height);
+    },
+    showVimeoVideo: function(id) {
+      var height, width;
+      width = this.checkDimensions(this.$element.data('width') || 560);
+      height = width / (500 / 281);
+      return this.showVideoIframe(id + '?autoplay=1', width, height);
+    },
+    showInstagramVideo: function(id) {
+      var height, width;
+      width = this.checkDimensions(this.$element.data('width') || 612);
+      this.resize(width);
+      height = width + 80;
+      this.lightbox_body.html('<iframe width="' + width + '" height="' + height + '" src="' + this.addTrailingSlash(id) + 'embed/" frameborder="0" allowfullscreen></iframe>');
+      this.options.onContentLoaded.call(this);
+      if (this.modal_arrows) {
+        return this.modal_arrows.css('display', 'none');
+      }
+    },
+    showVideoIframe: function(url, width, height) {
+      height = height || width;
+      this.resize(width);
+      this.lightbox_body.html('<div class="embed-responsive embed-responsive-16by9"><iframe width="' + width + '" height="' + height + '" src="' + url + '" frameborder="0" allowfullscreen class="embed-responsive-item"></iframe></div>');
+      this.options.onContentLoaded.call(this);
+      if (this.modal_arrows) {
+        this.modal_arrows.css('display', 'none');
+      }
+      return this;
+    },
+    loadRemoteContent: function(url) {
+      var disableExternalCheck, width,
+        _this = this;
+      width = this.$element.data('width') || 560;
+      this.resize(width);
+      disableExternalCheck = this.$element.data('disableExternalCheck') || false;
+      if (!disableExternalCheck && !this.isExternal(url)) {
+        this.lightbox_body.load(url, $.proxy(function() {
+          return _this.$element.trigger('loaded.bs.modal');
+        }));
+      } else {
+        this.lightbox_body.html('<iframe width="' + width + '" height="' + width + '" src="' + url + '" frameborder="0" allowfullscreen></iframe>');
+        this.options.onContentLoaded.call(this);
+      }
+      if (this.modal_arrows) {
+        this.modal_arrows.css('display', 'none');
+      }
+      return this;
+    },
+    isExternal: function(url) {
+      var match;
+      match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
+      if (typeof match[1] === "string" && match[1].length > 0 && match[1].toLowerCase() !== location.protocol) {
+        return true;
+      }
+      if (typeof match[2] === "string" && match[2].length > 0 && match[2].replace(new RegExp(":(" + {
+        "http:": 80,
+        "https:": 443
+      }[location.protocol] + ")?$"), "") !== location.host) {
+        return true;
+      }
+      return false;
+    },
+    error: function(message) {
+      this.lightbox_body.html(message);
+      return this;
+    },
+    preloadImage: function(src, onLoadShowImage) {
+      var img,
+        _this = this;
+      img = new Image();
+      if ((onLoadShowImage == null) || onLoadShowImage === true) {
+        img.onload = function() {
+          var image;
+          image = $('<img />');
+          image.attr('src', img.src);
+          image.addClass('img-responsive');
+          _this.lightbox_body.html(image);
+          if (_this.modal_arrows) {
+            _this.modal_arrows.css('display', 'block');
+          }
+          return image.load(function() {
+            _this.resize(img.width);
+            return _this.options.onContentLoaded.call(_this);
+          });
+        };
+        img.onerror = function() {
+          return _this.error('Failed to load image: ' + src);
+        };
+      }
+      img.src = src;
+      return img;
+    },
+    resize: function(width) {
+      var width_total;
+      width_total = width + this.border.left + this.padding.left + this.padding.right + this.border.right;
+      this.modal_dialog.css('width', 'auto').css('max-width', width_total);
+      this.lightbox_container.find('a').css('line-height', function() {
+        return $(this).parent().height() + 'px';
+      });
+      return this;
+    },
+    checkDimensions: function(width) {
+      var body_width, width_total;
+      width_total = width + this.border.left + this.padding.left + this.padding.right + this.border.right;
+      body_width = document.body.clientWidth;
+      if (width_total > body_width) {
+        width = this.modal_body.width();
+      }
+      return width;
+    },
+    close: function() {
+      return this.modal.modal('hide');
+    },
+    addTrailingSlash: function(url) {
+      if (url.substr(-1) !== '/') {
+        url += '/';
+      }
+      return url;
+    }
+  };
+
+  $.fn.ekkoLightbox = function(options) {
+    return this.each(function() {
+      var $this;
+      $this = $(this);
+      options = $.extend({
+        remote: $this.attr('data-remote') || $this.attr('href'),
+        gallery_parent_selector: $this.attr('data-parent'),
+        type: $this.attr('data-type')
+      }, options, $this.data());
+      new EkkoLightbox(this, options);
+      return this;
+    });
+  };
+
+  $.fn.ekkoLightbox.defaults = {
+    gallery_parent_selector: 'document.body',
+    left_arrow_class: '.glyphicon .glyphicon-chevron-left',
+    right_arrow_class: '.glyphicon .glyphicon-chevron-right',
+    directional_arrows: true,
+    type: null,
+    always_show_close: true,
+    loadingMessage: 'Loading...',
+    onShow: function() {},
+    onShown: function() {},
+    onHide: function() {},
+    onHidden: function() {},
+    onNavigate: function() {},
+    onContentLoaded: function() {}
+  };
+
+}).call(this);

=== modified file 'themes/openlp2v2/bundles'
--- themes/openlp2v2/bundles	2015-10-13 20:53:14 +0000
+++ themes/openlp2v2/bundles	2015-10-16 22:17:24 +0000
@@ -1,4 +1,4 @@
-assets/css/all-nocdn.css=bootstrap.css,font-awesome.css,rst.css,code.css,colorbox.css,theme.css,animate.css,animations.css,style.css,custom.css
-assets/css/all.css=font-awesome.css,rst.css,code.css,colorbox.css,theme.css,animate.css,animations.css,style.css,custom.css
-assets/js/all-nocdn.js=jquery.min.js,bootstrap.min.js,jquery.colorbox-min.js,jquery.appear.js,jquery.backstretch.js,modernizr.js,isotope.js,template.js,custom.js
-assets/js/all.js=jquery.colorbox-min.js,jquery.appear.js,jquery.backstretch.js,modernizr.js,isotope.js,template.js,custom.js
+assets/css/all-nocdn.css=bootstrap.css,font-awesome.css,rst.css,code.css,colorbox.css,theme.css,animate.css,animations.css,ekko-lightbox.css,style.css,custom.css
+assets/css/all.css=font-awesome.css,rst.css,code.css,colorbox.css,theme.css,animate.css,animations.css,ekko-lightbox.css,style.css,custom.css
+assets/js/all-nocdn.js=jquery.min.js,bootstrap.min.js,jquery.colorbox-min.js,jquery.appear.js,jquery.backstretch.js,modernizr.js,isotope.js,ekko-lightbox.js,template.js,custom.js
+assets/js/all.js=jquery.colorbox-min.js,jquery.appear.js,jquery.backstretch.js,modernizr.js,isotope.js,ekko-lightbox.js,template.js,custom.js

=== modified file 'themes/openlp2v2/templates/front_page.html'
--- themes/openlp2v2/templates/front_page.html	2015-10-15 21:08:29 +0000
+++ themes/openlp2v2/templates/front_page.html	2015-10-16 22:17:24 +0000
@@ -122,6 +122,27 @@
                                 </p>
                             </div>
                         </div>
+                        <div class="space"></div>
+                        <div class="row">
+                            <h2 class="text-center"><i class="fa fw fa-desktop"></i> Screenshots</h2>
+                            <div class="col-md-offset-2 col-md-8">
+                                <div class="space"></div>
+                                <div class="row">
+                                    <a href="/screenshots/mainwindow.png" data-toggle="lightbox" data-gallery="screenshots" data-title="Control OpenLP from the Main Window" class="col-sm-3">
+                                        <img src="/screenshots/mainwindow.png" class="img-responsive">
+                                    </a>
+                                    <a href="/screenshots/songimporterchoices.png" data-toggle="lightbox" data-gallery="screenshots" data-title="Import Songs from Many Formats" class="col-sm-3">
+                                        <img src="/screenshots/songimporterchoices.png" class="img-responsive">
+                                    </a>
+                                    <a href="/screenshots/song_edit_verse_in_use.png" data-toggle="lightbox" data-gallery="screenshots" data-title="Editing a Song" class="col-sm-3">
+                                        <img src="/screenshots/song_edit_verse_in_use.png" class="img-responsive">
+                                    </a>
+                                    <a href="/screenshots/stage_view_song_w_note.png" data-toggle="lightbox" data-gallery="screenshots" data-title="Browser-based Stage View" class="col-sm-3">
+                                        <img src="/screenshots/stage_view_song_w_note.png" class="img-responsive">
+                                    </a>
+                                </div>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>


Follow ups