Merge lp:~openlp-dev/openlp/webengine-migrate into lp:openlp

Proposed by Tomas Groth
Status: Superseded
Proposed branch: lp:~openlp-dev/openlp/webengine-migrate
Merge into: lp:openlp
Diff against target: 33194 lines (+15184/-5325)
370 files modified
.bzrignore (+7/-0)
karma.conf.js (+77/-0)
nose2.cfg (+8/-8)
openlp/.version (+1/-1)
openlp/core/api/deploy.py (+1/-1)
openlp/core/api/endpoint/controller.py (+8/-6)
openlp/core/api/endpoint/core.py (+2/-2)
openlp/core/api/endpoint/remote.py (+1/-0)
openlp/core/api/endpoint/service.py (+1/-0)
openlp/core/api/http/server.py (+6/-7)
openlp/core/api/http/wsgiapp.py (+1/-0)
openlp/core/api/tab.py (+4/-3)
openlp/core/api/websockets.py (+1/-0)
openlp/core/app.py (+10/-7)
openlp/core/common/actions.py (+1/-1)
openlp/core/common/applocation.py (+2/-1)
openlp/core/common/db.py (+1/-0)
openlp/core/common/httputils.py (+1/-0)
openlp/core/common/i18n.py (+2/-1)
openlp/core/common/mixins.py (+1/-0)
openlp/core/common/path.py (+1/-0)
openlp/core/common/registry.py (+2/-0)
openlp/core/common/settings.py (+40/-3)
openlp/core/display/html/black.css (+292/-0)
openlp/core/display/html/display.html (+39/-0)
openlp/core/display/html/display.js (+789/-0)
openlp/core/display/html/reveal.css (+1591/-0)
openlp/core/display/html/reveal.js (+5586/-0)
openlp/core/display/html/textFit.js (+237/-0)
openlp/core/display/render.py (+746/-0)
openlp/core/display/renderer.py (+0/-586)
openlp/core/display/screens.py (+283/-181)
openlp/core/display/webengine.py (+93/-0)
openlp/core/display/window.py (+402/-0)
openlp/core/lib/__init__.py (+97/-307)
openlp/core/lib/db.py (+4/-3)
openlp/core/lib/htmlbuilder.py (+3/-14)
openlp/core/lib/imagemanager.py (+6/-5)
openlp/core/lib/mediamanageritem.py (+6/-5)
openlp/core/lib/plugin.py (+1/-0)
openlp/core/lib/pluginmanager.py (+2/-0)
openlp/core/lib/serviceitem.py (+105/-193)
openlp/core/lib/settingstab.py (+4/-4)
openlp/core/lib/theme.py (+9/-7)
openlp/core/lib/ui.py (+1/-0)
openlp/core/loader.py (+1/-1)
openlp/core/projectors/constants.py (+1/-0)
openlp/core/projectors/db.py (+6/-3)
openlp/core/projectors/editform.py (+6/-5)
openlp/core/projectors/manager.py (+6/-5)
openlp/core/projectors/pjlink.py (+6/-5)
openlp/core/projectors/sourceselectform.py (+2/-1)
openlp/core/projectors/tab.py (+5/-4)
openlp/core/projectors/upgrade.py (+2/-1)
openlp/core/resources.py (+1/-1)
openlp/core/server.py (+1/-1)
openlp/core/ui/aboutform.py (+1/-0)
openlp/core/ui/advancedtab.py (+5/-4)
openlp/core/ui/exceptiondialog.py (+4/-4)
openlp/core/ui/exceptionform.py (+7/-46)
openlp/core/ui/filerenamedialog.py (+3/-3)
openlp/core/ui/filerenameform.py (+1/-1)
openlp/core/ui/firsttimeform.py (+42/-39)
openlp/core/ui/firsttimelanguagedialog.py (+3/-3)
openlp/core/ui/firsttimelanguageform.py (+2/-1)
openlp/core/ui/firsttimewizard.py (+30/-32)
openlp/core/ui/formattingtagdialog.py (+4/-4)
openlp/core/ui/formattingtagform.py (+1/-1)
openlp/core/ui/generaltab.py (+8/-144)
openlp/core/ui/icons.py (+2/-1)
openlp/core/ui/maindisplay.py (+0/-602)
openlp/core/ui/mainwindow.py (+20/-27)
openlp/core/ui/media/endpoint.py (+1/-0)
openlp/core/ui/media/mediacontroller.py (+6/-5)
openlp/core/ui/media/playertab.py (+4/-4)
openlp/core/ui/media/systemplayer.py (+2/-1)
openlp/core/ui/media/vendor/vlc.py (+1197/-892)
openlp/core/ui/media/vlcplayer.py (+2/-1)
openlp/core/ui/media/webkitplayer.py (+0/-312)
openlp/core/ui/plugindialog.py (+3/-3)
openlp/core/ui/pluginform.py (+2/-1)
openlp/core/ui/printservicedialog.py (+4/-4)
openlp/core/ui/printserviceform.py (+3/-2)
openlp/core/ui/screenstab.py (+97/-0)
openlp/core/ui/serviceitemeditdialog.py (+4/-4)
openlp/core/ui/serviceitemeditform.py (+3/-3)
openlp/core/ui/servicemanager.py (+19/-15)
openlp/core/ui/servicenoteform.py (+4/-4)
openlp/core/ui/settingsdialog.py (+3/-3)
openlp/core/ui/settingsform.py (+21/-13)
openlp/core/ui/shortcutlistdialog.py (+3/-3)
openlp/core/ui/shortcutlistform.py (+2/-1)
openlp/core/ui/slidecontroller.py (+162/-167)
openlp/core/ui/splashscreen.py (+2/-2)
openlp/core/ui/starttimedialog.py (+3/-3)
openlp/core/ui/starttimeform.py (+1/-1)
openlp/core/ui/style.py (+1/-0)
openlp/core/ui/themeform.py (+14/-8)
openlp/core/ui/themelayoutdialog.py (+3/-3)
openlp/core/ui/themelayoutform.py (+1/-1)
openlp/core/ui/thememanager.py (+11/-11)
openlp/core/ui/themestab.py (+5/-5)
openlp/core/ui/themewizard.py (+4/-4)
openlp/core/version.py (+54/-2)
openlp/core/widgets/docks.py (+2/-1)
openlp/core/widgets/edits.py (+2/-1)
openlp/core/widgets/layouts.py (+185/-0)
openlp/core/widgets/toolbar.py (+1/-0)
openlp/core/widgets/views.py (+40/-38)
openlp/core/widgets/widgets.py (+267/-1)
openlp/core/widgets/wizard.py (+5/-4)
openlp/plugins/alerts/alertsplugin.py (+8/-7)
openlp/plugins/alerts/endpoint.py (+1/-0)
openlp/plugins/alerts/forms/__init__.py (+1/-1)
openlp/plugins/alerts/forms/alertdialog.py (+4/-4)
openlp/plugins/alerts/forms/alertform.py (+2/-1)
openlp/plugins/alerts/lib/alertsmanager.py (+3/-5)
openlp/plugins/alerts/lib/alertstab.py (+3/-3)
openlp/plugins/bibles/bibleplugin.py (+1/-0)
openlp/plugins/bibles/endpoint.py (+2/-1)
openlp/plugins/bibles/forms/bibleimportform.py (+7/-6)
openlp/plugins/bibles/forms/booknamedialog.py (+3/-3)
openlp/plugins/bibles/forms/booknameform.py (+2/-1)
openlp/plugins/bibles/forms/editbibledialog.py (+5/-5)
openlp/plugins/bibles/forms/editbibleform.py (+3/-1)
openlp/plugins/bibles/forms/languagedialog.py (+3/-3)
openlp/plugins/bibles/forms/languageform.py (+2/-1)
openlp/plugins/bibles/lib/biblestab.py (+6/-5)
openlp/plugins/bibles/lib/db.py (+5/-4)
openlp/plugins/bibles/lib/importers/csvbible.py (+1/-0)
openlp/plugins/bibles/lib/importers/http.py (+1/-0)
openlp/plugins/bibles/lib/importers/osis.py (+1/-0)
openlp/plugins/bibles/lib/importers/sword.py (+1/-0)
openlp/plugins/bibles/lib/importers/wordproject.py (+2/-1)
openlp/plugins/bibles/lib/importers/zefania.py (+1/-0)
openlp/plugins/bibles/lib/manager.py (+2/-0)
openlp/plugins/bibles/lib/mediaitem.py (+9/-9)
openlp/plugins/bibles/lib/upgrade.py (+1/-0)
openlp/plugins/custom/customplugin.py (+3/-2)
openlp/plugins/custom/endpoint.py (+2/-1)
openlp/plugins/custom/forms/editcustomdialog.py (+4/-4)
openlp/plugins/custom/forms/editcustomform.py (+1/-1)
openlp/plugins/custom/forms/editcustomslidedialog.py (+3/-3)
openlp/plugins/custom/forms/editcustomslideform.py (+2/-1)
openlp/plugins/custom/lib/customtab.py (+3/-3)
openlp/plugins/custom/lib/customxmlhandler.py (+1/-0)
openlp/plugins/custom/lib/mediaitem.py (+5/-4)
openlp/plugins/images/endpoint.py (+2/-1)
openlp/plugins/images/forms/__init__.py (+1/-1)
openlp/plugins/images/forms/addgroupdialog.py (+3/-3)
openlp/plugins/images/forms/addgroupform.py (+1/-1)
openlp/plugins/images/forms/choosegroupdialog.py (+3/-3)
openlp/plugins/images/forms/choosegroupform.py (+1/-1)
openlp/plugins/images/imageplugin.py (+3/-2)
openlp/plugins/images/lib/imagetab.py (+3/-3)
openlp/plugins/images/lib/mediaitem.py (+5/-5)
openlp/plugins/images/lib/upgrade.py (+1/-0)
openlp/plugins/media/endpoint.py (+2/-1)
openlp/plugins/media/forms/mediaclipselectordialog.py (+3/-3)
openlp/plugins/media/forms/mediaclipselectorform.py (+3/-2)
openlp/plugins/media/lib/mediaitem.py (+4/-3)
openlp/plugins/media/lib/mediatab.py (+3/-3)
openlp/plugins/media/mediaplugin.py (+3/-0)
openlp/plugins/presentations/endpoint.py (+2/-1)
openlp/plugins/presentations/lib/impresscontroller.py (+5/-3)
openlp/plugins/presentations/lib/mediaitem.py (+5/-5)
openlp/plugins/presentations/lib/messagelistener.py (+1/-0)
openlp/plugins/presentations/lib/pdfcontroller.py (+3/-2)
openlp/plugins/presentations/lib/powerpointcontroller.py (+21/-10)
openlp/plugins/presentations/lib/presentationcontroller.py (+1/-0)
openlp/plugins/presentations/lib/presentationtab.py (+3/-3)
openlp/plugins/presentations/presentationplugin.py (+2/-1)
openlp/plugins/songs/endpoint.py (+2/-1)
openlp/plugins/songs/forms/__init__.py (+1/-1)
openlp/plugins/songs/forms/authorsdialog.py (+3/-3)
openlp/plugins/songs/forms/authorsform.py (+1/-1)
openlp/plugins/songs/forms/duplicatesongremovalform.py (+2/-1)
openlp/plugins/songs/forms/editsongdialog.py (+4/-4)
openlp/plugins/songs/forms/editsongform.py (+6/-5)
openlp/plugins/songs/forms/editversedialog.py (+3/-3)
openlp/plugins/songs/forms/editverseform.py (+2/-1)
openlp/plugins/songs/forms/mediafilesdialog.py (+3/-3)
openlp/plugins/songs/forms/mediafilesform.py (+2/-1)
openlp/plugins/songs/forms/songbookdialog.py (+3/-3)
openlp/plugins/songs/forms/songbookform.py (+1/-1)
openlp/plugins/songs/forms/songexportform.py (+4/-3)
openlp/plugins/songs/forms/songimportform.py (+4/-3)
openlp/plugins/songs/forms/songmaintenancedialog.py (+3/-3)
openlp/plugins/songs/forms/songmaintenanceform.py (+5/-3)
openlp/plugins/songs/forms/songreviewwidget.py (+4/-4)
openlp/plugins/songs/forms/songselectform.py (+1/-0)
openlp/plugins/songs/forms/topicsdialog.py (+3/-3)
openlp/plugins/songs/forms/topicsform.py (+1/-1)
openlp/plugins/songs/lib/__init__.py (+2/-2)
openlp/plugins/songs/lib/db.py (+2/-2)
openlp/plugins/songs/lib/importer.py (+2/-0)
openlp/plugins/songs/lib/importers/cclifile.py (+2/-0)
openlp/plugins/songs/lib/importers/chordpro.py (+1/-0)
openlp/plugins/songs/lib/importers/dreambeam.py (+1/-0)
openlp/plugins/songs/lib/importers/easyslides.py (+1/-0)
openlp/plugins/songs/lib/importers/easyworship.py (+3/-2)
openlp/plugins/songs/lib/importers/foilpresenter.py (+2/-1)
openlp/plugins/songs/lib/importers/lyrix.py (+1/-0)
openlp/plugins/songs/lib/importers/mediashout.py (+1/-0)
openlp/plugins/songs/lib/importers/openlp.py (+4/-2)
openlp/plugins/songs/lib/importers/openlyrics.py (+1/-0)
openlp/plugins/songs/lib/importers/openoffice.py (+2/-0)
openlp/plugins/songs/lib/importers/opensong.py (+1/-0)
openlp/plugins/songs/lib/importers/opspro.py (+1/-0)
openlp/plugins/songs/lib/importers/powersong.py (+1/-0)
openlp/plugins/songs/lib/importers/presentationmanager.py (+1/-1)
openlp/plugins/songs/lib/importers/propresenter.py (+1/-0)
openlp/plugins/songs/lib/importers/songbeamer.py (+2/-1)
openlp/plugins/songs/lib/importers/songimport.py (+3/-2)
openlp/plugins/songs/lib/importers/songshowplus.py (+1/-0)
openlp/plugins/songs/lib/importers/songsoffellowship.py (+2/-0)
openlp/plugins/songs/lib/importers/sundayplus.py (+2/-2)
openlp/plugins/songs/lib/importers/videopsalm.py (+1/-0)
openlp/plugins/songs/lib/importers/wordsofworship.py (+1/-0)
openlp/plugins/songs/lib/importers/worshipassistant.py (+1/-0)
openlp/plugins/songs/lib/importers/worshipcenterpro.py (+1/-0)
openlp/plugins/songs/lib/importers/zionworx.py (+1/-0)
openlp/plugins/songs/lib/mediaitem.py (+5/-4)
openlp/plugins/songs/lib/openlyricsexport.py (+1/-0)
openlp/plugins/songs/lib/openlyricsxml.py (+1/-0)
openlp/plugins/songs/lib/songselect.py (+1/-0)
openlp/plugins/songs/lib/songstab.py (+3/-3)
openlp/plugins/songs/lib/upgrade.py (+3/-2)
openlp/plugins/songs/reporting.py (+1/-0)
openlp/plugins/songs/songsplugin.py (+5/-5)
openlp/plugins/songusage/forms/songusagedeletedialog.py (+3/-3)
openlp/plugins/songusage/forms/songusagedeleteform.py (+1/-1)
openlp/plugins/songusage/forms/songusagedetaildialog.py (+3/-3)
openlp/plugins/songusage/forms/songusagedetailform.py (+3/-1)
openlp/plugins/songusage/lib/upgrade.py (+2/-1)
openlp/plugins/songusage/songusageplugin.py (+3/-2)
package.json (+25/-0)
run_openlp.py (+3/-1)
scripts/appveyor-webhook.py (+5/-4)
scripts/appveyor.yml (+4/-10)
scripts/check_dependencies.py (+1/-2)
scripts/clean_up_resources.py (+1/-0)
scripts/jenkins_script.py (+2/-1)
scripts/lp-merge.py (+5/-4)
scripts/mp_update.py (+3/-1)
scripts/reveal-js.patch (+25/-0)
scripts/translation_utils.py (+3/-3)
scripts/websocket_client.py (+2/-1)
setup.cfg (+1/-2)
setup.py (+2/-1)
tests/functional/openlp_core/api/endpoint/test_controller.py (+44/-16)
tests/functional/openlp_core/api/endpoint/test_remote.py (+1/-1)
tests/functional/openlp_core/api/http/test_init.py (+1/-2)
tests/functional/openlp_core/api/http/test_wsgiapp.py (+1/-0)
tests/functional/openlp_core/api/test_deploy.py (+7/-4)
tests/functional/openlp_core/api/test_tab.py (+1/-0)
tests/functional/openlp_core/api/test_websockets.py (+1/-0)
tests/functional/openlp_core/common/test_actions.py (+123/-128)
tests/functional/openlp_core/common/test_applocation.py (+1/-0)
tests/functional/openlp_core/common/test_common.py (+2/-2)
tests/functional/openlp_core/common/test_db.py (+1/-1)
tests/functional/openlp_core/common/test_path.py (+2/-2)
tests/functional/openlp_core/display/test_render.py (+210/-0)
tests/functional/openlp_core/display/test_renderer.py (+0/-208)
tests/functional/openlp_core/display/test_screens.py (+66/-18)
tests/functional/openlp_core/lib/test_db.py (+2/-2)
tests/functional/openlp_core/lib/test_formattingtags.py (+1/-0)
tests/functional/openlp_core/lib/test_htmlbuilder.py (+4/-13)
tests/functional/openlp_core/lib/test_image_manager.py (+2/-1)
tests/functional/openlp_core/lib/test_lib.py (+3/-177)
tests/functional/openlp_core/lib/test_serviceitem.py (+43/-60)
tests/functional/openlp_core/lib/test_ui.py (+4/-4)
tests/functional/openlp_core/test_app.py (+3/-0)
tests/functional/openlp_core/test_server.py (+6/-5)
tests/functional/openlp_core/test_threading.py (+1/-1)
tests/functional/openlp_core/ui/media/test_systemplayer.py (+1/-1)
tests/functional/openlp_core/ui/media/test_vlcplayer.py (+1/-1)
tests/functional/openlp_core/ui/media/test_webkitplayer.py (+0/-66)
tests/functional/openlp_core/ui/test_exceptionform.py (+42/-56)
tests/functional/openlp_core/ui/test_firsttimeform.py (+63/-38)
tests/functional/openlp_core/ui/test_formattingtagsform.py (+1/-1)
tests/functional/openlp_core/ui/test_icons.py (+0/-1)
tests/functional/openlp_core/ui/test_maindisplay.py (+0/-283)
tests/functional/openlp_core/ui/test_mainwindow.py (+20/-14)
tests/functional/openlp_core/ui/test_servicemanager.py (+15/-12)
tests/functional/openlp_core/ui/test_slidecontroller.py (+17/-21)
tests/functional/openlp_core/widgets/test_views.py (+15/-16)
tests/functional/openlp_core/widgets/test_widgets.py (+199/-0)
tests/functional/openlp_plugins/bibles/test_bibleimport.py (+1/-1)
tests/functional/openlp_plugins/bibles/test_bibleserver.py (+1/-1)
tests/functional/openlp_plugins/bibles/test_csvimport.py (+1/-0)
tests/functional/openlp_plugins/bibles/test_mediaitem.py (+2/-2)
tests/functional/openlp_plugins/bibles/test_opensongimport.py (+2/-1)
tests/functional/openlp_plugins/bibles/test_osisimport.py (+2/-1)
tests/functional/openlp_plugins/bibles/test_swordimport.py (+5/-4)
tests/functional/openlp_plugins/bibles/test_wordprojectimport.py (+2/-1)
tests/functional/openlp_plugins/bibles/test_zefaniaimport.py (+1/-0)
tests/functional/openlp_plugins/custom/test_mediaitem.py (+2/-1)
tests/functional/openlp_plugins/images/test_imagetab.py (+1/-0)
tests/functional/openlp_plugins/images/test_upgrade.py (+1/-0)
tests/functional/openlp_plugins/media/test_mediaitem.py (+1/-0)
tests/functional/openlp_plugins/presentations/test_impresscontroller.py (+3/-3)
tests/functional/openlp_plugins/presentations/test_mediaitem.py (+1/-1)
tests/functional/openlp_plugins/presentations/test_messagelistener.py (+1/-1)
tests/functional/openlp_plugins/presentations/test_pdfcontroller.py (+5/-5)
tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py (+3/-2)
tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py.THIS (+224/-0)
tests/functional/openlp_plugins/presentations/test_presentationcontroller.py (+1/-0)
tests/functional/openlp_plugins/songs/test_chordproimport.py (+2/-1)
tests/functional/openlp_plugins/songs/test_db.py (+1/-1)
tests/functional/openlp_plugins/songs/test_easyslidesimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_editsongform.py (+1/-1)
tests/functional/openlp_plugins/songs/test_editverseform.py (+1/-0)
tests/functional/openlp_plugins/songs/test_ewimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_foilpresenterimport.py (+1/-1)
tests/functional/openlp_plugins/songs/test_lib.py (+2/-2)
tests/functional/openlp_plugins/songs/test_lyriximport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_mediashout.py (+2/-1)
tests/functional/openlp_plugins/songs/test_openlpimporter.py (+1/-1)
tests/functional/openlp_plugins/songs/test_openlyricsimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_openoffice.py (+1/-0)
tests/functional/openlp_plugins/songs/test_opensongimport.py (+2/-1)
tests/functional/openlp_plugins/songs/test_opsproimport.py (+5/-3)
tests/functional/openlp_plugins/songs/test_powerpraiseimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_propresenterimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_songbeamerimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_songproimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_songselect.py (+4/-3)
tests/functional/openlp_plugins/songs/test_songshowplusimport.py (+2/-1)
tests/functional/openlp_plugins/songs/test_sundayplusimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_videopsalm.py (+2/-1)
tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_worshipassistantimport.py (+1/-0)
tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py (+3/-2)
tests/functional/openlp_plugins/songs/test_zionworximport.py (+1/-0)
tests/helpers/songfileimport.py (+2/-1)
tests/interfaces/openlp_core/common/test_utils.py (+0/-1)
tests/interfaces/openlp_core/ui/test_servicemanager.py (+3/-3)
tests/interfaces/openlp_core/ui/test_settings_form.py (+1/-0)
tests/interfaces/openlp_core/ui/test_thememanager.py (+1/-1)
tests/interfaces/openlp_core/widgets/test_edits.py (+1/-1)
tests/interfaces/openlp_core/widgets/test_views.py (+5/-0)
tests/interfaces/openlp_plugins/bibles/test_lib_http.py (+1/-1)
tests/interfaces/openlp_plugins/custom/forms/test_customform.py (+1/-1)
tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py (+7/-8)
tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py (+1/-0)
tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py (+1/-0)
tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py (+1/-1)
tests/js/fake_webchannel.js (+5/-0)
tests/js/polyfill.js (+84/-0)
tests/js/test_display.js (+632/-0)
tests/openlp_core/common/test_network_interfaces.py (+0/-1)
tests/openlp_core/projectors/test_projector_bugfixes_01.py (+1/-0)
tests/openlp_core/projectors/test_projector_constants.py (+1/-0)
tests/openlp_core/projectors/test_projector_db.py (+1/-1)
tests/openlp_core/projectors/test_projector_editform.py (+1/-1)
tests/openlp_core/projectors/test_projector_pjlink_base_01.py (+3/-5)
tests/openlp_core/projectors/test_projector_pjlink_base_02.py (+0/-1)
tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py (+2/-3)
tests/openlp_core/projectors/test_projector_pjlink_commands_01.py (+2/-12)
tests/openlp_core/projectors/test_projector_pjlink_commands_02.py (+2/-2)
tests/openlp_core/projectors/test_projector_pjlink_udp.py (+1/-1)
tests/openlp_core/projectors/test_projector_sourceform.py (+3/-3)
tests/openlp_core/projectors/test_projector_utilities.py (+3/-2)
tests/openlp_core/projectors/test_projectormanager.py (+1/-1)
tests/utils/__init__.py (+7/-10)
tests/utils/constants.py (+1/-0)
tests/utils/test_bzr_tags.py (+2/-1)
tests/utils/test_pylint.py (+4/-2)
To merge this branch: bzr merge lp:~openlp-dev/openlp/webengine-migrate
Reviewer Review Type Date Requested Status
OpenLP Core Pending
Review via email: mp+363155@code.launchpad.net

This proposal supersedes a proposal from 2019-02-12.

This proposal has been superseded by a proposal from 2019-02-13.

Commit message

Migration from WebKit to Webengine. Also introduced reveal.js for slide rendering, new screen setup dialogs and many other changes.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/73/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/75/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/76/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/77/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/31/ for more details

2880. By Tomas Groth

pep8 fixes

2881. By Tomas Groth

More pep8 fixes

2882. By Tomas Groth

Even more pep8 fixes and removed the old htmlbuilder.

2883. By Raoul Snyman

Fix an unused import and some incorrect patch()s

2884. By Raoul Snyman

HEAD

2885. By Tomas Groth

trunk

Unmerged revisions

2885. By Tomas Groth

trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2018-06-26 17:10:34 +0000
+++ .bzrignore 2019-02-13 20:54:40 +0000
@@ -45,4 +45,11 @@
45resources/windows/warnOpenLP.txt45resources/windows/warnOpenLP.txt
46*.ropeproject46*.ropeproject
47tags47tags
48output
49htmlcov
50node_modules
51openlp-test-projectordb.sqlite
52package-lock.json
53.cache
54test
48tests.kdev455tests.kdev4
4956
=== added file 'karma.conf.js'
--- karma.conf.js 1970-01-01 00:00:00 +0000
+++ karma.conf.js 2019-02-13 20:54:40 +0000
@@ -0,0 +1,77 @@
1module.exports = function(config) {
2 config.set({
3 // base path that will be used to resolve all patterns (eg. files, exclude)
4 basePath: "",
5
6 // frameworks to use
7 // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
8 frameworks: ["jasmine"],
9
10 // list of files / patterns to load in the browser
11 files: [
12 "tests/js/polyfill.js",
13 "tests/js/fake_webchannel.js",
14 "openlp/core/display/html/reveal.js",
15 "openlp/core/display/html/display.js",
16 "tests/js/test_*.js"
17 ],
18
19 // list of files to exclude
20 exclude: [
21 ],
22
23 // preprocess matching files before serving them to the browser
24 // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
25 preprocessors: {
26 // source files, that you wanna generate coverage for
27 // do not include tests or libraries
28 // (these files will be instrumented by Istanbul)
29 "display.js": ["coverage"]
30 },
31
32 // test results reporter to use
33 // possible values: "dots", "progress"
34 // available reporters: https://npmjs.org/browse/keyword/karma-reporter
35 reporters: ["progress", "coverage"],
36
37 // configure the coverateReporter
38 coverageReporter: {
39 type : "html",
40 dir : "htmlcov/"
41 },
42
43 // web server port
44 port: 9876,
45
46 // enable / disable colors in the output (reporters and logs)
47 colors: true,
48
49 // level of logging
50 // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
51 logLevel: config.LOG_DEBUG,
52
53 // loggers
54 /* loggers: [
55 {"type": "file", "filename": "karma.log"}
56 ],*/
57
58 // enable / disable watching file and executing tests whenever any file changes
59 autoWatch: true,
60
61 // start these browsers
62 // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
63 browsers: ["PhantomJS"],
64
65 // Continuous Integration mode
66 // if true, Karma captures browsers, runs the tests and exits
67 singleRun: false,
68
69 // Concurrency level
70 // how many browser should be started simultaneous
71 concurrency: Infinity,
72
73 client: {
74 captureConsole: true
75 }
76 })
77}
078
=== modified file 'nose2.cfg'
--- nose2.cfg 2017-11-16 05:03:19 +0000
+++ nose2.cfg 2019-02-13 20:54:40 +0000
@@ -1,5 +1,5 @@
1[unittest]1[unittest]
2verbose = True2verbose = true
3plugins = nose2.plugins.mp3plugins = nose2.plugins.mp
44
5[log-capture]5[log-capture]
@@ -9,19 +9,19 @@
9log-level = ERROR9log-level = ERROR
1010
11[test-result]11[test-result]
12always-on = True12always-on = true
13descriptions = True13descriptions = true
1414
15[coverage]15[coverage]
16always-on = True16always-on = true
17coverage = openlp17coverage = openlp
18coverage-report = html18coverage-report = html
1919
20[multiprocess]20[multiprocess]
21always-on = False21always-on = false
22processes = 422processes = 4
2323
24[output-buffer]24[output-buffer]
25always-on = True25always-on = true
26stderr = True26stderr = true
27stdout = False27stdout = true
2828
=== modified file 'openlp/.version'
--- openlp/.version 2019-01-11 20:23:24 +0000
+++ openlp/.version 2019-02-13 20:54:40 +0000
@@ -1,1 +1,1 @@
12.9.012.5.dev2856
2\ No newline at end of file2\ No newline at end of file
33
=== modified file 'openlp/core/api/deploy.py'
--- openlp/core/api/deploy.py 2018-01-04 07:00:55 +0000
+++ openlp/core/api/deploy.py 2019-02-13 20:54:40 +0000
@@ -25,7 +25,7 @@
25from zipfile import ZipFile25from zipfile import ZipFile
2626
27from openlp.core.common.applocation import AppLocation27from openlp.core.common.applocation import AppLocation
28from openlp.core.common.httputils import download_file, get_web_page, get_url_file_size28from openlp.core.common.httputils import download_file, get_url_file_size, get_web_page
29from openlp.core.common.registry import Registry29from openlp.core.common.registry import Registry
3030
3131
3232
=== modified file 'openlp/core/api/endpoint/controller.py'
--- openlp/core/api/endpoint/controller.py 2018-08-25 14:08:19 +0000
+++ openlp/core/api/endpoint/controller.py 2019-02-13 20:54:40 +0000
@@ -34,6 +34,7 @@
34from openlp.core.lib import create_thumb34from openlp.core.lib import create_thumb
35from openlp.core.lib.serviceitem import ItemCapabilities35from openlp.core.lib.serviceitem import ItemCapabilities
3636
37
37log = logging.getLogger(__name__)38log = logging.getLogger(__name__)
3839
39controller_endpoint = Endpoint('controller')40controller_endpoint = Endpoint('controller')
@@ -48,7 +49,7 @@
4849
49 :param request: the http request - not used50 :param request: the http request - not used
50 """51 """
51 log.debug("controller_text ")52 log.debug('controller_text')
52 live_controller = Registry().get('live_controller')53 live_controller = Registry().get('live_controller')
53 current_item = live_controller.service_item54 current_item = live_controller.service_item
54 data = []55 data = []
@@ -57,13 +58,14 @@
57 item = {}58 item = {}
58 # Handle text (songs, custom, bibles)59 # Handle text (songs, custom, bibles)
59 if current_item.is_text():60 if current_item.is_text():
60 if frame['verseTag']:61 if frame['verse']:
61 item['tag'] = str(frame['verseTag'])62 item['tag'] = str(frame['verse'])
62 else:63 else:
63 item['tag'] = str(index + 1)64 item['tag'] = str(index + 1)
64 item['chords_text'] = str(frame['chords_text'])65 # TODO: Figure out rendering chords
65 item['text'] = str(frame['text'])66 item['chords_text'] = str(frame.get('chords_text', ''))
66 item['html'] = str(frame['html'])67 item['text'] = frame['text']
68 item['html'] = current_item.get_rendered_frame(index)
67 # Handle images, unless a custom thumbnail is given or if thumbnails is disabled69 # Handle images, unless a custom thumbnail is given or if thumbnails is disabled
68 elif current_item.is_image() and not frame.get('image', '') and Settings().value('api/thumbnails'):70 elif current_item.is_image() and not frame.get('image', '') and Settings().value('api/thumbnails'):
69 item['tag'] = str(index + 1)71 item['tag'] = str(index + 1)
7072
=== modified file 'openlp/core/api/endpoint/core.py'
--- openlp/core/api/endpoint/core.py 2018-08-25 14:08:19 +0000
+++ openlp/core/api/endpoint/core.py 2019-02-13 20:54:40 +0000
@@ -30,8 +30,8 @@
30from openlp.core.common.i18n import UiStrings, translate30from openlp.core.common.i18n import UiStrings, translate
31from openlp.core.common.registry import Registry31from openlp.core.common.registry import Registry
32from openlp.core.lib import image_to_byte32from openlp.core.lib import image_to_byte
33from openlp.core.lib.plugin import StringContent33from openlp.core.lib.plugin import PluginStatus, StringContent
34from openlp.core.lib.plugin import PluginStatus34
3535
36template_dir = 'templates'36template_dir = 'templates'
37static_dir = 'static'37static_dir = 'static'
3838
=== modified file 'openlp/core/api/endpoint/remote.py'
--- openlp/core/api/endpoint/remote.py 2017-12-29 09:15:48 +0000
+++ openlp/core/api/endpoint/remote.py 2019-02-13 20:54:40 +0000
@@ -24,6 +24,7 @@
24from openlp.core.api.endpoint.core import TRANSLATED_STRINGS24from openlp.core.api.endpoint.core import TRANSLATED_STRINGS
25from openlp.core.api.http.endpoint import Endpoint25from openlp.core.api.http.endpoint import Endpoint
2626
27
27log = logging.getLogger(__name__)28log = logging.getLogger(__name__)
2829
29remote_endpoint = Endpoint('remote', template_dir='remotes')30remote_endpoint = Endpoint('remote', template_dir='remotes')
3031
=== modified file 'openlp/core/api/endpoint/service.py'
--- openlp/core/api/endpoint/service.py 2017-12-29 09:15:48 +0000
+++ openlp/core/api/endpoint/service.py 2019-02-13 20:54:40 +0000
@@ -26,6 +26,7 @@
26from openlp.core.api.http.endpoint import Endpoint26from openlp.core.api.http.endpoint import Endpoint
27from openlp.core.common.registry import Registry27from openlp.core.common.registry import Registry
2828
29
29log = logging.getLogger(__name__)30log = logging.getLogger(__name__)
3031
31service_endpoint = Endpoint('service')32service_endpoint = Endpoint('service')
3233
=== modified file 'openlp/core/api/http/server.py'
--- openlp/core/api/http/server.py 2018-01-07 04:36:45 +0000
+++ openlp/core/api/http/server.py 2019-02-13 20:54:40 +0000
@@ -30,22 +30,21 @@
30from waitress.server import create_server30from waitress.server import create_server
3131
32from openlp.core.api.deploy import download_and_check, download_sha25632from openlp.core.api.deploy import download_and_check, download_sha256
33from openlp.core.api.endpoint.controller import controller_endpoint, api_controller_endpoint33from openlp.core.api.endpoint.controller import api_controller_endpoint, controller_endpoint
34from openlp.core.api.endpoint.core import chords_endpoint, stage_endpoint, blank_endpoint, main_endpoint34from openlp.core.api.endpoint.core import blank_endpoint, chords_endpoint, main_endpoint, stage_endpoint
35from openlp.core.api.endpoint.remote import remote_endpoint35from openlp.core.api.endpoint.remote import remote_endpoint
36from openlp.core.api.endpoint.service import service_endpoint, api_service_endpoint36from openlp.core.api.endpoint.service import api_service_endpoint, service_endpoint
37from openlp.core.api.http import application37from openlp.core.api.http import application, register_endpoint
38from openlp.core.api.http import register_endpoint
39from openlp.core.api.poll import Poller38from openlp.core.api.poll import Poller
40from openlp.core.common.applocation import AppLocation39from openlp.core.common.applocation import AppLocation
41from openlp.core.common.i18n import UiStrings40from openlp.core.common.i18n import UiStrings, translate
42from openlp.core.common.i18n import translate
43from openlp.core.common.mixins import LogMixin, RegistryProperties41from openlp.core.common.mixins import LogMixin, RegistryProperties
44from openlp.core.common.path import create_paths42from openlp.core.common.path import create_paths
45from openlp.core.common.registry import Registry, RegistryBase43from openlp.core.common.registry import Registry, RegistryBase
46from openlp.core.common.settings import Settings44from openlp.core.common.settings import Settings
47from openlp.core.threading import ThreadWorker, run_thread45from openlp.core.threading import ThreadWorker, run_thread
4846
47
49log = logging.getLogger(__name__)48log = logging.getLogger(__name__)
5049
5150
5251
=== modified file 'openlp/core/api/http/wsgiapp.py'
--- openlp/core/api/http/wsgiapp.py 2018-07-04 20:42:55 +0000
+++ openlp/core/api/http/wsgiapp.py 2019-02-13 20:54:40 +0000
@@ -33,6 +33,7 @@
33from openlp.core.api.http.errors import HttpError, NotFound, ServerError33from openlp.core.api.http.errors import HttpError, NotFound, ServerError
34from openlp.core.common.applocation import AppLocation34from openlp.core.common.applocation import AppLocation
3535
36
36ARGS_REGEX = re.compile(r'''\{(\w+)(?::([^}]+))?\}''', re.VERBOSE)37ARGS_REGEX = re.compile(r'''\{(\w+)(?::([^}]+))?\}''', re.VERBOSE)
3738
38log = logging.getLogger(__name__)39log = logging.getLogger(__name__)
3940
=== modified file 'openlp/core/api/tab.py'
--- openlp/core/api/tab.py 2018-08-25 14:08:19 +0000
+++ openlp/core/api/tab.py 2019-02-13 20:54:40 +0000
@@ -31,6 +31,7 @@
31from openlp.core.lib.settingstab import SettingsTab31from openlp.core.lib.settingstab import SettingsTab
32from openlp.core.ui.icons import UiIcons32from openlp.core.ui.icons import UiIcons
3333
34
34ZERO_URL = '0.0.0.0'35ZERO_URL = '0.0.0.0'
3536
3637
@@ -43,9 +44,9 @@
43 advanced_translated = translate('OpenLP.AdvancedTab', 'Advanced')44 advanced_translated = translate('OpenLP.AdvancedTab', 'Advanced')
44 super(ApiTab, self).__init__(parent, 'api', advanced_translated)45 super(ApiTab, self).__init__(parent, 'api', advanced_translated)
4546
46 def setupUi(self):47 def setup_ui(self):
47 self.setObjectName('ApiTab')48 self.setObjectName('ApiTab')
48 super(ApiTab, self).setupUi()49 super(ApiTab, self).setup_ui()
49 self.server_settings_group_box = QtWidgets.QGroupBox(self.left_column)50 self.server_settings_group_box = QtWidgets.QGroupBox(self.left_column)
50 self.server_settings_group_box.setObjectName('server_settings_group_box')51 self.server_settings_group_box.setObjectName('server_settings_group_box')
51 self.server_settings_layout = QtWidgets.QFormLayout(self.server_settings_group_box)52 self.server_settings_layout = QtWidgets.QFormLayout(self.server_settings_group_box)
@@ -154,7 +155,7 @@
154 self.thumbnails_check_box.stateChanged.connect(self.on_thumbnails_check_box_changed)155 self.thumbnails_check_box.stateChanged.connect(self.on_thumbnails_check_box_changed)
155 self.address_edit.textChanged.connect(self.set_urls)156 self.address_edit.textChanged.connect(self.set_urls)
156157
157 def retranslateUi(self):158 def retranslate_ui(self):
158 self.tab_title_visible = translate('RemotePlugin.RemoteTab', 'Remote Interface')159 self.tab_title_visible = translate('RemotePlugin.RemoteTab', 'Remote Interface')
159 self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))160 self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))
160 self.address_label.setText(translate('RemotePlugin.RemoteTab', 'Serve on IP address:'))161 self.address_label.setText(translate('RemotePlugin.RemoteTab', 'Serve on IP address:'))
161162
=== modified file 'openlp/core/api/websockets.py'
--- openlp/core/api/websockets.py 2018-01-07 17:50:29 +0000
+++ openlp/core/api/websockets.py 2019-02-13 20:54:40 +0000
@@ -35,6 +35,7 @@
35from openlp.core.common.settings import Settings35from openlp.core.common.settings import Settings
36from openlp.core.threading import ThreadWorker, run_thread36from openlp.core.threading import ThreadWorker, run_thread
3737
38
38log = logging.getLogger(__name__)39log = logging.getLogger(__name__)
3940
4041
4142
=== modified file 'openlp/core/app.py'
--- openlp/core/app.py 2018-10-25 16:38:39 +0000
+++ openlp/core/app.py 2019-02-13 20:54:40 +0000
@@ -33,27 +33,28 @@
33from datetime import datetime33from datetime import datetime
34from traceback import format_exception34from traceback import format_exception
3535
36from PyQt5 import QtCore, QtWidgets36from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets # noqa
3737
38from openlp.core.state import State38from openlp.core.state import State
39from openlp.core.common import is_macosx, is_win39from openlp.core.common import is_macosx, is_win
40from openlp.core.common.applocation import AppLocation40from openlp.core.common.applocation import AppLocation
41from openlp.core.loader import loader41from openlp.core.loader import loader
42from openlp.core.common.i18n import LanguageManager, UiStrings, translate42from openlp.core.common.i18n import LanguageManager, UiStrings, translate
43from openlp.core.common.path import create_paths, copytree43from openlp.core.common.path import copytree, create_paths
44from openlp.core.common.registry import Registry44from openlp.core.common.registry import Registry
45from openlp.core.common.settings import Settings45from openlp.core.common.settings import Settings
46from openlp.core.display.screens import ScreenList46from openlp.core.display.screens import ScreenList
47from openlp.core.resources import qInitResources47from openlp.core.resources import qInitResources
48from openlp.core.ui.splashscreen import SplashScreen48from openlp.core.server import Server
49from openlp.core.ui.exceptionform import ExceptionForm49from openlp.core.ui.exceptionform import ExceptionForm
50from openlp.core.ui.firsttimeform import FirstTimeForm50from openlp.core.ui.firsttimeform import FirstTimeForm
51from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm51from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
52from openlp.core.ui.mainwindow import MainWindow52from openlp.core.ui.mainwindow import MainWindow
53from openlp.core.ui.splashscreen import SplashScreen
53from openlp.core.ui.style import get_application_stylesheet54from openlp.core.ui.style import get_application_stylesheet
54from openlp.core.server import Server
55from openlp.core.version import check_for_update, get_version55from openlp.core.version import check_for_update, get_version
5656
57
57__all__ = ['OpenLP', 'main']58__all__ = ['OpenLP', 'main']
5859
5960
@@ -74,7 +75,8 @@
74 """75 """
75 self.is_event_loop_active = True76 self.is_event_loop_active = True
76 result = QtWidgets.QApplication.exec()77 result = QtWidgets.QApplication.exec()
77 self.server.close_server()78 if hasattr(self, 'server'):
79 self.server.close_server()
78 return result80 return result
7981
80 def run(self, args):82 def run(self, args):
@@ -317,7 +319,7 @@
317 file_path = log_path / 'openlp.log'319 file_path = log_path / 'openlp.log'
318 # TODO: FileHandler accepts a Path object in Py3.6320 # TODO: FileHandler accepts a Path object in Py3.6
319 logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8')321 logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8')
320 logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))322 logfile.setFormatter(logging.Formatter('%(asctime)s %(threadName)s %(name)-55s %(levelname)-8s %(message)s'))
321 log.addHandler(logfile)323 log.addHandler(logfile)
322 if log.isEnabledFor(logging.DEBUG):324 if log.isEnabledFor(logging.DEBUG):
323 print('Logging to: {name}'.format(name=file_path))325 print('Logging to: {name}'.format(name=file_path))
@@ -330,7 +332,8 @@
330 :param args: Some args332 :param args: Some args
331 """333 """
332 args = parse_options(args)334 args = parse_options(args)
333 qt_args = []335 qt_args = ['--disable-web-security']
336 # qt_args = []
334 if args and args.loglevel.lower() in ['d', 'debug']:337 if args and args.loglevel.lower() in ['d', 'debug']:
335 log.setLevel(logging.DEBUG)338 log.setLevel(logging.DEBUG)
336 elif args and args.loglevel.lower() in ['w', 'warning']:339 elif args and args.loglevel.lower() in ['w', 'warning']:
337340
=== modified file 'openlp/core/common/actions.py'
--- openlp/core/common/actions.py 2018-10-13 10:24:01 +0000
+++ openlp/core/common/actions.py 2019-02-13 20:54:40 +0000
@@ -29,6 +29,7 @@
2929
30from openlp.core.common.settings import Settings30from openlp.core.common.settings import Settings
3131
32
32log = logging.getLogger(__name__)33log = logging.getLogger(__name__)
3334
3435
@@ -113,7 +114,6 @@
113 if item[1] == action:114 if item[1] == action:
114 self.actions.remove(item)115 self.actions.remove(item)
115 return116 return
116 log.warning('Action "{action}" does not exist.'.format(action=action))
117117
118118
119class CategoryList(object):119class CategoryList(object):
120120
=== modified file 'openlp/core/common/applocation.py'
--- openlp/core/common/applocation.py 2018-10-07 23:34:00 +0000
+++ openlp/core/common/applocation.py 2019-02-13 20:54:40 +0000
@@ -29,10 +29,11 @@
29import appdirs29import appdirs
3030
31import openlp31import openlp
32from openlp.core.common import get_frozen_path, is_win, is_macosx32from openlp.core.common import get_frozen_path, is_macosx, is_win
33from openlp.core.common.path import Path, create_paths33from openlp.core.common.path import Path, create_paths
34from openlp.core.common.settings import Settings34from openlp.core.common.settings import Settings
3535
36
36log = logging.getLogger(__name__)37log = logging.getLogger(__name__)
3738
38FROZEN_APP_PATH = Path(sys.argv[0]).parent39FROZEN_APP_PATH = Path(sys.argv[0]).parent
3940
=== modified file 'openlp/core/common/db.py'
--- openlp/core/common/db.py 2017-12-29 09:15:48 +0000
+++ openlp/core/common/db.py 2019-02-13 20:54:40 +0000
@@ -27,6 +27,7 @@
2727
28import sqlalchemy28import sqlalchemy
2929
30
30log = logging.getLogger(__name__)31log = logging.getLogger(__name__)
3132
3233
3334
=== modified file 'openlp/core/common/httputils.py'
--- openlp/core/common/httputils.py 2018-06-08 20:55:20 +0000
+++ openlp/core/common/httputils.py 2019-02-13 20:54:40 +0000
@@ -34,6 +34,7 @@
34from openlp.core.common.registry import Registry34from openlp.core.common.registry import Registry
35from openlp.core.common.settings import ProxyMode, Settings35from openlp.core.common.settings import ProxyMode, Settings
3636
37
37log = logging.getLogger(__name__ + '.__init__')38log = logging.getLogger(__name__ + '.__init__')
3839
39USER_AGENTS = {40USER_AGENTS = {
4041
=== modified file 'openlp/core/common/i18n.py'
--- openlp/core/common/i18n.py 2018-10-27 11:05:41 +0000
+++ openlp/core/common/i18n.py 2019-02-13 20:54:40 +0000
@@ -29,10 +29,11 @@
2929
30from PyQt5 import QtCore, QtWidgets30from PyQt5 import QtCore, QtWidgets
3131
32from openlp.core.common import is_win, is_macosx32from openlp.core.common import is_macosx, is_win
33from openlp.core.common.applocation import AppLocation33from openlp.core.common.applocation import AppLocation
34from openlp.core.common.settings import Settings34from openlp.core.common.settings import Settings
3535
36
36log = logging.getLogger(__name__)37log = logging.getLogger(__name__)
3738
3839
3940
=== modified file 'openlp/core/common/mixins.py'
--- openlp/core/common/mixins.py 2018-11-18 17:29:47 +0000
+++ openlp/core/common/mixins.py 2019-02-13 20:54:40 +0000
@@ -28,6 +28,7 @@
28from openlp.core.common import is_win, trace_error_handler28from openlp.core.common import is_win, trace_error_handler
29from openlp.core.common.registry import Registry29from openlp.core.common.registry import Registry
3030
31
31DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event', 'on_controller_size_changed',32DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event', 'on_controller_size_changed',
32 'preview_size_changed', 'resizeEvent']33 'preview_size_changed', 'resizeEvent']
3334
3435
=== modified file 'openlp/core/common/path.py'
--- openlp/core/common/path.py 2018-08-12 11:14:47 +0000
+++ openlp/core/common/path.py 2019-02-13 20:54:40 +0000
@@ -25,6 +25,7 @@
2525
26from openlp.core.common import is_win26from openlp.core.common import is_win
2727
28
28if is_win():29if is_win():
29 from pathlib import WindowsPath as PathVariant # pragma: nocover30 from pathlib import WindowsPath as PathVariant # pragma: nocover
30else:31else:
3132
=== modified file 'openlp/core/common/registry.py'
--- openlp/core/common/registry.py 2018-10-25 16:38:39 +0000
+++ openlp/core/common/registry.py 2019-02-13 20:54:40 +0000
@@ -27,6 +27,7 @@
2727
28from openlp.core.common import de_hump, trace_error_handler28from openlp.core.common import de_hump, trace_error_handler
2929
30
30log = logging.getLogger(__name__)31log = logging.getLogger(__name__)
3132
3233
@@ -143,6 +144,7 @@
143 if event in self.functions_list:144 if event in self.functions_list:
144 for function in self.functions_list[event]:145 for function in self.functions_list[event]:
145 try:146 try:
147 log.debug('Running function {} for {}'.format(function, event))
146 result = function(*args, **kwargs)148 result = function(*args, **kwargs)
147 if result:149 if result:
148 results.append(result)150 results.append(result)
149151
=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py 2018-10-24 19:35:22 +0000
+++ openlp/core/common/settings.py 2019-02-13 20:54:40 +0000
@@ -33,7 +33,8 @@
3333
34from openlp.core.common import SlideLimits, ThemeLevel, is_linux, is_win34from openlp.core.common import SlideLimits, ThemeLevel, is_linux, is_win
35from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder35from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
36from openlp.core.common.path import Path, str_to_path, files_to_paths36from openlp.core.common.path import Path, files_to_paths, str_to_path
37
3738
38log = logging.getLogger(__name__)39log = logging.getLogger(__name__)
3940
@@ -70,6 +71,34 @@
70 return string71 return string
7172
7273
74def upgrade_screens(number, x_position, y_position, height, width, can_override, is_display_screen):
75 """
76 Upgrade them monitor setting from a few single entries to a composite JSON entry
77
78 :param int number: The old monitor number
79 :param int x_position: The X position
80 :param int y_position: The Y position
81 :param bool can_override: Are the screen positions overridden
82 :param bool is_display_screen: Is this a display screen
83 :returns dict: Dictionary with the new value
84 """
85 geometry_key = 'geometry'
86 if can_override:
87 geometry_key = 'custom_geometry'
88 return {
89 number: {
90 'number': number,
91 geometry_key: {
92 'x': x_position,
93 'y': y_position,
94 'height': height,
95 'width': width
96 },
97 'is_display': is_display_screen
98 }
99 }
100
101
73class Settings(QtCore.QSettings):102class Settings(QtCore.QSettings):
74 """103 """
75 Class to wrap QSettings.104 Class to wrap QSettings.
@@ -175,6 +204,7 @@
175 # circular dependency.204 # circular dependency.
176 'core/display on monitor': True,205 'core/display on monitor': True,
177 'core/override position': False,206 'core/override position': False,
207 'core/monitor': {},
178 'core/application version': '0.0',208 'core/application version': '0.0',
179 'images/background color': '#000000',209 'images/background color': '#000000',
180 'media/players': 'system,webkit',210 'media/players': 'system,webkit',
@@ -276,6 +306,8 @@
276 ('songuasge/db hostname', 'songusage/db hostname', []),306 ('songuasge/db hostname', 'songusage/db hostname', []),
277 ('songuasge/db database', 'songusage/db database', []),307 ('songuasge/db database', 'songusage/db database', []),
278 ('presentations / Powerpoint Viewer', '', []),308 ('presentations / Powerpoint Viewer', '', []),
309 (['core/monitor', 'core/x position', 'core/y position', 'core/height', 'core/width', 'core/override',
310 'core/display on monitor'], 'core/screens', [(upgrade_screens, [1, 0, 0, None, None, False, False])]),
279 ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4!311 ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4!
280 ('bibles/proxy address', '', []),312 ('bibles/proxy address', '', []),
281 ('bibles/proxy username', '', []),313 ('bibles/proxy username', '', []),
@@ -545,7 +577,7 @@
545 :param value: The value to save577 :param value: The value to save
546 :rtype: None578 :rtype: None
547 """579 """
548 if isinstance(value, Path) or (isinstance(value, list) and value and isinstance(value[0], Path)):580 if isinstance(value, (Path, dict)) or (isinstance(value, list) and value and isinstance(value[0], Path)):
549 value = json.dumps(value, cls=OpenLPJsonEncoder)581 value = json.dumps(value, cls=OpenLPJsonEncoder)
550 super().setValue(key, value)582 super().setValue(key, value)
551583
@@ -568,8 +600,11 @@
568 # An empty list saved to the settings results in a None type being returned.600 # An empty list saved to the settings results in a None type being returned.
569 elif isinstance(default_value, list):601 elif isinstance(default_value, list):
570 return []602 return []
603 # An empty dictionary saved to the settings results in a None type being returned.
604 elif isinstance(default_value, dict):
605 return {}
571 elif isinstance(setting, str):606 elif isinstance(setting, str):
572 if '__Path__' in setting:607 if '__Path__' in setting or setting.startswith('{'):
573 return json.loads(setting, cls=OpenLPJsonDecoder)608 return json.loads(setting, cls=OpenLPJsonDecoder)
574 # Convert the setting to the correct type.609 # Convert the setting to the correct type.
575 if isinstance(default_value, bool):610 if isinstance(default_value, bool):
@@ -578,6 +613,8 @@
578 # Sometimes setting is string instead of a boolean.613 # Sometimes setting is string instead of a boolean.
579 return setting == 'true'614 return setting == 'true'
580 if isinstance(default_value, int):615 if isinstance(default_value, int):
616 if setting is None:
617 return 0
581 return int(setting)618 return int(setting)
582 return setting619 return setting
583620
584621
=== added directory 'openlp/core/display/html'
=== added file 'openlp/core/display/html/black.css'
--- openlp/core/display/html/black.css 1970-01-01 00:00:00 +0000
+++ openlp/core/display/html/black.css 2019-02-13 20:54:40 +0000
@@ -0,0 +1,292 @@
1/**
2 * Black theme for reveal.js. This is the opposite of the 'white' theme.
3 *
4 * By Hakim El Hattab, http://hakim.se
5 */
6@import url(../../lib/font/source-sans-pro/source-sans-pro.css);
7section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 {
8 color: #222; }
9
10/*********************************************
11 * GLOBAL STYLES
12 *********************************************/
13body {
14 background: #222;
15 background-color: #222; }
16
17.reveal {
18 font-family: "Source Sans Pro", Helvetica, sans-serif;
19 font-size: 42px;
20 font-weight: normal;
21 color: #fff; }
22
23::selection {
24 color: #fff;
25 background: #bee4fd;
26 text-shadow: none; }
27
28::-moz-selection {
29 color: #fff;
30 background: #bee4fd;
31 text-shadow: none; }
32
33.reveal .slides > section,
34.reveal .slides > section > section {
35 line-height: 1.3;
36 font-weight: inherit; }
37
38/*********************************************
39 * HEADERS
40 *********************************************/
41.reveal h1,
42.reveal h2,
43.reveal h3,
44.reveal h4,
45.reveal h5,
46.reveal h6 {
47 margin: 0 0 20px 0;
48 color: #fff;
49 font-family: "Source Sans Pro", Helvetica, sans-serif;
50 font-weight: 600;
51 line-height: 1.2;
52 letter-spacing: normal;
53 text-transform: uppercase;
54 text-shadow: none;
55 word-wrap: break-word; }
56
57.reveal h1 {
58 font-size: 2.5em; }
59
60.reveal h2 {
61 font-size: 1.6em; }
62
63.reveal h3 {
64 font-size: 1.3em; }
65
66.reveal h4 {
67 font-size: 1em; }
68
69.reveal h1 {
70 text-shadow: none; }
71
72/*********************************************
73 * OTHER
74 *********************************************/
75.reveal p {
76 margin: 20px 0;
77 line-height: 1.3; }
78
79/* Ensure certain elements are never larger than the slide itself */
80.reveal img,
81.reveal video,
82.reveal iframe {
83 max-width: 95%;
84 max-height: 95%; }
85
86.reveal strong,
87.reveal b {
88 font-weight: bold; }
89
90.reveal em {
91 font-style: italic; }
92
93.reveal ol,
94.reveal dl,
95.reveal ul {
96 display: inline-block;
97 text-align: left;
98 margin: 0 0 0 1em; }
99
100.reveal ol {
101 list-style-type: decimal; }
102
103.reveal ul {
104 list-style-type: disc; }
105
106.reveal ul ul {
107 list-style-type: square; }
108
109.reveal ul ul ul {
110 list-style-type: circle; }
111
112.reveal ul ul,
113.reveal ul ol,
114.reveal ol ol,
115.reveal ol ul {
116 display: block;
117 margin-left: 40px; }
118
119.reveal dt {
120 font-weight: bold; }
121
122.reveal dd {
123 margin-left: 40px; }
124
125.reveal q,
126.reveal blockquote {
127 quotes: none; }
128
129.reveal blockquote {
130 display: block;
131 position: relative;
132 width: 70%;
133 margin: 20px auto;
134 padding: 5px;
135 font-style: italic;
136 background: rgba(255, 255, 255, 0.05);
137 box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
138
139.reveal blockquote p:first-child,
140.reveal blockquote p:last-child {
141 display: inline-block; }
142
143.reveal q {
144 font-style: italic; }
145
146.reveal pre {
147 display: block;
148 position: relative;
149 width: 90%;
150 margin: 20px auto;
151 text-align: left;
152 font-size: 0.55em;
153 font-family: monospace;
154 line-height: 1.2em;
155 word-wrap: break-word;
156 box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
157
158.reveal code {
159 font-family: monospace; }
160
161.reveal pre code {
162 display: block;
163 padding: 5px;
164 overflow: auto;
165 max-height: 400px;
166 word-wrap: normal; }
167
168.reveal table {
169 margin: auto;
170 border-collapse: collapse;
171 border-spacing: 0; }
172
173.reveal table th {
174 font-weight: bold; }
175
176.reveal table th,
177.reveal table td {
178 text-align: left;
179 padding: 0.2em 0.5em 0.2em 0.5em;
180 border-bottom: 1px solid; }
181
182.reveal table th[align="center"],
183.reveal table td[align="center"] {
184 text-align: center; }
185
186.reveal table th[align="right"],
187.reveal table td[align="right"] {
188 text-align: right; }
189
190.reveal table tbody tr:last-child th,
191.reveal table tbody tr:last-child td {
192 border-bottom: none; }
193
194.reveal sup {
195 vertical-align: super; }
196
197.reveal sub {
198 vertical-align: sub; }
199
200.reveal small {
201 display: inline-block;
202 font-size: 0.6em;
203 line-height: 1.2em;
204 vertical-align: top; }
205
206.reveal small * {
207 vertical-align: top; }
208
209/*********************************************
210 * LINKS
211 *********************************************/
212.reveal a {
213 color: #42affa;
214 text-decoration: none;
215 -webkit-transition: color .15s ease;
216 -moz-transition: color .15s ease;
217 transition: color .15s ease; }
218
219.reveal a:hover {
220 color: #8dcffc;
221 text-shadow: none;
222 border: none; }
223
224.reveal .roll span:after {
225 color: #fff;
226 background: #068de9; }
227
228/*********************************************
229 * IMAGES
230 *********************************************/
231.reveal section img {
232 margin: 15px 0px;
233 background: rgba(255, 255, 255, 0.12);
234 border: 4px solid #fff;
235 box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
236
237.reveal section img.plain {
238 border: 0;
239 box-shadow: none; }
240
241.reveal a img {
242 -webkit-transition: all .15s linear;
243 -moz-transition: all .15s linear;
244 transition: all .15s linear; }
245
246.reveal a:hover img {
247 background: rgba(255, 255, 255, 0.2);
248 border-color: #42affa;
249 box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
250
251/*********************************************
252 * NAVIGATION CONTROLS
253 *********************************************/
254.reveal .controls .navigate-left,
255.reveal .controls .navigate-left.enabled {
256 border-right-color: #42affa; }
257
258.reveal .controls .navigate-right,
259.reveal .controls .navigate-right.enabled {
260 border-left-color: #42affa; }
261
262.reveal .controls .navigate-up,
263.reveal .controls .navigate-up.enabled {
264 border-bottom-color: #42affa; }
265
266.reveal .controls .navigate-down,
267.reveal .controls .navigate-down.enabled {
268 border-top-color: #42affa; }
269
270.reveal .controls .navigate-left.enabled:hover {
271 border-right-color: #8dcffc; }
272
273.reveal .controls .navigate-right.enabled:hover {
274 border-left-color: #8dcffc; }
275
276.reveal .controls .navigate-up.enabled:hover {
277 border-bottom-color: #8dcffc; }
278
279.reveal .controls .navigate-down.enabled:hover {
280 border-top-color: #8dcffc; }
281
282/*********************************************
283 * PROGRESS BAR
284 *********************************************/
285.reveal .progress {
286 background: rgba(0, 0, 0, 0.2); }
287
288.reveal .progress span {
289 background: #42affa;
290 -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
291 -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
292 transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
0293
=== added file 'openlp/core/display/html/checkerboard.png'
1Binary files openlp/core/display/html/checkerboard.png 1970-01-01 00:00:00 +0000 and openlp/core/display/html/checkerboard.png 2019-02-13 20:54:40 +0000 differ294Binary files openlp/core/display/html/checkerboard.png 1970-01-01 00:00:00 +0000 and openlp/core/display/html/checkerboard.png 2019-02-13 20:54:40 +0000 differ
=== added file 'openlp/core/display/html/display.html'
--- openlp/core/display/html/display.html 1970-01-01 00:00:00 +0000
+++ openlp/core/display/html/display.html 2019-02-13 20:54:40 +0000
@@ -0,0 +1,39 @@
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>Display Window</title>
5 <link href="reveal.css" rel="stylesheet">
6 <style type="text/css">
7 body {
8 background: transparent !important;
9 color: #fff !important;
10 }
11 sup {
12 vertical-align: super !important;
13 font-size: smaller !important;
14 }
15 .reveal .slides > section,
16 .reveal .slides > section > section {
17 padding: 0;
18 }
19 .reveal > .backgrounds > .present {
20 visibility: hidden !important;
21 }
22 #global-background {
23 display: block;
24 visibility: visible;
25 z-index: -1;
26 }
27 </style>
28 <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
29 <script type="text/javascript" src="reveal.js"></script>
30 <script type="text/javascript" src="display.js"></script>
31 </head>
32 <body>
33 <div class="reveal">
34 <div id="global-background" class="slide-background present" data-loaded="true"></div>
35 <div class="slides"></div>
36 <div class="footer"></div>
37 </div>
38 </body>
39</html>
040
=== added file 'openlp/core/display/html/display.js'
--- openlp/core/display/html/display.js 1970-01-01 00:00:00 +0000
+++ openlp/core/display/html/display.js 2019-02-13 20:54:40 +0000
@@ -0,0 +1,789 @@
1/**
2 * display.js is the main Javascript file that is used to drive the display.
3 */
4
5/**
6 * Background type enumeration
7 */
8var BackgroundType = {
9 Transparent: "transparent",
10 Solid: "solid",
11 Gradient: "gradient",
12 Video: "video",
13 Image: "image"
14};
15
16/**
17 * Gradient type enumeration
18 */
19var GradientType = {
20 Horizontal: "horizontal",
21 LeftTop: "leftTop",
22 LeftBottom: "leftBottom",
23 Vertical: "vertical",
24 Circular: "circular"
25};
26
27/**
28 * Horizontal alignment enumeration
29 */
30var HorizontalAlign = {
31 Left: "left",
32 Right: "right",
33 Center: "center",
34 Justify: "justify"
35};
36
37/**
38 * Vertical alignment enumeration
39 */
40var VerticalAlign = {
41 Top: "top",
42 Middle: "middle",
43 Bottom: "bottom"
44};
45
46/**
47 * Audio state enumeration
48 */
49var AudioState = {
50 Playing: "playing",
51 Paused: "paused",
52 Stopped: "stopped"
53};
54
55/**
56 * Return an array of elements based on the selector query
57 * @param {string} selector - The selector to find elements
58 * @returns {array} An array of matching elements
59 */
60function $(selector) {
61 return Array.from(document.querySelectorAll(selector));
62}
63
64/**
65 * Build linear gradient CSS
66 * @private
67 * @param {string} startDir - Starting direction
68 * @param {string} endDir - Ending direction
69 * @param {string} startColor - The starting color
70 * @param {string} endColor - The ending color
71 * @returns {string} A string of the gradient CSS
72 */
73function _buildLinearGradient(startDir, endDir, startColor, endColor) {
74 return "-webkit-gradient(linear, " + startDir + ", " + endDir + ", from(" + startColor + "), to(" + endColor + ")) fixed";
75}
76
77/**
78 * Build radial gradient CSS
79 * @private
80 * @param {string} width - Width of the gradient
81 * @param {string} startColor - The starting color
82 * @param {string} endColor - The ending color
83 * @returns {string} A string of the gradient CSS
84 */
85function _buildRadialGradient(width, startColor, endColor) {
86 return "-webkit-gradient(radial, " + width + " 50%, 100, " + width + " 50%, " + width + ", from(" + startColor + "), to(" + endColor + ")) fixed";
87}
88
89/**
90 * Get a style value from an element (computed or manual)
91 * @private
92 * @param {Object} element - The element whose style we want
93 * @param {string} style - The name of the style we want
94 * @returns {(Number|string)} The style value (type depends on the style)
95 */
96function _getStyle(element, style) {
97 return document.defaultView.getComputedStyle(element).getPropertyValue(style);
98}
99
100/**
101 * Convert newlines to <br> tags
102 * @private
103 * @param {string} text - The text to parse
104 * @returns {string} The text now with <br> tags
105 */
106function _nl2br(text) {
107 return text.replace("\r\n", "\n").replace("\n", "<br>");
108}
109
110/**
111 * Prepare text by creating paragraphs and calling _nl2br to convert newlines to <br> tags
112 * @private
113 * @param {string} text - The text to parse
114 * @returns {string} The text now with <p> and <br> tags
115 */
116function _prepareText(text) {
117 return "<p>" + _nl2br(text) + "</p>";
118}
119
120/**
121 * The paths we get are JSON versions of Python Path objects, so let's just fix that.
122 * @private
123 * @param {object} path - The Path object
124 * @returns {string} The actual file path
125 */
126function _pathToString(path) {
127 var filename = path.__Path__.join("/").replace("//", "/");
128 if (!filename.startsWith("/")) {
129 filename = "/" + filename;
130 }
131 return filename;
132}
133
134/**
135 * An audio player with a play list
136 */
137var AudioPlayer = function (audioElement) {
138 this._audioElement = null;
139 this._eventListeners = {};
140 this._playlist = [];
141 this._currentTrack = null;
142 this._canRepeat = false;
143 this._state = AudioState.Stopped;
144 this.createAudioElement();
145};
146
147/**
148 * Call all listeners associated with this event
149 * @private
150 * @param {object} event - The event that was emitted
151 */
152AudioPlayer.prototype._callListener = function (event) {
153 if (this._eventListeners.hasOwnProperty(event.type)) {
154 this._eventListeners[event.type].forEach(function (listener) {
155 listener(event);
156 });
157 }
158 else {
159 console.warn("Received unknown event \"" + event.type + "\", doing nothing.");
160 }
161};
162
163/**
164 * Create the <audio> element that is used to play the audio
165 */
166AudioPlayer.prototype.createAudioElement = function () {
167 this._audioElement = document.createElement("audio");
168 this._audioElement.addEventListener("ended", this.onEnded);
169 this._audioElement.addEventListener("ended", this._callListener);
170 this._audioElement.addEventListener("timeupdate", this._callListener);
171 this._audioElement.addEventListener("volumechange", this._callListener);
172 this._audioElement.addEventListener("durationchange", this._callListener);
173 this._audioElement.addEventListener("loadeddata", this._callListener);
174 document.addEventListener("complete", function(event) {
175 document.body.appendChild(this._audioElement);
176 });
177};
178AudioPlayer.prototype.addEventListener = function (eventType, listener) {
179 this._eventListeners[eventType] = this._eventListeners[eventType] || [];
180 this._eventListeners[eventType].push(listener);
181};
182AudioPlayer.prototype.onEnded = function (event) {
183 this.nextTrack();
184};
185AudioPlayer.prototype.setCanRepeat = function (canRepeat) {
186 this._canRepeat = canRepeat;
187};
188AudioPlayer.prototype.clearTracks = function () {
189 this._playlist = [];
190};
191AudioPlayer.prototype.addTrack = function (track) {
192 this._playlist.push(track);
193};
194AudioPlayer.prototype.nextTrack = function () {
195 if (!!this._currentTrack) {
196 var trackIndex = this._playlist.indexOf(this._currentTrack);
197 if ((trackIndex + 1 >= this._playlist.length) && this._canRepeat) {
198 this.play(this._playlist[0]);
199 }
200 else if (trackIndex + 1 < this._playlist.length) {
201 this.play(this._playlist[trackIndex + 1]);
202 }
203 else {
204 this.stop();
205 }
206 }
207 else if (this._playlist.length > 0) {
208 this.play(this._playlist[0]);
209 }
210 else {
211 console.warn("No tracks in playlist, doing nothing.");
212 }
213};
214AudioPlayer.prototype.play = function () {
215 if (arguments.length > 0) {
216 this._currentTrack = arguments[0];
217 this._audioElement.src = this._currentTrack;
218 this._audioElement.play();
219 this._state = AudioState.Playing;
220 }
221 else if (this._state == AudioState.Paused) {
222 this._audioElement.play();
223 this._state = AudioState.Playing;
224 }
225 else {
226 console.warn("No track currently paused and no track specified, doing nothing.");
227 }
228};
229
230/**
231 * Pause
232 */
233AudioPlayer.prototype.pause = function () {
234 this._audioElement.pause();
235 this._state = AudioState.Paused;
236};
237
238/**
239 * Stop playing
240 */
241AudioPlayer.prototype.stop = function () {
242 this._audioElement.pause();
243 this._audioElement.src = "";
244 this._state = AudioState.Stopped;
245};
246
247/**
248 * The Display object is what we use from OpenLP
249 */
250var Display = {
251 _slides: {},
252 _revealConfig: {
253 margin: 0.0,
254 minScale: 1.0,
255 maxScale: 1.0,
256 controls: false,
257 progress: false,
258 history: false,
259 overview: false,
260 center: false,
261 help: false,
262 transition: "none",
263 backgroundTransition: "none",
264 viewDistance: 9999,
265 width: "100%",
266 height: "100%"
267 },
268 /**
269 * Start up reveal and do any other initialisation
270 */
271 init: function () {
272 Reveal.initialize(this._revealConfig);
273 },
274 /**
275 * Reinitialise Reveal
276 */
277 reinit: function () {
278 Reveal.reinitialize();
279 },
280 /**
281 * Set the transition type
282 * @param {string} transitionType - Can be one of "none", "fade", "slide", "convex", "concave", "zoom"
283 */
284 setTransition: function (transitionType) {
285 Reveal.configure({"transition": transitionType});
286 },
287 /**
288 * Clear the current list of slides
289 */
290 clearSlides: function () {
291 $(".slides")[0].innerHTML = "";
292 this._slides = {};
293 },
294 /**
295 * Checks if the present slide content fits within the slide
296 */
297 doesContentFit: function () {
298 console.debug("scrollHeight: " + $(".slides")[0].scrollHeight + ", clientHeight: " + $(".slides")[0].clientHeight);
299 return $(".slides")[0].clientHeight >= $(".slides")[0].scrollHeight;
300 },
301 /**
302 * Generate the OpenLP startup splashscreen
303 * @param {string} bg_color - The background color
304 * @param {string} image - Path to the splash image
305 */
306 setStartupSplashScreen: function(bg_color, image) {
307 Display.clearSlides();
308 var globalBackground = $("#global-background")[0];
309 globalBackground.style.cssText = "";
310 globalBackground.style.setProperty("background", bg_color);
311 var slidesDiv = $(".slides")[0];
312 var section = document.createElement("section");
313 section.setAttribute("id", 0);
314 section.setAttribute("data-background", bg_color);
315 section.setAttribute("style", "height: 100%; width: 100%; position: relative;");
316 var img = document.createElement('img');
317 img.src = image;
318 img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto;");
319 section.appendChild(img);
320 slidesDiv.appendChild(section);
321 Display._slides['0'] = 0;
322 Display.reinit();
323 },
324 /**
325 * Set fullscreen image from path
326 * @param {string} bg_color - The background color
327 * @param {string} image - Path to the image
328 */
329 setFullscreenImage: function(bg_color, image) {
330 Display.clearSlides();
331 var globalBackground = $("#global-background")[0];
332 globalBackground.style.cssText = "";
333 globalBackground.style.setProperty("background", bg_color);
334 var slidesDiv = $(".slides")[0];
335 var section = document.createElement("section");
336 section.setAttribute("id", 0);
337 section.setAttribute("data-background", bg_color);
338 section.setAttribute("style", "height: 100%; width: 100%;");
339 var img = document.createElement('img');
340 img.src = image;
341 img.setAttribute("style", "height: 100%; width: 100%");
342 section.appendChild(img);
343 slidesDiv.appendChild(section);
344 Display._slides['0'] = 0;
345 Display.reinit();
346 },
347 /**
348 * Set fullscreen image from base64 data
349 * @param {string} bg_color - The background color
350 * @param {string} image - Path to the image
351 */
352 setFullscreenImageFromData: function(bg_color, image_data) {
353 Display.clearSlides();
354 var globalBackground = $("#global-background")[0];
355 globalBackground.style.cssText = "";
356 globalBackground.style.setProperty("background", bg_color);
357 var slidesDiv = $(".slides")[0];
358 var section = document.createElement("section");
359 section.setAttribute("id", 0);
360 section.setAttribute("data-background", bg_color);
361 section.setAttribute("style", "height: 100%; width: 100%;");
362 var img = document.createElement('img');
363 img.src = 'data:image/png;base64,' + image_data;
364 img.setAttribute("style", "height: 100%; width: 100%");
365 section.appendChild(img);
366 slidesDiv.appendChild(section);
367 Display._slides['0'] = 0;
368 Display.reinit();
369 },
370 /**
371 * Display an alert
372 * @param {string} text - The alert text
373 * @param {int} location - The location of the text (top, middle or bottom)
374 */
375 alert: function (text, location) {
376 console.debug(" alert text: " + text, ", location: " + location);
377 /*
378 * The implementation should show an alert.
379 * It should be able to handle receiving a new alert before a previous one is "finished", basically queueing it.
380 */
381 return;
382},
383
384 /**
385 * Add a slides. If the slide exists but the HTML is different, update the slide.
386 * @param {string} verse - The verse number, e.g. "v1"
387 * @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
388 * @param {string} footer_text - The HTML for the footer"
389 * @param {bool} [reinit=true] - Re-initialize Reveal. Defaults to true.
390 */
391 addTextSlide: function (verse, text, footer_text) {
392 var html = _prepareText(text);
393 if (this._slides.hasOwnProperty(verse)) {
394 var slide = $("#" + verse)[0];
395 if (slide.innerHTML != html) {
396 slide.innerHTML = html;
397 }
398 }
399 else {
400 var slidesDiv = $(".slides")[0];
401 var slide = document.createElement("section");
402 slide.setAttribute("id", verse);
403 slide.innerHTML = html;
404 slidesDiv.appendChild(slide);
405 var slides = $(".slides > section");
406 this._slides[verse] = slides.length - 1;
407
408 console.debug(" footer_text: " + footer_text);
409
410 var footerDiv = $(".footer")[0];
411 footerDiv.innerHTML = footer_text;
412 }
413 if ((arguments.length > 3) && (arguments[3] === true)) {
414 this.reinit();
415 }
416 else if (arguments.length == 3) {
417 this.reinit();
418 }
419 },
420 /**
421 * Set text slides.
422 * @param {Object[]} slides - A list of slides to add as JS objects: {"verse": "v1", "text": "line 1\nline2"}
423 */
424 setTextSlides: function (slides) {
425 Display.clearSlides();
426 slides.forEach(function (slide) {
427 Display.addTextSlide(slide.verse, slide.text, slide.footer, false);
428 });
429 Display.reinit();
430 Display.goToSlide(0);
431 },
432 /**
433 * Set image slides
434 * @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}]
435 */
436 setImageSlides: function (slides) {
437 Display.clearSlides();
438 var slidesDiv = $(".slides")[0];
439 slides.forEach(function (slide, index) {
440 var section = document.createElement("section");
441 section.setAttribute("id", index);
442 section.setAttribute("data-background", "#000");
443 var img = document.createElement('img');
444 img.src = slide["path"];
445 img.setAttribute("style", "height: 100%; width: 100%;");
446 section.appendChild(img);
447 slidesDiv.appendChild(section);
448 Display._slides[index.toString()] = index;
449 });
450 Display.reinit();
451 },
452 /**
453 * Set a video
454 * @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
455 */
456 setVideo: function (video) {
457 this.clearSlides();
458 var section = document.createElement("section");
459 section.setAttribute("data-background", "#000");
460 var videoElement = document.createElement("video");
461 videoElement.src = video["path"];
462 videoElement.preload = "auto";
463 videoElement.setAttribute("id", "video");
464 videoElement.setAttribute("style", "height: 100%; width: 100%;");
465 videoElement.autoplay = false;
466 // All the update methods below are Python functions, hence not camelCase
467 videoElement.addEventListener("durationchange", function (event) {
468 mediaWatcher.update_duration(event.target.duration);
469 });
470 videoElement.addEventListener("timeupdate", function (event) {
471 mediaWatcher.update_progress(event.target.currentTime);
472 });
473 videoElement.addEventListener("volumeupdate", function (event) {
474 mediaWatcher.update_volume(event.target.volume);
475 });
476 videoElement.addEventListener("ratechange", function (event) {
477 mediaWatcher.update_playback_rate(event.target.playbackRate);
478 });
479 videoElement.addEventListener("ended", function (event) {
480 mediaWatcher.has_ended(event.target.ended);
481 });
482 videoElement.addEventListener("muted", function (event) {
483 mediaWatcher.has_muted(event.target.muted);
484 });
485 section.appendChild(videoElement);
486 $(".slides")[0].appendChild(section);
487 this.reinit();
488 },
489 /**
490 * Play a video
491 */
492 playVideo: function () {
493 if ($("#video").length == 1) {
494 $("#video")[0].play();
495 }
496 },
497 /**
498 * Pause a video
499 */
500 pauseVideo: function () {
501 if ($("#video").length == 1) {
502 $("#video")[0].pause();
503 }
504 },
505 /**
506 * Stop a video
507 */
508 stopVideo: function () {
509 if ($("#video").length == 1) {
510 $("#video")[0].pause();
511 $("#video")[0].currentTime = 0.0;
512 }
513 },
514 /**
515 * Go to a particular time in a video
516 * @param seconds The position in seconds to seek to
517 */
518 seekVideo: function (seconds) {
519 if ($("#video").length == 1) {
520 $("#video")[0].currentTime = seconds;
521 }
522 },
523 /**
524 * Set the playback rate of a video
525 * @param rate A Double of the rate. 1.0 => 100% speed, 0.75 => 75% speed, 1.25 => 125% speed, etc.
526 */
527 setPlaybackRate: function (rate) {
528 if ($("#video").length == 1) {
529 $("#video")[0].playbackRate = rate;
530 }
531 },
532 /**
533 * Set the volume
534 * @param level The volume level from 0 to 100.
535 */
536 setVideoVolume: function (level) {
537 if ($("#video").length == 1) {
538 $("#video")[0].volume = level / 100.0;
539 }
540 },
541 /**
542 * Mute the volume
543 */
544 toggleVideoMute: function () {
545 if ($("#video").length == 1) {
546 $("#video")[0].muted = !$("#video")[0].muted;
547 }
548 },
549 /**
550 * Clear the background audio playlist
551 */
552 clearPlaylist: function () {
553 if ($("#background-audio").length == 1) {
554 var audio = $("#background-audio")[0];
555 /* audio.playList */
556 }
557 },
558 /**
559 * Add background audio
560 * @param files The list of files as objects in an array
561 */
562 addBackgroundAudio: function (files) {
563 },
564 /**
565 * Go to a slide.
566 * @param slide The slide number or name, e.g. "v1", 0
567 */
568 goToSlide: function (slide) {
569 if (this._slides.hasOwnProperty(slide)) {
570 Reveal.slide(this._slides[slide]);
571 }
572 else {
573 Reveal.slide(slide);
574 }
575 },
576 /**
577 * Go to the next slide in the list
578 */
579 next: Reveal.next,
580 /**
581 * Go to the previous slide in the list
582 */
583 prev: Reveal.prev,
584 /**
585 * Blank the screen
586 */
587 blankToBlack: function () {
588 if (!Reveal.isPaused()) {
589 Reveal.togglePause();
590 }
591 // var slidesDiv = $(".slides")[0];
592 },
593 /**
594 * Blank to theme
595 */
596 blankToTheme: function () {
597 var slidesDiv = $(".slides")[0];
598 slidesDiv.style.visibility = "hidden";
599 var footerDiv = $(".footer")[0];
600 footerDiv.style.visibility = "hidden";
601 if (Reveal.isPaused()) {
602 Reveal.togglePause();
603 }
604 },
605 /**
606 * Show the screen
607 */
608 show: function () {
609 var slidesDiv = $(".slides")[0];
610 slidesDiv.style.visibility = "visible";
611 var footerDiv = $(".footer")[0];
612 footerDiv.style.visibility = "visible";
613 if (Reveal.isPaused()) {
614 Reveal.togglePause();
615 }
616 },
617 /**
618 * Figure out how many lines can fit on a slide given the font size
619 * @param fontSize The font size in pts
620 */
621 calculateLineCount: function (fontSize) {
622 var p = $(".slides > section > p");
623 if (p.length == 0) {
624 this.addSlide("v1", "Arky arky");
625 p = $(".slides > section > p");
626 }
627 p = p[0];
628 p.style.fontSize = "" + fontSize + "pt";
629 var d = $(".slides")[0];
630 var lh = parseFloat(_getStyle(p, "line-height"));
631 var dh = parseFloat(_getStyle(d, "height"));
632 return Math.floor(dh / lh);
633 },
634 setTheme: function (theme) {
635 this._theme = theme;
636 var slidesDiv = $(".slides")
637 // Set the background
638 var globalBackground = $("#global-background")[0];
639 var backgroundStyle = {};
640 var backgroundHtml = "";
641 switch (theme.background_type) {
642 case BackgroundType.Transparent:
643 backgroundStyle["background"] = "transparent";
644 break;
645 case BackgroundType.Solid:
646 backgroundStyle["background"] = theme.background_color;
647 break;
648 case BackgroundType.Gradient:
649 switch (theme.background_direction) {
650 case GradientType.Horizontal:
651 backgroundStyle["background"] = _buildLinearGradient("left top", "left bottom",
652 theme.background_start_color,
653 theme.background_end_color);
654 break;
655 case GradientType.Vertical:
656 backgroundStyle["background"] = _buildLinearGradient("left top", "right top",
657 theme.background_start_color,
658 theme.background_end_color);
659 break;
660 case GradientType.LeftTop:
661 backgroundStyle["background"] = _buildLinearGradient("left top", "right bottom",
662 theme.background_start_color,
663 theme.background_end_color);
664 break;
665 case GradientType.LeftBottom:
666 backgroundStyle["background"] = _buildLinearGradient("left bottom", "right top",
667 theme.background_start_color,
668 theme.background_end_color);
669 break;
670 case GradientType.Circular:
671 backgroundStyle["background"] = _buildRadialGradient(window.innerWidth / 2, theme.background_start_color,
672 theme.background_end_color);
673 break;
674 default:
675 backgroundStyle["background"] = "#000";
676 }
677 break;
678 case BackgroundType.Image:
679 background_filename = _pathToString(theme.background_filename);
680 backgroundStyle["background-image"] = "url('file://" + background_filename + "')";
681 break;
682 case BackgroundType.Video:
683 background_filename = _pathToString(theme.background_filename);
684 backgroundStyle["background-color"] = theme.background_border_color;
685 backgroundHtml = "<video loop autoplay muted><source src='file://" + background_filename + "'></video>";
686 break;
687 default:
688 backgroundStyle["background"] = "#000";
689 }
690 globalBackground.style.cssText = "";
691 for (var key in backgroundStyle) {
692 if (backgroundStyle.hasOwnProperty(key)) {
693 globalBackground.style.setProperty(key, backgroundStyle[key]);
694 }
695 }
696 if (!!backgroundHtml) {
697 globalBackground.innerHTML = backgroundHtml;
698 }
699 // set up the main area
700 mainStyle = {
701 "word-wrap": "break-word",
702 /*"margin": "0",
703 "padding": "0"*/
704 };
705 if (!!theme.font_main_outline) {
706 mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " +
707 theme.font_main_outline_color;
708 mainStyle["-webkit-text-fill-color"] = theme.font_main_color;
709 }
710 mainStyle["font-family"] = theme.font_main_name;
711 mainStyle["font-size"] = "" + theme.font_main_size + "pt";
712 mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : "";
713 mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : "";
714 mainStyle["color"] = theme.font_main_color;
715 mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%";
716 mainStyle["text-align"] = theme.display_horizontal_align;
717 if (theme.display_horizontal_align != HorizontalAlign.Justify) {
718 mainStyle["white-space"] = "pre-wrap";
719 }
720 mainStyle["vertical-align"] = theme.display_vertical_align;
721 if (theme.hasOwnProperty('font_main_shadow_size')) {
722 mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "px " +
723 theme.font_main_shadow_size + "px";
724 }
725 mainStyle["padding-bottom"] = theme.display_vertical_align == VerticalAlign.Bottom ? "0.5em" : "0";
726 mainStyle["padding-left"] = !!theme.font_main_outline ? "" + (theme.font_main_outline_size * 2) + "pt" : "0";
727 // These need to be fixed, in the Python they use a width passed in as a parameter
728 mainStyle["position"] = "absolute";
729 mainStyle["width"] = "" + (window.innerWidth - (theme.font_main_outline_size * 4)) + "px";
730 mainStyle["height"] = "" + (window.innerHeight - (theme.font_main_outline_size * 4)) + "px";
731 mainStyle["left"] = "" + theme.font_main_x + "px";
732 mainStyle["top"] = "" + theme.font_main_y + "px";
733 var slidesDiv = $(".slides")[0];
734 slidesDiv.style.cssText = "";
735 for (var key in mainStyle) {
736 if (mainStyle.hasOwnProperty(key)) {
737 slidesDiv.style.setProperty(key, mainStyle[key]);
738 }
739 }
740 // Set up the footer
741 footerStyle = {
742 "text-align": "left"
743 };
744 footerStyle["position"] = "absolute";
745 footerStyle["left"] = "" + theme.font_footer_x + "px";
746 footerStyle["top"] = "" + theme.font_footer_y + "px";
747 footerStyle["bottom"] = "" + (window.innerHeight - theme.font_footer_y - theme.font_footer_height) + "px";
748 footerStyle["width"] = "" + theme.font_footer_width + "px";
749 footerStyle["font-family"] = theme.font_footer_name;
750 footerStyle["font-size"] = "" + theme.font_footer_size + "pt";
751 footerStyle["color"] = theme.font_footer_color;
752 footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap";
753 var footer = $(".footer")[0];
754 footer.style.cssText = "";
755 for (var key in footerStyle) {
756 if (footerStyle.hasOwnProperty(key)) {
757 footer.style.setProperty(key, footerStyle[key]);
758 }
759 }
760 },
761 /**
762 * Return the video types supported by the video tag
763 */
764 getVideoTypes: function () {
765 var videoElement = document.createElement('video');
766 var videoTypes = [];
767 if (videoElement.canPlayType('video/mp4; codecs="mp4v.20.8"') == "probably" ||
768 videoElement.canPlayType('video/mp4; codecs="avc1.42E01E"') == "pobably" ||
769 videoElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') == "probably") {
770 videoTypes.push(['video/mp4', '*.mp4']);
771 }
772 if (videoElement.canPlayType('video/ogg; codecs="theora"') == "probably") {
773 videoTypes.push(['video/ogg', '*.ogv']);
774 }
775 if (videoElement.canPlayType('video/webm; codecs="vp8, vorbis"') == "probably") {
776 videoTypes.push(['video/webm', '*.webm']);
777 }
778 return videoTypes;
779 },
780 /**
781 * Sets the scale of the page - used to make preview widgets scale
782 */
783 setScale: function(scale) {
784 document.body.style.zoom = scale+"%";
785 }
786};
787new QWebChannel(qt.webChannelTransport, function (channel) {
788 window.mediaWatcher = channel.objects.mediaWatcher;
789});
0790
=== added file 'openlp/core/display/html/openlp-splash-screen.png'
1Binary files openlp/core/display/html/openlp-splash-screen.png 1970-01-01 00:00:00 +0000 and openlp/core/display/html/openlp-splash-screen.png 2019-02-13 20:54:40 +0000 differ791Binary files openlp/core/display/html/openlp-splash-screen.png 1970-01-01 00:00:00 +0000 and openlp/core/display/html/openlp-splash-screen.png 2019-02-13 20:54:40 +0000 differ
=== added file 'openlp/core/display/html/reveal.css'
--- openlp/core/display/html/reveal.css 1970-01-01 00:00:00 +0000
+++ openlp/core/display/html/reveal.css 2019-02-13 20:54:40 +0000
@@ -0,0 +1,1591 @@
1/*!
2 * reveal.js
3 * http://revealjs.com
4 * MIT licensed
5 *
6 * Copyright (C) 2018 Hakim El Hattab, http://hakim.se
7 */
8/*********************************************
9 * RESET STYLES
10 *********************************************/
11html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe,
12.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre,
13.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code,
14.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp,
15.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var,
16.reveal b, .reveal u, .reveal center,
17.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li,
18.reveal fieldset, .reveal form, .reveal label, .reveal legend,
19.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td,
20.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed,
21.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup,
22.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary,
23.reveal time, .reveal mark, .reveal audio, .reveal video {
24 margin: 0;
25 padding: 0;
26 border: 0;
27 font-size: 100%;
28 font: inherit;
29 vertical-align: baseline; }
30
31.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure,
32.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section {
33 display: block; }
34
35/*********************************************
36 * GLOBAL STYLES
37 *********************************************/
38html,
39body {
40 width: 100%;
41 height: 100%;
42 overflow: hidden; }
43
44body {
45 position: relative;
46 line-height: 1;
47 background-color: #fff;
48 color: #000; }
49
50/*********************************************
51 * VIEW FRAGMENTS
52 *********************************************/
53.reveal .slides section .fragment {
54 opacity: 0;
55 visibility: hidden;
56 transition: all .2s ease; }
57 .reveal .slides section .fragment.visible {
58 opacity: 1;
59 visibility: inherit; }
60
61.reveal .slides section .fragment.grow {
62 opacity: 1;
63 visibility: inherit; }
64 .reveal .slides section .fragment.grow.visible {
65 -webkit-transform: scale(1.3);
66 transform: scale(1.3); }
67
68.reveal .slides section .fragment.shrink {
69 opacity: 1;
70 visibility: inherit; }
71 .reveal .slides section .fragment.shrink.visible {
72 -webkit-transform: scale(0.7);
73 transform: scale(0.7); }
74
75.reveal .slides section .fragment.zoom-in {
76 -webkit-transform: scale(0.1);
77 transform: scale(0.1); }
78 .reveal .slides section .fragment.zoom-in.visible {
79 -webkit-transform: none;
80 transform: none; }
81
82.reveal .slides section .fragment.fade-out {
83 opacity: 1;
84 visibility: inherit; }
85 .reveal .slides section .fragment.fade-out.visible {
86 opacity: 0;
87 visibility: hidden; }
88
89.reveal .slides section .fragment.semi-fade-out {
90 opacity: 1;
91 visibility: inherit; }
92 .reveal .slides section .fragment.semi-fade-out.visible {
93 opacity: 0.5;
94 visibility: inherit; }
95
96.reveal .slides section .fragment.strike {
97 opacity: 1;
98 visibility: inherit; }
99 .reveal .slides section .fragment.strike.visible {
100 text-decoration: line-through; }
101
102.reveal .slides section .fragment.fade-up {
103 -webkit-transform: translate(0, 20%);
104 transform: translate(0, 20%); }
105 .reveal .slides section .fragment.fade-up.visible {
106 -webkit-transform: translate(0, 0);
107 transform: translate(0, 0); }
108
109.reveal .slides section .fragment.fade-down {
110 -webkit-transform: translate(0, -20%);
111 transform: translate(0, -20%); }
112 .reveal .slides section .fragment.fade-down.visible {
113 -webkit-transform: translate(0, 0);
114 transform: translate(0, 0); }
115
116.reveal .slides section .fragment.fade-right {
117 -webkit-transform: translate(-20%, 0);
118 transform: translate(-20%, 0); }
119 .reveal .slides section .fragment.fade-right.visible {
120 -webkit-transform: translate(0, 0);
121 transform: translate(0, 0); }
122
123.reveal .slides section .fragment.fade-left {
124 -webkit-transform: translate(20%, 0);
125 transform: translate(20%, 0); }
126 .reveal .slides section .fragment.fade-left.visible {
127 -webkit-transform: translate(0, 0);
128 transform: translate(0, 0); }
129
130.reveal .slides section .fragment.fade-in-then-out,
131.reveal .slides section .fragment.current-visible {
132 opacity: 0;
133 visibility: hidden; }
134 .reveal .slides section .fragment.fade-in-then-out.current-fragment,
135 .reveal .slides section .fragment.current-visible.current-fragment {
136 opacity: 1;
137 visibility: inherit; }
138
139.reveal .slides section .fragment.fade-in-then-semi-out {
140 opacity: 0;
141 visibility: hidden; }
142 .reveal .slides section .fragment.fade-in-then-semi-out.visible {
143 opacity: 0.5;
144 visibility: inherit; }
145 .reveal .slides section .fragment.fade-in-then-semi-out.current-fragment {
146 opacity: 1;
147 visibility: inherit; }
148
149.reveal .slides section .fragment.highlight-red,
150.reveal .slides section .fragment.highlight-current-red,
151.reveal .slides section .fragment.highlight-green,
152.reveal .slides section .fragment.highlight-current-green,
153.reveal .slides section .fragment.highlight-blue,
154.reveal .slides section .fragment.highlight-current-blue {
155 opacity: 1;
156 visibility: inherit; }
157
158.reveal .slides section .fragment.highlight-red.visible {
159 color: #ff2c2d; }
160
161.reveal .slides section .fragment.highlight-green.visible {
162 color: #17ff2e; }
163
164.reveal .slides section .fragment.highlight-blue.visible {
165 color: #1b91ff; }
166
167.reveal .slides section .fragment.highlight-current-red.current-fragment {
168 color: #ff2c2d; }
169
170.reveal .slides section .fragment.highlight-current-green.current-fragment {
171 color: #17ff2e; }
172
173.reveal .slides section .fragment.highlight-current-blue.current-fragment {
174 color: #1b91ff; }
175
176/*********************************************
177 * DEFAULT ELEMENT STYLES
178 *********************************************/
179/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */
180.reveal:after {
181 content: '';
182 font-style: italic; }
183
184.reveal iframe {
185 z-index: 1; }
186
187/** Prevents layering issues in certain browser/transition combinations */
188.reveal a {
189 position: relative; }
190
191.reveal .stretch {
192 max-width: none;
193 max-height: none; }
194
195.reveal pre.stretch code {
196 height: 100%;
197 max-height: 100%;
198 box-sizing: border-box; }
199
200/*********************************************
201 * CONTROLS
202 *********************************************/
203@-webkit-keyframes bounce-right {
204 0%, 10%, 25%, 40%, 50% {
205 -webkit-transform: translateX(0);
206 transform: translateX(0); }
207 20% {
208 -webkit-transform: translateX(10px);
209 transform: translateX(10px); }
210 30% {
211 -webkit-transform: translateX(-5px);
212 transform: translateX(-5px); } }
213@keyframes bounce-right {
214 0%, 10%, 25%, 40%, 50% {
215 -webkit-transform: translateX(0);
216 transform: translateX(0); }
217 20% {
218 -webkit-transform: translateX(10px);
219 transform: translateX(10px); }
220 30% {
221 -webkit-transform: translateX(-5px);
222 transform: translateX(-5px); } }
223
224@-webkit-keyframes bounce-down {
225 0%, 10%, 25%, 40%, 50% {
226 -webkit-transform: translateY(0);
227 transform: translateY(0); }
228 20% {
229 -webkit-transform: translateY(10px);
230 transform: translateY(10px); }
231 30% {
232 -webkit-transform: translateY(-5px);
233 transform: translateY(-5px); } }
234
235@keyframes bounce-down {
236 0%, 10%, 25%, 40%, 50% {
237 -webkit-transform: translateY(0);
238 transform: translateY(0); }
239 20% {
240 -webkit-transform: translateY(10px);
241 transform: translateY(10px); }
242 30% {
243 -webkit-transform: translateY(-5px);
244 transform: translateY(-5px); } }
245
246.reveal .controls {
247 display: none;
248 position: absolute;
249 top: auto;
250 bottom: 12px;
251 right: 12px;
252 left: auto;
253 z-index: 1;
254 color: #000;
255 pointer-events: none;
256 font-size: 10px; }
257 .reveal .controls button {
258 position: absolute;
259 padding: 0;
260 background-color: transparent;
261 border: 0;
262 outline: 0;
263 cursor: pointer;
264 color: currentColor;
265 -webkit-transform: scale(0.9999);
266 transform: scale(0.9999);
267 transition: color 0.2s ease, opacity 0.2s ease, -webkit-transform 0.2s ease;
268 transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
269 z-index: 2;
270 pointer-events: auto;
271 font-size: inherit;
272 visibility: hidden;
273 opacity: 0;
274 -webkit-appearance: none;
275 -webkit-tap-highlight-color: transparent; }
276 .reveal .controls .controls-arrow:before,
277 .reveal .controls .controls-arrow:after {
278 content: '';
279 position: absolute;
280 top: 0;
281 left: 0;
282 width: 2.6em;
283 height: 0.5em;
284 border-radius: 0.25em;
285 background-color: currentColor;
286 transition: all 0.15s ease, background-color 0.8s ease;
287 -webkit-transform-origin: 0.2em 50%;
288 transform-origin: 0.2em 50%;
289 will-change: transform; }
290 .reveal .controls .controls-arrow {
291 position: relative;
292 width: 3.6em;
293 height: 3.6em; }
294 .reveal .controls .controls-arrow:before {
295 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg);
296 transform: translateX(0.5em) translateY(1.55em) rotate(45deg); }
297 .reveal .controls .controls-arrow:after {
298 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg);
299 transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); }
300 .reveal .controls .controls-arrow:hover:before {
301 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(40deg);
302 transform: translateX(0.5em) translateY(1.55em) rotate(40deg); }
303 .reveal .controls .controls-arrow:hover:after {
304 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-40deg);
305 transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); }
306 .reveal .controls .controls-arrow:active:before {
307 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(36deg);
308 transform: translateX(0.5em) translateY(1.55em) rotate(36deg); }
309 .reveal .controls .controls-arrow:active:after {
310 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-36deg);
311 transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); }
312 .reveal .controls .navigate-left {
313 right: 6.4em;
314 bottom: 3.2em;
315 -webkit-transform: translateX(-10px);
316 transform: translateX(-10px); }
317 .reveal .controls .navigate-right {
318 right: 0;
319 bottom: 3.2em;
320 -webkit-transform: translateX(10px);
321 transform: translateX(10px); }
322 .reveal .controls .navigate-right .controls-arrow {
323 -webkit-transform: rotate(180deg);
324 transform: rotate(180deg); }
325 .reveal .controls .navigate-right.highlight {
326 -webkit-animation: bounce-right 2s 50 both ease-out;
327 animation: bounce-right 2s 50 both ease-out; }
328 .reveal .controls .navigate-up {
329 right: 3.2em;
330 bottom: 6.4em;
331 -webkit-transform: translateY(-10px);
332 transform: translateY(-10px); }
333 .reveal .controls .navigate-up .controls-arrow {
334 -webkit-transform: rotate(90deg);
335 transform: rotate(90deg); }
336 .reveal .controls .navigate-down {
337 right: 3.2em;
338 bottom: 0;
339 -webkit-transform: translateY(10px);
340 transform: translateY(10px); }
341 .reveal .controls .navigate-down .controls-arrow {
342 -webkit-transform: rotate(-90deg);
343 transform: rotate(-90deg); }
344 .reveal .controls .navigate-down.highlight {
345 -webkit-animation: bounce-down 2s 50 both ease-out;
346 animation: bounce-down 2s 50 both ease-out; }
347 .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled,
348 .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled {
349 opacity: 0.3; }
350 .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled:hover,
351 .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled:hover {
352 opacity: 1; }
353 .reveal .controls[data-controls-back-arrows="hidden"] .navigate-left.enabled,
354 .reveal .controls[data-controls-back-arrows="hidden"] .navigate-up.enabled {
355 opacity: 0;
356 visibility: hidden; }
357 .reveal .controls .enabled {
358 visibility: visible;
359 opacity: 0.9;
360 cursor: pointer;
361 -webkit-transform: none;
362 transform: none; }
363 .reveal .controls .enabled.fragmented {
364 opacity: 0.5; }
365 .reveal .controls .enabled:hover,
366 .reveal .controls .enabled.fragmented:hover {
367 opacity: 1; }
368
369.reveal:not(.has-vertical-slides) .controls .navigate-left {
370 bottom: 1.4em;
371 right: 5.5em; }
372
373.reveal:not(.has-vertical-slides) .controls .navigate-right {
374 bottom: 1.4em;
375 right: 0.5em; }
376
377.reveal:not(.has-horizontal-slides) .controls .navigate-up {
378 right: 1.4em;
379 bottom: 5em; }
380
381.reveal:not(.has-horizontal-slides) .controls .navigate-down {
382 right: 1.4em;
383 bottom: 0.5em; }
384
385.reveal.has-dark-background .controls {
386 color: #fff; }
387
388.reveal.has-light-background .controls {
389 color: #000; }
390
391.reveal.no-hover .controls .controls-arrow:hover:before,
392.reveal.no-hover .controls .controls-arrow:active:before {
393 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg);
394 transform: translateX(0.5em) translateY(1.55em) rotate(45deg); }
395
396.reveal.no-hover .controls .controls-arrow:hover:after,
397.reveal.no-hover .controls .controls-arrow:active:after {
398 -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg);
399 transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); }
400
401@media screen and (min-width: 500px) {
402 .reveal .controls[data-controls-layout="edges"] {
403 top: 0;
404 right: 0;
405 bottom: 0;
406 left: 0; }
407 .reveal .controls[data-controls-layout="edges"] .navigate-left,
408 .reveal .controls[data-controls-layout="edges"] .navigate-right,
409 .reveal .controls[data-controls-layout="edges"] .navigate-up,
410 .reveal .controls[data-controls-layout="edges"] .navigate-down {
411 bottom: auto;
412 right: auto; }
413 .reveal .controls[data-controls-layout="edges"] .navigate-left {
414 top: 50%;
415 left: 8px;
416 margin-top: -1.8em; }
417 .reveal .controls[data-controls-layout="edges"] .navigate-right {
418 top: 50%;
419 right: 8px;
420 margin-top: -1.8em; }
421 .reveal .controls[data-controls-layout="edges"] .navigate-up {
422 top: 8px;
423 left: 50%;
424 margin-left: -1.8em; }
425 .reveal .controls[data-controls-layout="edges"] .navigate-down {
426 bottom: 8px;
427 left: 50%;
428 margin-left: -1.8em; } }
429
430/*********************************************
431 * PROGRESS BAR
432 *********************************************/
433.reveal .progress {
434 position: absolute;
435 display: none;
436 height: 3px;
437 width: 100%;
438 bottom: 0;
439 left: 0;
440 z-index: 10;
441 background-color: rgba(0, 0, 0, 0.2);
442 color: #fff; }
443
444.reveal .progress:after {
445 content: '';
446 display: block;
447 position: absolute;
448 height: 10px;
449 width: 100%;
450 top: -10px; }
451
452.reveal .progress span {
453 display: block;
454 height: 100%;
455 width: 0px;
456 background-color: currentColor;
457 transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
458
459/*********************************************
460 * SLIDE NUMBER
461 *********************************************/
462.reveal .slide-number {
463 position: absolute;
464 display: block;
465 right: 8px;
466 bottom: 8px;
467 z-index: 31;
468 font-family: Helvetica, sans-serif;
469 font-size: 12px;
470 line-height: 1;
471 color: #fff;
472 background-color: rgba(0, 0, 0, 0.4);
473 padding: 5px; }
474
475.reveal .slide-number a {
476 color: currentColor; }
477
478.reveal .slide-number-delimiter {
479 margin: 0 3px; }
480
481/*********************************************
482 * SLIDES
483 *********************************************/
484.reveal {
485 position: relative;
486 width: 100%;
487 height: 100%;
488 overflow: hidden;
489 -ms-touch-action: none;
490 touch-action: none; }
491
492@media only screen and (orientation: landscape) {
493 .reveal.ua-iphone {
494 position: fixed; } }
495
496.reveal .slides {
497 position: absolute;
498 width: 100%;
499 height: 100%;
500 top: 0;
501 right: 0;
502 bottom: 0;
503 left: 0;
504 margin: auto;
505 pointer-events: none;
506 overflow: visible;
507 z-index: 1;
508 text-align: center;
509 -webkit-perspective: 600px;
510 perspective: 600px;
511 -webkit-perspective-origin: 50% 40%;
512 perspective-origin: 50% 40%; }
513
514.reveal .slides > section {
515 -ms-perspective: 600px; }
516
517.reveal .slides > section,
518.reveal .slides > section > section {
519 display: none;
520 position: absolute;
521 width: 100%;
522 padding: 20px 0px;
523 pointer-events: auto;
524 z-index: 10;
525 -webkit-transform-style: flat;
526 transform-style: flat;
527 transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
528 transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
529
530/* Global transition speed settings */
531.reveal[data-transition-speed="fast"] .slides section {
532 transition-duration: 400ms; }
533
534.reveal[data-transition-speed="slow"] .slides section {
535 transition-duration: 1200ms; }
536
537/* Slide-specific transition speed overrides */
538.reveal .slides section[data-transition-speed="fast"] {
539 transition-duration: 400ms; }
540
541.reveal .slides section[data-transition-speed="slow"] {
542 transition-duration: 1200ms; }
543
544.reveal .slides > section.stack {
545 padding-top: 0;
546 padding-bottom: 0;
547 pointer-events: none; }
548
549.reveal .slides > section.present,
550.reveal .slides > section > section.present {
551 display: block;
552 z-index: 11;
553 opacity: 1; }
554
555.reveal .slides > section:empty,
556.reveal .slides > section > section:empty,
557.reveal .slides > section[data-background-interactive],
558.reveal .slides > section > section[data-background-interactive] {
559 pointer-events: none; }
560
561.reveal.center,
562.reveal.center .slides,
563.reveal.center .slides section {
564 min-height: 0 !important; }
565
566/* Don't allow interaction with invisible slides */
567.reveal .slides > section.future,
568.reveal .slides > section > section.future,
569.reveal .slides > section.past,
570.reveal .slides > section > section.past {
571 pointer-events: none; }
572
573.reveal.overview .slides > section,
574.reveal.overview .slides > section > section {
575 pointer-events: auto; }
576
577.reveal .slides > section.past,
578.reveal .slides > section.future,
579.reveal .slides > section > section.past,
580.reveal .slides > section > section.future {
581 opacity: 0; }
582
583/*********************************************
584 * Mixins for readability of transitions
585 *********************************************/
586/*********************************************
587 * SLIDE TRANSITION
588 * Aliased 'linear' for backwards compatibility
589 *********************************************/
590.reveal.slide section {
591 -webkit-backface-visibility: hidden;
592 backface-visibility: hidden; }
593
594.reveal .slides > section[data-transition=slide].past,
595.reveal .slides > section[data-transition~=slide-out].past,
596.reveal.slide .slides > section:not([data-transition]).past {
597 -webkit-transform: translate(-150%, 0);
598 transform: translate(-150%, 0); }
599
600.reveal .slides > section[data-transition=slide].future,
601.reveal .slides > section[data-transition~=slide-in].future,
602.reveal.slide .slides > section:not([data-transition]).future {
603 -webkit-transform: translate(150%, 0);
604 transform: translate(150%, 0); }
605
606.reveal .slides > section > section[data-transition=slide].past,
607.reveal .slides > section > section[data-transition~=slide-out].past,
608.reveal.slide .slides > section > section:not([data-transition]).past {
609 -webkit-transform: translate(0, -150%);
610 transform: translate(0, -150%); }
611
612.reveal .slides > section > section[data-transition=slide].future,
613.reveal .slides > section > section[data-transition~=slide-in].future,
614.reveal.slide .slides > section > section:not([data-transition]).future {
615 -webkit-transform: translate(0, 150%);
616 transform: translate(0, 150%); }
617
618.reveal.linear section {
619 -webkit-backface-visibility: hidden;
620 backface-visibility: hidden; }
621
622.reveal .slides > section[data-transition=linear].past,
623.reveal .slides > section[data-transition~=linear-out].past,
624.reveal.linear .slides > section:not([data-transition]).past {
625 -webkit-transform: translate(-150%, 0);
626 transform: translate(-150%, 0); }
627
628.reveal .slides > section[data-transition=linear].future,
629.reveal .slides > section[data-transition~=linear-in].future,
630.reveal.linear .slides > section:not([data-transition]).future {
631 -webkit-transform: translate(150%, 0);
632 transform: translate(150%, 0); }
633
634.reveal .slides > section > section[data-transition=linear].past,
635.reveal .slides > section > section[data-transition~=linear-out].past,
636.reveal.linear .slides > section > section:not([data-transition]).past {
637 -webkit-transform: translate(0, -150%);
638 transform: translate(0, -150%); }
639
640.reveal .slides > section > section[data-transition=linear].future,
641.reveal .slides > section > section[data-transition~=linear-in].future,
642.reveal.linear .slides > section > section:not([data-transition]).future {
643 -webkit-transform: translate(0, 150%);
644 transform: translate(0, 150%); }
645
646/*********************************************
647 * CONVEX TRANSITION
648 * Aliased 'default' for backwards compatibility
649 *********************************************/
650.reveal .slides section[data-transition=default].stack,
651.reveal.default .slides section.stack {
652 -webkit-transform-style: preserve-3d;
653 transform-style: preserve-3d; }
654
655.reveal .slides > section[data-transition=default].past,
656.reveal .slides > section[data-transition~=default-out].past,
657.reveal.default .slides > section:not([data-transition]).past {
658 -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
659 transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); }
660
661.reveal .slides > section[data-transition=default].future,
662.reveal .slides > section[data-transition~=default-in].future,
663.reveal.default .slides > section:not([data-transition]).future {
664 -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
665 transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); }
666
667.reveal .slides > section > section[data-transition=default].past,
668.reveal .slides > section > section[data-transition~=default-out].past,
669.reveal.default .slides > section > section:not([data-transition]).past {
670 -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0);
671 transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); }
672
673.reveal .slides > section > section[data-transition=default].future,
674.reveal .slides > section > section[data-transition~=default-in].future,
675.reveal.default .slides > section > section:not([data-transition]).future {
676 -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0);
677 transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); }
678
679.reveal .slides section[data-transition=convex].stack,
680.reveal.convex .slides section.stack {
681 -webkit-transform-style: preserve-3d;
682 transform-style: preserve-3d; }
683
684.reveal .slides > section[data-transition=convex].past,
685.reveal .slides > section[data-transition~=convex-out].past,
686.reveal.convex .slides > section:not([data-transition]).past {
687 -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
688 transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); }
689
690.reveal .slides > section[data-transition=convex].future,
691.reveal .slides > section[data-transition~=convex-in].future,
692.reveal.convex .slides > section:not([data-transition]).future {
693 -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
694 transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); }
695
696.reveal .slides > section > section[data-transition=convex].past,
697.reveal .slides > section > section[data-transition~=convex-out].past,
698.reveal.convex .slides > section > section:not([data-transition]).past {
699 -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0);
700 transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); }
701
702.reveal .slides > section > section[data-transition=convex].future,
703.reveal .slides > section > section[data-transition~=convex-in].future,
704.reveal.convex .slides > section > section:not([data-transition]).future {
705 -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0);
706 transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); }
707
708/*********************************************
709 * CONCAVE TRANSITION
710 *********************************************/
711.reveal .slides section[data-transition=concave].stack,
712.reveal.concave .slides section.stack {
713 -webkit-transform-style: preserve-3d;
714 transform-style: preserve-3d; }
715
716.reveal .slides > section[data-transition=concave].past,
717.reveal .slides > section[data-transition~=concave-out].past,
718.reveal.concave .slides > section:not([data-transition]).past {
719 -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0);
720 transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); }
721
722.reveal .slides > section[data-transition=concave].future,
723.reveal .slides > section[data-transition~=concave-in].future,
724.reveal.concave .slides > section:not([data-transition]).future {
725 -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0);
726 transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); }
727
728.reveal .slides > section > section[data-transition=concave].past,
729.reveal .slides > section > section[data-transition~=concave-out].past,
730.reveal.concave .slides > section > section:not([data-transition]).past {
731 -webkit-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0);
732 transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); }
733
734.reveal .slides > section > section[data-transition=concave].future,
735.reveal .slides > section > section[data-transition~=concave-in].future,
736.reveal.concave .slides > section > section:not([data-transition]).future {
737 -webkit-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0);
738 transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); }
739
740/*********************************************
741 * ZOOM TRANSITION
742 *********************************************/
743.reveal .slides section[data-transition=zoom],
744.reveal.zoom .slides section:not([data-transition]) {
745 transition-timing-function: ease; }
746
747.reveal .slides > section[data-transition=zoom].past,
748.reveal .slides > section[data-transition~=zoom-out].past,
749.reveal.zoom .slides > section:not([data-transition]).past {
750 visibility: hidden;
751 -webkit-transform: scale(16);
752 transform: scale(16); }
753
754.reveal .slides > section[data-transition=zoom].future,
755.reveal .slides > section[data-transition~=zoom-in].future,
756.reveal.zoom .slides > section:not([data-transition]).future {
757 visibility: hidden;
758 -webkit-transform: scale(0.2);
759 transform: scale(0.2); }
760
761.reveal .slides > section > section[data-transition=zoom].past,
762.reveal .slides > section > section[data-transition~=zoom-out].past,
763.reveal.zoom .slides > section > section:not([data-transition]).past {
764 -webkit-transform: translate(0, -150%);
765 transform: translate(0, -150%); }
766
767.reveal .slides > section > section[data-transition=zoom].future,
768.reveal .slides > section > section[data-transition~=zoom-in].future,
769.reveal.zoom .slides > section > section:not([data-transition]).future {
770 -webkit-transform: translate(0, 150%);
771 transform: translate(0, 150%); }
772
773/*********************************************
774 * CUBE TRANSITION
775 *
776 * WARNING:
777 * this is deprecated and will be removed in a
778 * future version.
779 *********************************************/
780.reveal.cube .slides {
781 -webkit-perspective: 1300px;
782 perspective: 1300px; }
783
784.reveal.cube .slides section {
785 padding: 30px;
786 min-height: 700px;
787 -webkit-backface-visibility: hidden;
788 backface-visibility: hidden;
789 box-sizing: border-box;
790 -webkit-transform-style: preserve-3d;
791 transform-style: preserve-3d; }
792
793.reveal.center.cube .slides section {
794 min-height: 0; }
795
796.reveal.cube .slides section:not(.stack):before {
797 content: '';
798 position: absolute;
799 display: block;
800 width: 100%;
801 height: 100%;
802 left: 0;
803 top: 0;
804 background: rgba(0, 0, 0, 0.1);
805 border-radius: 4px;
806 -webkit-transform: translateZ(-20px);
807 transform: translateZ(-20px); }
808
809.reveal.cube .slides section:not(.stack):after {
810 content: '';
811 position: absolute;
812 display: block;
813 width: 90%;
814 height: 30px;
815 left: 5%;
816 bottom: 0;
817 background: none;
818 z-index: 1;
819 border-radius: 4px;
820 box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2);
821 -webkit-transform: translateZ(-90px) rotateX(65deg);
822 transform: translateZ(-90px) rotateX(65deg); }
823
824.reveal.cube .slides > section.stack {
825 padding: 0;
826 background: none; }
827
828.reveal.cube .slides > section.past {
829 -webkit-transform-origin: 100% 0%;
830 transform-origin: 100% 0%;
831 -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg);
832 transform: translate3d(-100%, 0, 0) rotateY(-90deg); }
833
834.reveal.cube .slides > section.future {
835 -webkit-transform-origin: 0% 0%;
836 transform-origin: 0% 0%;
837 -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg);
838 transform: translate3d(100%, 0, 0) rotateY(90deg); }
839
840.reveal.cube .slides > section > section.past {
841 -webkit-transform-origin: 0% 100%;
842 transform-origin: 0% 100%;
843 -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg);
844 transform: translate3d(0, -100%, 0) rotateX(90deg); }
845
846.reveal.cube .slides > section > section.future {
847 -webkit-transform-origin: 0% 0%;
848 transform-origin: 0% 0%;
849 -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg);
850 transform: translate3d(0, 100%, 0) rotateX(-90deg); }
851
852/*********************************************
853 * PAGE TRANSITION
854 *
855 * WARNING:
856 * this is deprecated and will be removed in a
857 * future version.
858 *********************************************/
859.reveal.page .slides {
860 -webkit-perspective-origin: 0% 50%;
861 perspective-origin: 0% 50%;
862 -webkit-perspective: 3000px;
863 perspective: 3000px; }
864
865.reveal.page .slides section {
866 padding: 30px;
867 min-height: 700px;
868 box-sizing: border-box;
869 -webkit-transform-style: preserve-3d;
870 transform-style: preserve-3d; }
871
872.reveal.page .slides section.past {
873 z-index: 12; }
874
875.reveal.page .slides section:not(.stack):before {
876 content: '';
877 position: absolute;
878 display: block;
879 width: 100%;
880 height: 100%;
881 left: 0;
882 top: 0;
883 background: rgba(0, 0, 0, 0.1);
884 -webkit-transform: translateZ(-20px);
885 transform: translateZ(-20px); }
886
887.reveal.page .slides section:not(.stack):after {
888 content: '';
889 position: absolute;
890 display: block;
891 width: 90%;
892 height: 30px;
893 left: 5%;
894 bottom: 0;
895 background: none;
896 z-index: 1;
897 border-radius: 4px;
898 box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2);
899 -webkit-transform: translateZ(-90px) rotateX(65deg); }
900
901.reveal.page .slides > section.stack {
902 padding: 0;
903 background: none; }
904
905.reveal.page .slides > section.past {
906 -webkit-transform-origin: 0% 0%;
907 transform-origin: 0% 0%;
908 -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg);
909 transform: translate3d(-40%, 0, 0) rotateY(-80deg); }
910
911.reveal.page .slides > section.future {
912 -webkit-transform-origin: 100% 0%;
913 transform-origin: 100% 0%;
914 -webkit-transform: translate3d(0, 0, 0);
915 transform: translate3d(0, 0, 0); }
916
917.reveal.page .slides > section > section.past {
918 -webkit-transform-origin: 0% 0%;
919 transform-origin: 0% 0%;
920 -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg);
921 transform: translate3d(0, -40%, 0) rotateX(80deg); }
922
923.reveal.page .slides > section > section.future {
924 -webkit-transform-origin: 0% 100%;
925 transform-origin: 0% 100%;
926 -webkit-transform: translate3d(0, 0, 0);
927 transform: translate3d(0, 0, 0); }
928
929/*********************************************
930 * FADE TRANSITION
931 *********************************************/
932.reveal .slides section[data-transition=fade],
933.reveal.fade .slides section:not([data-transition]),
934.reveal.fade .slides > section > section:not([data-transition]) {
935 -webkit-transform: none;
936 transform: none;
937 transition: opacity 0.5s; }
938
939.reveal.fade.overview .slides section,
940.reveal.fade.overview .slides > section > section {
941 transition: none; }
942
943/*********************************************
944 * NO TRANSITION
945 *********************************************/
946.reveal .slides section[data-transition=none],
947.reveal.none .slides section:not([data-transition]) {
948 -webkit-transform: none;
949 transform: none;
950 transition: none; }
951
952/*********************************************
953 * PAUSED MODE
954 *********************************************/
955.reveal .pause-overlay {
956 position: absolute;
957 top: 0;
958 left: 0;
959 width: 100%;
960 height: 100%;
961 background: black;
962 visibility: hidden;
963 opacity: 0;
964 z-index: 100;
965 transition: all 1s ease; }
966
967.reveal .pause-overlay .resume-button {
968 position: absolute;
969 bottom: 20px;
970 right: 20px;
971 color: #ccc;
972 border-radius: 2px;
973 padding: 6px 14px;
974 border: 2px solid #ccc;
975 font-size: 16px;
976 background: transparent;
977 cursor: pointer; }
978 .reveal .pause-overlay .resume-button:hover {
979 color: #fff;
980 border-color: #fff; }
981
982.reveal.paused .pause-overlay {
983 visibility: visible;
984 opacity: 1; }
985
986/*********************************************
987 * FALLBACK
988 *********************************************/
989.no-transforms {
990 overflow-y: auto; }
991
992.no-transforms .reveal .slides {
993 position: relative;
994 width: 80%;
995 height: auto !important;
996 top: 0;
997 left: 50%;
998 margin: 0;
999 text-align: center; }
1000
1001.no-transforms .reveal .controls,
1002.no-transforms .reveal .progress {
1003 display: none !important; }
1004
1005.no-transforms .reveal .slides section {
1006 display: block !important;
1007 opacity: 1 !important;
1008 position: relative !important;
1009 height: auto;
1010 min-height: 0;
1011 top: 0;
1012 left: -50%;
1013 margin: 70px 0;
1014 -webkit-transform: none;
1015 transform: none; }
1016
1017.no-transforms .reveal .slides section section {
1018 left: 0; }
1019
1020.reveal .no-transition,
1021.reveal .no-transition * {
1022 transition: none !important; }
1023
1024/*********************************************
1025 * PER-SLIDE BACKGROUNDS
1026 *********************************************/
1027.reveal .backgrounds {
1028 position: absolute;
1029 width: 100%;
1030 height: 100%;
1031 top: 0;
1032 left: 0;
1033 -webkit-perspective: 600px;
1034 perspective: 600px; }
1035
1036.reveal .slide-background {
1037 display: none;
1038 position: absolute;
1039 width: 100%;
1040 height: 100%;
1041 opacity: 0;
1042 visibility: hidden;
1043 overflow: hidden;
1044 background-color: transparent;
1045 transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
1046
1047.reveal .slide-background-content {
1048 position: absolute;
1049 width: 100%;
1050 height: 100%;
1051 background-position: 50% 50%;
1052 background-repeat: no-repeat;
1053 background-size: cover; }
1054
1055.reveal .slide-background.stack {
1056 display: block; }
1057
1058.reveal .slide-background.present {
1059 opacity: 1;
1060 visibility: visible;
1061 z-index: 2; }
1062
1063.print-pdf .reveal .slide-background {
1064 opacity: 1 !important;
1065 visibility: visible !important; }
1066
1067/* Video backgrounds */
1068.reveal .slide-background video {
1069 position: absolute;
1070 width: 100%;
1071 height: 100%;
1072 max-width: none;
1073 max-height: none;
1074 top: 0;
1075 left: 0;
1076 -o-object-fit: cover;
1077 object-fit: cover; }
1078
1079.reveal .slide-background[data-background-size="contain"] video {
1080 -o-object-fit: contain;
1081 object-fit: contain; }
1082
1083/* Immediate transition style */
1084.reveal[data-background-transition=none] > .backgrounds .slide-background,
1085.reveal > .backgrounds .slide-background[data-background-transition=none] {
1086 transition: none; }
1087
1088/* Slide */
1089.reveal[data-background-transition=slide] > .backgrounds .slide-background,
1090.reveal > .backgrounds .slide-background[data-background-transition=slide] {
1091 opacity: 1;
1092 -webkit-backface-visibility: hidden;
1093 backface-visibility: hidden; }
1094
1095.reveal[data-background-transition=slide] > .backgrounds .slide-background.past,
1096.reveal > .backgrounds .slide-background.past[data-background-transition=slide] {
1097 -webkit-transform: translate(-100%, 0);
1098 transform: translate(-100%, 0); }
1099
1100.reveal[data-background-transition=slide] > .backgrounds .slide-background.future,
1101.reveal > .backgrounds .slide-background.future[data-background-transition=slide] {
1102 -webkit-transform: translate(100%, 0);
1103 transform: translate(100%, 0); }
1104
1105.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past,
1106.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] {
1107 -webkit-transform: translate(0, -100%);
1108 transform: translate(0, -100%); }
1109
1110.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future,
1111.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] {
1112 -webkit-transform: translate(0, 100%);
1113 transform: translate(0, 100%); }
1114
1115/* Convex */
1116.reveal[data-background-transition=convex] > .backgrounds .slide-background.past,
1117.reveal > .backgrounds .slide-background.past[data-background-transition=convex] {
1118 opacity: 0;
1119 -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
1120 transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); }
1121
1122.reveal[data-background-transition=convex] > .backgrounds .slide-background.future,
1123.reveal > .backgrounds .slide-background.future[data-background-transition=convex] {
1124 opacity: 0;
1125 -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
1126 transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); }
1127
1128.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.past,
1129.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=convex] {
1130 opacity: 0;
1131 -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0);
1132 transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); }
1133
1134.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.future,
1135.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=convex] {
1136 opacity: 0;
1137 -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0);
1138 transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); }
1139
1140/* Concave */
1141.reveal[data-background-transition=concave] > .backgrounds .slide-background.past,
1142.reveal > .backgrounds .slide-background.past[data-background-transition=concave] {
1143 opacity: 0;
1144 -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0);
1145 transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); }
1146
1147.reveal[data-background-transition=concave] > .backgrounds .slide-background.future,
1148.reveal > .backgrounds .slide-background.future[data-background-transition=concave] {
1149 opacity: 0;
1150 -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0);
1151 transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); }
1152
1153.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.past,
1154.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=concave] {
1155 opacity: 0;
1156 -webkit-transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0);
1157 transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); }
1158
1159.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.future,
1160.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=concave] {
1161 opacity: 0;
1162 -webkit-transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0);
1163 transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); }
1164
1165/* Zoom */
1166.reveal[data-background-transition=zoom] > .backgrounds .slide-background,
1167.reveal > .backgrounds .slide-background[data-background-transition=zoom] {
1168 transition-timing-function: ease; }
1169
1170.reveal[data-background-transition=zoom] > .backgrounds .slide-background.past,
1171.reveal > .backgrounds .slide-background.past[data-background-transition=zoom] {
1172 opacity: 0;
1173 visibility: hidden;
1174 -webkit-transform: scale(16);
1175 transform: scale(16); }
1176
1177.reveal[data-background-transition=zoom] > .backgrounds .slide-background.future,
1178.reveal > .backgrounds .slide-background.future[data-background-transition=zoom] {
1179 opacity: 0;
1180 visibility: hidden;
1181 -webkit-transform: scale(0.2);
1182 transform: scale(0.2); }
1183
1184.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past,
1185.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=zoom] {
1186 opacity: 0;
1187 visibility: hidden;
1188 -webkit-transform: scale(16);
1189 transform: scale(16); }
1190
1191.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future,
1192.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=zoom] {
1193 opacity: 0;
1194 visibility: hidden;
1195 -webkit-transform: scale(0.2);
1196 transform: scale(0.2); }
1197
1198/* Global transition speed settings */
1199.reveal[data-transition-speed="fast"] > .backgrounds .slide-background {
1200 transition-duration: 400ms; }
1201
1202.reveal[data-transition-speed="slow"] > .backgrounds .slide-background {
1203 transition-duration: 1200ms; }
1204
1205/*********************************************
1206 * OVERVIEW
1207 *********************************************/
1208.reveal.overview {
1209 -webkit-perspective-origin: 50% 50%;
1210 perspective-origin: 50% 50%;
1211 -webkit-perspective: 700px;
1212 perspective: 700px; }
1213 .reveal.overview .slides {
1214 -moz-transform-style: preserve-3d; }
1215 .reveal.overview .slides section {
1216 height: 100%;
1217 top: 0 !important;
1218 opacity: 1 !important;
1219 overflow: hidden;
1220 visibility: visible !important;
1221 cursor: pointer;
1222 box-sizing: border-box; }
1223 .reveal.overview .slides section:hover,
1224 .reveal.overview .slides section.present {
1225 outline: 10px solid rgba(150, 150, 150, 0.4);
1226 outline-offset: 10px; }
1227 .reveal.overview .slides section .fragment {
1228 opacity: 1;
1229 transition: none; }
1230 .reveal.overview .slides section:after,
1231 .reveal.overview .slides section:before {
1232 display: none !important; }
1233 .reveal.overview .slides > section.stack {
1234 padding: 0;
1235 top: 0 !important;
1236 background: none;
1237 outline: none;
1238 overflow: visible; }
1239 .reveal.overview .backgrounds {
1240 -webkit-perspective: inherit;
1241 perspective: inherit;
1242 -moz-transform-style: preserve-3d; }
1243 .reveal.overview .backgrounds .slide-background {
1244 opacity: 1;
1245 visibility: visible;
1246 outline: 10px solid rgba(150, 150, 150, 0.1);
1247 outline-offset: 10px; }
1248 .reveal.overview .backgrounds .slide-background.stack {
1249 overflow: visible; }
1250
1251.reveal.overview .slides section,
1252.reveal.overview-deactivating .slides section {
1253 transition: none; }
1254
1255.reveal.overview .backgrounds .slide-background,
1256.reveal.overview-deactivating .backgrounds .slide-background {
1257 transition: none; }
1258
1259/*********************************************
1260 * RTL SUPPORT
1261 *********************************************/
1262.reveal.rtl .slides,
1263.reveal.rtl .slides h1,
1264.reveal.rtl .slides h2,
1265.reveal.rtl .slides h3,
1266.reveal.rtl .slides h4,
1267.reveal.rtl .slides h5,
1268.reveal.rtl .slides h6 {
1269 direction: rtl;
1270 font-family: sans-serif; }
1271
1272.reveal.rtl pre,
1273.reveal.rtl code {
1274 direction: ltr; }
1275
1276.reveal.rtl ol,
1277.reveal.rtl ul {
1278 text-align: right; }
1279
1280.reveal.rtl .progress span {
1281 float: right; }
1282
1283/*********************************************
1284 * PARALLAX BACKGROUND
1285 *********************************************/
1286.reveal.has-parallax-background .backgrounds {
1287 transition: all 0.8s ease; }
1288
1289/* Global transition speed settings */
1290.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds {
1291 transition-duration: 400ms; }
1292
1293.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds {
1294 transition-duration: 1200ms; }
1295
1296/*********************************************
1297 * LINK PREVIEW OVERLAY
1298 *********************************************/
1299.reveal .overlay {
1300 position: absolute;
1301 top: 0;
1302 left: 0;
1303 width: 100%;
1304 height: 100%;
1305 z-index: 1000;
1306 background: rgba(0, 0, 0, 0.9);
1307 opacity: 0;
1308 visibility: hidden;
1309 transition: all 0.3s ease; }
1310
1311.reveal .overlay.visible {
1312 opacity: 1;
1313 visibility: visible; }
1314
1315.reveal .overlay .spinner {
1316 position: absolute;
1317 display: block;
1318 top: 50%;
1319 left: 50%;
1320 width: 32px;
1321 height: 32px;
1322 margin: -16px 0 0 -16px;
1323 z-index: 10;
1324 background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D);
1325 visibility: visible;
1326 opacity: 0.6;
1327 transition: all 0.3s ease; }
1328
1329.reveal .overlay header {
1330 position: absolute;
1331 left: 0;
1332 top: 0;
1333 width: 100%;
1334 height: 40px;
1335 z-index: 2;
1336 border-bottom: 1px solid #222; }
1337
1338.reveal .overlay header a {
1339 display: inline-block;
1340 width: 40px;
1341 height: 40px;
1342 line-height: 36px;
1343 padding: 0 10px;
1344 float: right;
1345 opacity: 0.6;
1346 box-sizing: border-box; }
1347
1348.reveal .overlay header a:hover {
1349 opacity: 1; }
1350
1351.reveal .overlay header a .icon {
1352 display: inline-block;
1353 width: 20px;
1354 height: 20px;
1355 background-position: 50% 50%;
1356 background-size: 100%;
1357 background-repeat: no-repeat; }
1358
1359.reveal .overlay header a.close .icon {
1360 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); }
1361
1362.reveal .overlay header a.external .icon {
1363 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); }
1364
1365.reveal .overlay .viewport {
1366 position: absolute;
1367 display: -webkit-box;
1368 display: -ms-flexbox;
1369 display: flex;
1370 top: 40px;
1371 right: 0;
1372 bottom: 0;
1373 left: 0; }
1374
1375.reveal .overlay.overlay-preview .viewport iframe {
1376 width: 100%;
1377 height: 100%;
1378 max-width: 100%;
1379 max-height: 100%;
1380 border: 0;
1381 opacity: 0;
1382 visibility: hidden;
1383 transition: all 0.3s ease; }
1384
1385.reveal .overlay.overlay-preview.loaded .viewport iframe {
1386 opacity: 1;
1387 visibility: visible; }
1388
1389.reveal .overlay.overlay-preview.loaded .viewport-inner {
1390 position: absolute;
1391 z-index: -1;
1392 left: 0;
1393 top: 45%;
1394 width: 100%;
1395 text-align: center;
1396 letter-spacing: normal; }
1397
1398.reveal .overlay.overlay-preview .x-frame-error {
1399 opacity: 0;
1400 transition: opacity 0.3s ease 0.3s; }
1401
1402.reveal .overlay.overlay-preview.loaded .x-frame-error {
1403 opacity: 1; }
1404
1405.reveal .overlay.overlay-preview.loaded .spinner {
1406 opacity: 0;
1407 visibility: hidden;
1408 -webkit-transform: scale(0.2);
1409 transform: scale(0.2); }
1410
1411.reveal .overlay.overlay-help .viewport {
1412 overflow: auto;
1413 color: #fff; }
1414
1415.reveal .overlay.overlay-help .viewport .viewport-inner {
1416 width: 600px;
1417 margin: auto;
1418 padding: 20px 20px 80px 20px;
1419 text-align: center;
1420 letter-spacing: normal; }
1421
1422.reveal .overlay.overlay-help .viewport .viewport-inner .title {
1423 font-size: 20px; }
1424
1425.reveal .overlay.overlay-help .viewport .viewport-inner table {
1426 border: 1px solid #fff;
1427 border-collapse: collapse;
1428 font-size: 16px; }
1429
1430.reveal .overlay.overlay-help .viewport .viewport-inner table th,
1431.reveal .overlay.overlay-help .viewport .viewport-inner table td {
1432 width: 200px;
1433 padding: 14px;
1434 border: 1px solid #fff;
1435 vertical-align: middle; }
1436
1437.reveal .overlay.overlay-help .viewport .viewport-inner table th {
1438 padding-top: 20px;
1439 padding-bottom: 20px; }
1440
1441/*********************************************
1442 * PLAYBACK COMPONENT
1443 *********************************************/
1444.reveal .playback {
1445 position: absolute;
1446 left: 15px;
1447 bottom: 20px;
1448 z-index: 30;
1449 cursor: pointer;
1450 transition: all 400ms ease;
1451 -webkit-tap-highlight-color: transparent; }
1452
1453.reveal.overview .playback {
1454 opacity: 0;
1455 visibility: hidden; }
1456
1457/*********************************************
1458 * ROLLING LINKS
1459 *********************************************/
1460.reveal .roll {
1461 display: inline-block;
1462 line-height: 1.2;
1463 overflow: hidden;
1464 vertical-align: top;
1465 -webkit-perspective: 400px;
1466 perspective: 400px;
1467 -webkit-perspective-origin: 50% 50%;
1468 perspective-origin: 50% 50%; }
1469
1470.reveal .roll:hover {
1471 background: none;
1472 text-shadow: none; }
1473
1474.reveal .roll span {
1475 display: block;
1476 position: relative;
1477 padding: 0 2px;
1478 pointer-events: none;
1479 transition: all 400ms ease;
1480 -webkit-transform-origin: 50% 0%;
1481 transform-origin: 50% 0%;
1482 -webkit-transform-style: preserve-3d;
1483 transform-style: preserve-3d;
1484 -webkit-backface-visibility: hidden;
1485 backface-visibility: hidden; }
1486
1487.reveal .roll:hover span {
1488 background: rgba(0, 0, 0, 0.5);
1489 -webkit-transform: translate3d(0px, 0px, -45px) rotateX(90deg);
1490 transform: translate3d(0px, 0px, -45px) rotateX(90deg); }
1491
1492.reveal .roll span:after {
1493 content: attr(data-title);
1494 display: block;
1495 position: absolute;
1496 left: 0;
1497 top: 0;
1498 padding: 0 2px;
1499 -webkit-backface-visibility: hidden;
1500 backface-visibility: hidden;
1501 -webkit-transform-origin: 50% 0%;
1502 transform-origin: 50% 0%;
1503 -webkit-transform: translate3d(0px, 110%, 0px) rotateX(-90deg);
1504 transform: translate3d(0px, 110%, 0px) rotateX(-90deg); }
1505
1506/*********************************************
1507 * SPEAKER NOTES
1508 *********************************************/
1509.reveal aside.notes {
1510 display: none; }
1511
1512.reveal .speaker-notes {
1513 display: none;
1514 position: absolute;
1515 width: 25vw;
1516 height: 100%;
1517 top: 0;
1518 left: 100%;
1519 padding: 14px 18px 14px 18px;
1520 z-index: 1;
1521 font-size: 18px;
1522 line-height: 1.4;
1523 border: 1px solid rgba(0, 0, 0, 0.05);
1524 color: #222;
1525 background-color: #f5f5f5;
1526 overflow: auto;
1527 box-sizing: border-box;
1528 text-align: left;
1529 font-family: Helvetica, sans-serif;
1530 -webkit-overflow-scrolling: touch; }
1531 .reveal .speaker-notes .notes-placeholder {
1532 color: #ccc;
1533 font-style: italic; }
1534 .reveal .speaker-notes:focus {
1535 outline: none; }
1536 .reveal .speaker-notes:before {
1537 content: 'Speaker notes';
1538 display: block;
1539 margin-bottom: 10px;
1540 opacity: 0.5; }
1541
1542.reveal.show-notes {
1543 max-width: 75vw;
1544 overflow: visible; }
1545
1546.reveal.show-notes .speaker-notes {
1547 display: block; }
1548
1549@media screen and (min-width: 1600px) {
1550 .reveal .speaker-notes {
1551 font-size: 20px; } }
1552
1553@media screen and (max-width: 1024px) {
1554 .reveal.show-notes {
1555 border-left: 0;
1556 max-width: none;
1557 max-height: 70%;
1558 overflow: visible; }
1559 .reveal.show-notes .speaker-notes {
1560 top: 100%;
1561 left: 0;
1562 width: 100%;
1563 height: 42.8571428571%; } }
1564
1565@media screen and (max-width: 600px) {
1566 .reveal.show-notes {
1567 max-height: 60%; }
1568 .reveal.show-notes .speaker-notes {
1569 top: 100%;
1570 height: 66.6666666667%; }
1571 .reveal .speaker-notes {
1572 font-size: 14px; } }
1573
1574/*********************************************
1575 * ZOOM PLUGIN
1576 *********************************************/
1577.zoomed .reveal *,
1578.zoomed .reveal *:before,
1579.zoomed .reveal *:after {
1580 -webkit-backface-visibility: visible !important;
1581 backface-visibility: visible !important; }
1582
1583.zoomed .reveal .progress,
1584.zoomed .reveal .controls {
1585 opacity: 0; }
1586
1587.zoomed .reveal .roll span {
1588 background: none; }
1589
1590.zoomed .reveal .roll span:after {
1591 visibility: hidden; }
01592
=== added file 'openlp/core/display/html/reveal.js'
--- openlp/core/display/html/reveal.js 1970-01-01 00:00:00 +0000
+++ openlp/core/display/html/reveal.js 2019-02-13 20:54:40 +0000
@@ -0,0 +1,5586 @@
1/*!
2 * reveal.js
3 * http://revealjs.com
4 * MIT licensed
5 *
6 * Copyright (C) 2018 Hakim El Hattab, http://hakim.se
7 */
8(function( root, factory ) {
9 if( typeof define === 'function' && define.amd ) {
10 // AMD. Register as an anonymous module.
11 define( function() {
12 root.Reveal = factory();
13 return root.Reveal;
14 } );
15 } else if( typeof exports === 'object' ) {
16 // Node. Does not work with strict CommonJS.
17 module.exports = factory();
18 } else {
19 // Browser globals.
20 root.Reveal = factory();
21 }
22}( this, function() {
23
24 'use strict';
25
26 var Reveal;
27
28 // The reveal.js version
29 var VERSION = '3.7.0';
30
31 var SLIDES_SELECTOR = '.slides section',
32 HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
33 VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section',
34 HOME_SLIDE_SELECTOR = '.slides>section:first-of-type',
35 UA = navigator.userAgent,
36
37 // Configuration defaults, can be overridden at initialization time
38 config = {
39
40 // The "normal" size of the presentation, aspect ratio will be preserved
41 // when the presentation is scaled to fit different resolutions
42 width: 960,
43 height: 700,
44
45 // Factor of the display size that should remain empty around the content
46 margin: 0.04,
47
48 // Bounds for smallest/largest possible scale to apply to content
49 minScale: 0.2,
50 maxScale: 2.0,
51
52 // Display presentation control arrows
53 controls: true,
54
55 // Help the user learn the controls by providing hints, for example by
56 // bouncing the down arrow when they first encounter a vertical slide
57 controlsTutorial: true,
58
59 // Determines where controls appear, "edges" or "bottom-right"
60 controlsLayout: 'bottom-right',
61
62 // Visibility rule for backwards navigation arrows; "faded", "hidden"
63 // or "visible"
64 controlsBackArrows: 'faded',
65
66 // Display a presentation progress bar
67 progress: true,
68
69 // Display the page number of the current slide
70 slideNumber: false,
71
72 // Use 1 based indexing for # links to match slide number (default is zero
73 // based)
74 hashOneBasedIndex: false,
75
76 // Determine which displays to show the slide number on
77 showSlideNumber: 'all',
78
79 // Push each slide change to the browser history
80 history: false,
81
82 // Enable keyboard shortcuts for navigation
83 keyboard: true,
84
85 // Optional function that blocks keyboard events when retuning false
86 keyboardCondition: null,
87
88 // Enable the slide overview mode
89 overview: true,
90
91 // Disables the default reveal.js slide layout so that you can use
92 // custom CSS layout
93 disableLayout: false,
94
95 // Vertical centering of slides
96 center: true,
97
98 // Enables touch navigation on devices with touch input
99 touch: true,
100
101 // Loop the presentation
102 loop: false,
103
104 // Change the presentation direction to be RTL
105 rtl: false,
106
107 // Randomizes the order of slides each time the presentation loads
108 shuffle: false,
109
110 // Turns fragments on and off globally
111 fragments: true,
112
113 // Flags whether to include the current fragment in the URL,
114 // so that reloading brings you to the same fragment position
115 fragmentInURL: false,
116
117 // Flags if the presentation is running in an embedded mode,
118 // i.e. contained within a limited portion of the screen
119 embedded: false,
120
121 // Flags if we should show a help overlay when the question-mark
122 // key is pressed
123 help: true,
124
125 // Flags if it should be possible to pause the presentation (blackout)
126 pause: true,
127
128 // Flags if speaker notes should be visible to all viewers
129 showNotes: false,
130
131 // Global override for autolaying embedded media (video/audio/iframe)
132 // - null: Media will only autoplay if data-autoplay is present
133 // - true: All media will autoplay, regardless of individual setting
134 // - false: No media will autoplay, regardless of individual setting
135 autoPlayMedia: null,
136
137 // Controls automatic progression to the next slide
138 // - 0: Auto-sliding only happens if the data-autoslide HTML attribute
139 // is present on the current slide or fragment
140 // - 1+: All slides will progress automatically at the given interval
141 // - false: No auto-sliding, even if data-autoslide is present
142 autoSlide: 0,
143
144 // Stop auto-sliding after user input
145 autoSlideStoppable: true,
146
147 // Use this method for navigation when auto-sliding (defaults to navigateNext)
148 autoSlideMethod: null,
149
150 // Specify the average time in seconds that you think you will spend
151 // presenting each slide. This is used to show a pacing timer in the
152 // speaker view
153 defaultTiming: null,
154
155 // Enable slide navigation via mouse wheel
156 mouseWheel: false,
157
158 // Apply a 3D roll to links on hover
159 rollingLinks: false,
160
161 // Hides the address bar on mobile devices
162 hideAddressBar: true,
163
164 // Opens links in an iframe preview overlay
165 // Add `data-preview-link` and `data-preview-link="false"` to customise each link
166 // individually
167 previewLinks: false,
168
169 // Exposes the reveal.js API through window.postMessage
170 postMessage: true,
171
172 // Dispatches all reveal.js events to the parent window through postMessage
173 postMessageEvents: false,
174
175 // Focuses body when page changes visibility to ensure keyboard shortcuts work
176 focusBodyOnPageVisibilityChange: true,
177
178 // Transition style
179 transition: 'slide', // none/fade/slide/convex/concave/zoom
180
181 // Transition speed
182 transitionSpeed: 'default', // default/fast/slow
183
184 // Transition style for full page slide backgrounds
185 backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom
186
187 // Parallax background image
188 parallaxBackgroundImage: '', // CSS syntax, e.g. "a.jpg"
189
190 // Parallax background size
191 parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px"
192
193 // Parallax background repeat
194 parallaxBackgroundRepeat: '', // repeat/repeat-x/repeat-y/no-repeat/initial/inherit
195
196 // Parallax background position
197 parallaxBackgroundPosition: '', // CSS syntax, e.g. "top left"
198
199 // Amount of pixels to move the parallax background per slide step
200 parallaxBackgroundHorizontal: null,
201 parallaxBackgroundVertical: null,
202
203 // The maximum number of pages a single slide can expand onto when printing
204 // to PDF, unlimited by default
205 pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY,
206
207 // Prints each fragment on a separate slide
208 pdfSeparateFragments: true,
209
210 // Offset used to reduce the height of content within exported PDF pages.
211 // This exists to account for environment differences based on how you
212 // print to PDF. CLI printing options, like phantomjs and wkpdf, can end
213 // on precisely the total height of the document whereas in-browser
214 // printing has to end one pixel before.
215 pdfPageHeightOffset: -1,
216
217 // Number of slides away from the current that are visible
218 viewDistance: 3,
219
220 // The display mode that will be used to show slides
221 display: 'block',
222
223 // Script dependencies to load
224 dependencies: []
225
226 },
227
228 // Flags if Reveal.initialize() has been called
229 initialized = false,
230
231 // Flags if reveal.js is loaded (has dispatched the 'ready' event)
232 loaded = false,
233
234 // Flags if the overview mode is currently active
235 overview = false,
236
237 // Holds the dimensions of our overview slides, including margins
238 overviewSlideWidth = null,
239 overviewSlideHeight = null,
240
241 // The horizontal and vertical index of the currently active slide
242 indexh,
243 indexv,
244
245 // The previous and current slide HTML elements
246 previousSlide,
247 currentSlide,
248
249 previousBackground,
250
251 // Remember which directions that the user has navigated towards
252 hasNavigatedRight = false,
253 hasNavigatedDown = false,
254
255 // Slides may hold a data-state attribute which we pick up and apply
256 // as a class to the body. This list contains the combined state of
257 // all current slides.
258 state = [],
259
260 // The current scale of the presentation (see width/height config)
261 scale = 1,
262
263 // CSS transform that is currently applied to the slides container,
264 // split into two groups
265 slidesTransform = { layout: '', overview: '' },
266
267 // Cached references to DOM elements
268 dom = {},
269
270 // Features supported by the browser, see #checkCapabilities()
271 features = {},
272
273 // Client is a mobile device, see #checkCapabilities()
274 isMobileDevice,
275
276 // Client is a desktop Chrome, see #checkCapabilities()
277 isChrome,
278
279 // Throttles mouse wheel navigation
280 lastMouseWheelStep = 0,
281
282 // Delays updates to the URL due to a Chrome thumbnailer bug
283 writeURLTimeout = 0,
284
285 // Flags if the interaction event listeners are bound
286 eventsAreBound = false,
287
288 // The current auto-slide duration
289 autoSlide = 0,
290
291 // Auto slide properties
292 autoSlidePlayer,
293 autoSlideTimeout = 0,
294 autoSlideStartTime = -1,
295 autoSlidePaused = false,
296
297 // Holds information about the currently ongoing touch input
298 touch = {
299 startX: 0,
300 startY: 0,
301 startSpan: 0,
302 startCount: 0,
303 captured: false,
304 threshold: 40
305 },
306
307 // Holds information about the keyboard shortcuts
308 keyboardShortcuts = {
309 'N , SPACE': 'Next slide',
310 'P': 'Previous slide',
311 '&#8592; , H': 'Navigate left',
312 '&#8594; , L': 'Navigate right',
313 '&#8593; , K': 'Navigate up',
314 '&#8595; , J': 'Navigate down',
315 'Home': 'First slide',
316 'End': 'Last slide',
317 'B , .': 'Pause',
318 'F': 'Fullscreen',
319 'ESC, O': 'Slide overview'
320 },
321
322 // Holds custom key code mappings
323 registeredKeyBindings = {};
324
325 /**
326 * Starts up the presentation if the client is capable.
327 */
328 function initialize( options ) {
329
330 // Make sure we only initialize once
331 if( initialized === true ) return;
332
333 initialized = true;
334
335 checkCapabilities();
336
337 if( !features.transforms2d && !features.transforms3d ) {
338 document.body.setAttribute( 'class', 'no-transforms' );
339
340 // Since JS won't be running any further, we load all lazy
341 // loading elements upfront
342 var images = toArray( document.getElementsByTagName( 'img' ) ),
343 iframes = toArray( document.getElementsByTagName( 'iframe' ) );
344
345 var lazyLoadable = images.concat( iframes );
346
347 for( var i = 0, len = lazyLoadable.length; i < len; i++ ) {
348 var element = lazyLoadable[i];
349 if( element.getAttribute( 'data-src' ) ) {
350 element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
351 element.removeAttribute( 'data-src' );
352 }
353 }
354
355 // If the browser doesn't support core features we won't be
356 // using JavaScript to control the presentation
357 return;
358 }
359
360 // Cache references to key DOM elements
361 dom.wrapper = document.querySelector( '.reveal' );
362 dom.slides = document.querySelector( '.reveal .slides' );
363
364 // Force a layout when the whole page, incl fonts, has loaded
365 window.addEventListener( 'load', layout, false );
366
367 var query = Reveal.getQueryHash();
368
369 // Do not accept new dependencies via query config to avoid
370 // the potential of malicious script injection
371 if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];
372
373 // Copy options over to our config object
374 extend( config, options );
375 extend( config, query );
376
377 // Hide the address bar in mobile browsers
378 hideAddressBar();
379
380 // Loads the dependencies and continues to #start() once done
381 load();
382
383 }
384
385 /**
386 * Restarts up the presentation if the client is capable.
387 */
388 function reinitialize() {
389 initialized = false;
390 initialize(config);
391 }
392
393 /**
394 * Inspect the client to see what it's capable of, this
395 * should only happens once per runtime.
396 */
397 function checkCapabilities() {
398
399 isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA );
400 isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );
401
402 var testElement = document.createElement( 'div' );
403
404 features.transforms3d = 'WebkitPerspective' in testElement.style ||
405 'MozPerspective' in testElement.style ||
406 'msPerspective' in testElement.style ||
407 'OPerspective' in testElement.style ||
408 'perspective' in testElement.style;
409
410 features.transforms2d = 'WebkitTransform' in testElement.style ||
411 'MozTransform' in testElement.style ||
412 'msTransform' in testElement.style ||
413 'OTransform' in testElement.style ||
414 'transform' in testElement.style;
415
416 features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
417 features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function';
418
419 features.canvas = !!document.createElement( 'canvas' ).getContext;
420
421 // Transitions in the overview are disabled in desktop and
422 // Safari due to lag
423 features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA );
424
425 // Flags if we should use zoom instead of transform to scale
426 // up slides. Zoom produces crisper results but has a lot of
427 // xbrowser quirks so we only use it in whitelsited browsers.
428 features.zoom = 'zoom' in testElement.style && !isMobileDevice &&
429 ( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) );
430
431 }
432
433 /**
434 * Loads the dependencies of reveal.js. Dependencies are
435 * defined via the configuration option 'dependencies'
436 * and will be loaded prior to starting/binding reveal.js.
437 * Some dependencies may have an 'async' flag, if so they
438 * will load after reveal.js has been started up.
439 */
440 function load() {
441
442 var scripts = [],
443 scriptsAsync = [],
444 scriptsToPreload = 0;
445
446 // Called once synchronous scripts finish loading
447 function proceed() {
448 if( scriptsAsync.length ) {
449 // Load asynchronous scripts
450 head.js.apply( null, scriptsAsync );
451 }
452
453 start();
454 }
455
456 function loadScript( s ) {
457 head.ready( s.src.match( /([\w\d_\-]*)\.?js(\?[\w\d.=&]*)?$|[^\\\/]*$/i )[0], function() {
458 // Extension may contain callback functions
459 if( typeof s.callback === 'function' ) {
460 s.callback.apply( this );
461 }
462
463 if( --scriptsToPreload === 0 ) {
464 proceed();
465 }
466 });
467 }
468
469 for( var i = 0, len = config.dependencies.length; i < len; i++ ) {
470 var s = config.dependencies[i];
471
472 // Load if there's no condition or the condition is truthy
473 if( !s.condition || s.condition() ) {
474 if( s.async ) {
475 scriptsAsync.push( s.src );
476 }
477 else {
478 scripts.push( s.src );
479 }
480
481 loadScript( s );
482 }
483 }
484
485 if( scripts.length ) {
486 scriptsToPreload = scripts.length;
487
488 // Load synchronous scripts
489 head.js.apply( null, scripts );
490 }
491 else {
492 proceed();
493 }
494
495 }
496
497 /**
498 * Starts up reveal.js by binding input events and navigating
499 * to the current URL deeplink if there is one.
500 */
501 function start() {
502
503 loaded = true;
504
505 // Make sure we've got all the DOM elements we need
506 setupDOM();
507
508 // Listen to messages posted to this window
509 setupPostMessage();
510
511 // Prevent the slides from being scrolled out of view
512 setupScrollPrevention();
513
514 // Resets all vertical slides so that only the first is visible
515 resetVerticalSlides();
516
517 // Updates the presentation to match the current configuration values
518 configure();
519
520 // Read the initial hash
521 readURL();
522
523 // Update all backgrounds
524 updateBackground( true );
525
526 // Notify listeners that the presentation is ready but use a 1ms
527 // timeout to ensure it's not fired synchronously after #initialize()
528 setTimeout( function() {
529 // Enable transitions now that we're loaded
530 dom.slides.classList.remove( 'no-transition' );
531
532 dom.wrapper.classList.add( 'ready' );
533
534 dispatchEvent( 'ready', {
535 'indexh': indexh,
536 'indexv': indexv,
537 'currentSlide': currentSlide
538 } );
539 }, 1 );
540
541 // Special setup and config is required when printing to PDF
542 if( isPrintingPDF() ) {
543 removeEventListeners();
544
545 // The document needs to have loaded for the PDF layout
546 // measurements to be accurate
547 if( document.readyState === 'complete' ) {
548 setupPDF();
549 }
550 else {
551 window.addEventListener( 'load', setupPDF );
552 }
553 }
554
555 }
556
557 /**
558 * Finds and stores references to DOM elements which are
559 * required by the presentation. If a required element is
560 * not found, it is created.
561 */
562 function setupDOM() {
563
564 // Prevent transitions while we're loading
565 dom.slides.classList.add( 'no-transition' );
566
567 if( isMobileDevice ) {
568 dom.wrapper.classList.add( 'no-hover' );
569 }
570 else {
571 dom.wrapper.classList.remove( 'no-hover' );
572 }
573
574 if( /iphone/gi.test( UA ) ) {
575 dom.wrapper.classList.add( 'ua-iphone' );
576 }
577 else {
578 dom.wrapper.classList.remove( 'ua-iphone' );
579 }
580
581 // Background element
582 dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null );
583
584 // Progress bar
585 dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '<span></span>' );
586 dom.progressbar = dom.progress.querySelector( 'span' );
587
588 // Arrow controls
589 dom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls',
590 '<button class="navigate-left" aria-label="previous slide"><div class="controls-arrow"></div></button>' +
591 '<button class="navigate-right" aria-label="next slide"><div class="controls-arrow"></div></button>' +
592 '<button class="navigate-up" aria-label="above slide"><div class="controls-arrow"></div></button>' +
593 '<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>' );
594
595 // Slide number
596 dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
597
598 // Element containing notes that are visible to the audience
599 dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null );
600 dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' );
601 dom.speakerNotes.setAttribute( 'tabindex', '0' );
602
603 // Overlay graphic which is displayed during the paused mode
604 dom.pauseOverlay = createSingletonNode( dom.wrapper, 'div', 'pause-overlay', '<button class="resume-button">Resume presentation</button>' );
605 dom.resumeButton = dom.pauseOverlay.querySelector( '.resume-button' );
606
607 dom.wrapper.setAttribute( 'role', 'application' );
608
609 // There can be multiple instances of controls throughout the page
610 dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) );
611 dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) );
612 dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) );
613 dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) );
614 dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );
615 dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );
616
617 // The right and down arrows in the standard reveal.js controls
618 dom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' );
619 dom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' );
620
621 dom.statusDiv = createStatusDiv();
622 }
623
624 /**
625 * Creates a hidden div with role aria-live to announce the
626 * current slide content. Hide the div off-screen to make it
627 * available only to Assistive Technologies.
628 *
629 * @return {HTMLElement}
630 */
631 function createStatusDiv() {
632
633 var statusDiv = document.getElementById( 'aria-status-div' );
634 if( !statusDiv ) {
635 statusDiv = document.createElement( 'div' );
636 statusDiv.style.position = 'absolute';
637 statusDiv.style.height = '1px';
638 statusDiv.style.width = '1px';
639 statusDiv.style.overflow = 'hidden';
640 statusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )';
641 statusDiv.setAttribute( 'id', 'aria-status-div' );
642 statusDiv.setAttribute( 'aria-live', 'polite' );
643 statusDiv.setAttribute( 'aria-atomic','true' );
644 dom.wrapper.appendChild( statusDiv );
645 }
646 return statusDiv;
647
648 }
649
650 /**
651 * Converts the given HTML element into a string of text
652 * that can be announced to a screen reader. Hidden
653 * elements are excluded.
654 */
655 function getStatusText( node ) {
656
657 var text = '';
658
659 // Text node
660 if( node.nodeType === 3 ) {
661 text += node.textContent;
662 }
663 // Element node
664 else if( node.nodeType === 1 ) {
665
666 var isAriaHidden = node.getAttribute( 'aria-hidden' );
667 var isDisplayHidden = window.getComputedStyle( node )['display'] === 'none';
668 if( isAriaHidden !== 'true' && !isDisplayHidden ) {
669
670 toArray( node.childNodes ).forEach( function( child ) {
671 text += getStatusText( child );
672 } );
673
674 }
675
676 }
677
678 return text;
679
680 }
681
682 /**
683 * Configures the presentation for printing to a static
684 * PDF.
685 */
686 function setupPDF() {
687
688 var slideSize = getComputedSlideSize( window.innerWidth, window.innerHeight );
689
690 // Dimensions of the PDF pages
691 var pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),
692 pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );
693
694 // Dimensions of slides within the pages
695 var slideWidth = slideSize.width,
696 slideHeight = slideSize.height;
697
698 // Let the browser know what page size we want to print
699 injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' );
700
701 // Limit the size of certain elements to the dimensions of the slide
702 injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );
703
704 document.body.classList.add( 'print-pdf' );
705 document.body.style.width = pageWidth + 'px';
706 document.body.style.height = pageHeight + 'px';
707
708 // Make sure stretch elements fit on slide
709 layoutSlideContents( slideWidth, slideHeight );
710
711 // Add each slide's index as attributes on itself, we need these
712 // indices to generate slide numbers below
713 toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {
714 hslide.setAttribute( 'data-index-h', h );
715
716 if( hslide.classList.contains( 'stack' ) ) {
717 toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {
718 vslide.setAttribute( 'data-index-h', h );
719 vslide.setAttribute( 'data-index-v', v );
720 } );
721 }
722 } );
723
724 // Slide and slide background layout
725 toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
726
727 // Vertical stacks are not centred since their section
728 // children will be
729 if( slide.classList.contains( 'stack' ) === false ) {
730 // Center the slide inside of the page, giving the slide some margin
731 var left = ( pageWidth - slideWidth ) / 2,
732 top = ( pageHeight - slideHeight ) / 2;
733
734 var contentHeight = slide.scrollHeight;
735 var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );
736
737 // Adhere to configured pages per slide limit
738 numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide );
739
740 // Center slides vertically
741 if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) {
742 top = Math.max( ( pageHeight - contentHeight ) / 2, 0 );
743 }
744
745 // Wrap the slide in a page element and hide its overflow
746 // so that no page ever flows onto another
747 var page = document.createElement( 'div' );
748 page.className = 'pdf-page';
749 page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
750 slide.parentNode.insertBefore( page, slide );
751 page.appendChild( slide );
752
753 // Position the slide inside of the page
754 slide.style.left = left + 'px';
755 slide.style.top = top + 'px';
756 slide.style.width = slideWidth + 'px';
757
758 if( slide.slideBackgroundElement ) {
759 page.insertBefore( slide.slideBackgroundElement, slide );
760 }
761
762 // Inject notes if `showNotes` is enabled
763 if( config.showNotes ) {
764
765 // Are there notes for this slide?
766 var notes = getSlideNotes( slide );
767 if( notes ) {
768
769 var notesSpacing = 8;
770 var notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline';
771 var notesElement = document.createElement( 'div' );
772 notesElement.classList.add( 'speaker-notes' );
773 notesElement.classList.add( 'speaker-notes-pdf' );
774 notesElement.setAttribute( 'data-layout', notesLayout );
775 notesElement.innerHTML = notes;
776
777 if( notesLayout === 'separate-page' ) {
778 page.parentNode.insertBefore( notesElement, page.nextSibling );
779 }
780 else {
781 notesElement.style.left = notesSpacing + 'px';
782 notesElement.style.bottom = notesSpacing + 'px';
783 notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px';
784 page.appendChild( notesElement );
785 }
786
787 }
788
789 }
790
791 // Inject slide numbers if `slideNumbers` are enabled
792 if( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) {
793 var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1,
794 slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1;
795
796 var numberElement = document.createElement( 'div' );
797 numberElement.classList.add( 'slide-number' );
798 numberElement.classList.add( 'slide-number-pdf' );
799 numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV );
800 page.appendChild( numberElement );
801 }
802
803 // Copy page and show fragments one after another
804 if( config.pdfSeparateFragments ) {
805
806 // Each fragment 'group' is an array containing one or more
807 // fragments. Multiple fragments that appear at the same time
808 // are part of the same group.
809 var fragmentGroups = sortFragments( page.querySelectorAll( '.fragment' ), true );
810
811 var previousFragmentStep;
812 var previousPage;
813
814 fragmentGroups.forEach( function( fragments ) {
815
816 // Remove 'current-fragment' from the previous group
817 if( previousFragmentStep ) {
818 previousFragmentStep.forEach( function( fragment ) {
819 fragment.classList.remove( 'current-fragment' );
820 } );
821 }
822
823 // Show the fragments for the current index
824 fragments.forEach( function( fragment ) {
825 fragment.classList.add( 'visible', 'current-fragment' );
826 } );
827
828 // Create a separate page for the current fragment state
829 var clonedPage = page.cloneNode( true );
830 page.parentNode.insertBefore( clonedPage, ( previousPage || page ).nextSibling );
831
832 previousFragmentStep = fragments;
833 previousPage = clonedPage;
834
835 } );
836
837 // Reset the first/original page so that all fragments are hidden
838 fragmentGroups.forEach( function( fragments ) {
839 fragments.forEach( function( fragment ) {
840 fragment.classList.remove( 'visible', 'current-fragment' );
841 } );
842 } );
843
844 }
845 // Show all fragments
846 else {
847 toArray( page.querySelectorAll( '.fragment:not(.fade-out)' ) ).forEach( function( fragment ) {
848 fragment.classList.add( 'visible' );
849 } );
850 }
851
852 }
853
854 } );
855
856 // Notify subscribers that the PDF layout is good to go
857 dispatchEvent( 'pdf-ready' );
858
859 }
860
861 /**
862 * This is an unfortunate necessity. Some actions – such as
863 * an input field being focused in an iframe or using the
864 * keyboard to expand text selection beyond the bounds of
865 * a slide – can trigger our content to be pushed out of view.
866 * This scrolling can not be prevented by hiding overflow in
867 * CSS (we already do) so we have to resort to repeatedly
868 * checking if the slides have been offset :(
869 */
870 function setupScrollPrevention() {
871
872 setInterval( function() {
873 if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) {
874 dom.wrapper.scrollTop = 0;
875 dom.wrapper.scrollLeft = 0;
876 }
877 }, 1000 );
878
879 }
880
881 /**
882 * Creates an HTML element and returns a reference to it.
883 * If the element already exists the existing instance will
884 * be returned.
885 *
886 * @param {HTMLElement} container
887 * @param {string} tagname
888 * @param {string} classname
889 * @param {string} innerHTML
890 *
891 * @return {HTMLElement}
892 */
893 function createSingletonNode( container, tagname, classname, innerHTML ) {
894
895 // Find all nodes matching the description
896 var nodes = container.querySelectorAll( '.' + classname );
897
898 // Check all matches to find one which is a direct child of
899 // the specified container
900 for( var i = 0; i < nodes.length; i++ ) {
901 var testNode = nodes[i];
902 if( testNode.parentNode === container ) {
903 return testNode;
904 }
905 }
906
907 // If no node was found, create it now
908 var node = document.createElement( tagname );
909 node.className = classname;
910 if( typeof innerHTML === 'string' ) {
911 node.innerHTML = innerHTML;
912 }
913 container.appendChild( node );
914
915 return node;
916
917 }
918
919 /**
920 * Creates the slide background elements and appends them
921 * to the background container. One element is created per
922 * slide no matter if the given slide has visible background.
923 */
924 function createBackgrounds() {
925
926 var printMode = isPrintingPDF();
927
928 // Clear prior backgrounds
929 dom.background.innerHTML = '';
930 dom.background.classList.add( 'no-transition' );
931
932 // Iterate over all horizontal slides
933 toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) {
934
935 var backgroundStack = createBackground( slideh, dom.background );
936
937 // Iterate over all vertical slides
938 toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) {
939
940 createBackground( slidev, backgroundStack );
941
942 backgroundStack.classList.add( 'stack' );
943
944 } );
945
946 } );
947
948 // Add parallax background if specified
949 if( config.parallaxBackgroundImage ) {
950
951 dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")';
952 dom.background.style.backgroundSize = config.parallaxBackgroundSize;
953 dom.background.style.backgroundRepeat = config.parallaxBackgroundRepeat;
954 dom.background.style.backgroundPosition = config.parallaxBackgroundPosition;
955
956 // Make sure the below properties are set on the element - these properties are
957 // needed for proper transitions to be set on the element via CSS. To remove
958 // annoying background slide-in effect when the presentation starts, apply
959 // these properties after short time delay
960 setTimeout( function() {
961 dom.wrapper.classList.add( 'has-parallax-background' );
962 }, 1 );
963
964 }
965 else {
966
967 dom.background.style.backgroundImage = '';
968 dom.wrapper.classList.remove( 'has-parallax-background' );
969
970 }
971
972 }
973
974 /**
975 * Creates a background for the given slide.
976 *
977 * @param {HTMLElement} slide
978 * @param {HTMLElement} container The element that the background
979 * should be appended to
980 * @return {HTMLElement} New background div
981 */
982 function createBackground( slide, container ) {
983
984
985 // Main slide background element
986 var element = document.createElement( 'div' );
987 element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' );
988
989 // Inner background element that wraps images/videos/iframes
990 var contentElement = document.createElement( 'div' );
991 contentElement.className = 'slide-background-content';
992
993 element.appendChild( contentElement );
994 container.appendChild( element );
995
996 slide.slideBackgroundElement = element;
997 slide.slideBackgroundContentElement = contentElement;
998
999 // Syncs the background to reflect all current background settings
1000 syncBackground( slide );
1001
1002 return element;
1003
1004 }
1005
1006 /**
1007 * Renders all of the visual properties of a slide background
1008 * based on the various background attributes.
1009 *
1010 * @param {HTMLElement} slide
1011 */
1012 function syncBackground( slide ) {
1013
1014 var element = slide.slideBackgroundElement,
1015 contentElement = slide.slideBackgroundContentElement;
1016
1017 // Reset the prior background state in case this is not the
1018 // initial sync
1019 slide.classList.remove( 'has-dark-background' );
1020 slide.classList.remove( 'has-light-background' );
1021
1022 element.removeAttribute( 'data-loaded' );
1023 element.removeAttribute( 'data-background-hash' );
1024 element.removeAttribute( 'data-background-size' );
1025 element.removeAttribute( 'data-background-transition' );
1026 element.style.backgroundColor = '';
1027
1028 contentElement.style.backgroundSize = '';
1029 contentElement.style.backgroundRepeat = '';
1030 contentElement.style.backgroundPosition = '';
1031 contentElement.style.backgroundImage = '';
1032 contentElement.style.opacity = '';
1033 contentElement.innerHTML = '';
1034
1035 var data = {
1036 background: slide.getAttribute( 'data-background' ),
1037 backgroundSize: slide.getAttribute( 'data-background-size' ),
1038 backgroundImage: slide.getAttribute( 'data-background-image' ),
1039 backgroundVideo: slide.getAttribute( 'data-background-video' ),
1040 backgroundIframe: slide.getAttribute( 'data-background-iframe' ),
1041 backgroundColor: slide.getAttribute( 'data-background-color' ),
1042 backgroundRepeat: slide.getAttribute( 'data-background-repeat' ),
1043 backgroundPosition: slide.getAttribute( 'data-background-position' ),
1044 backgroundTransition: slide.getAttribute( 'data-background-transition' ),
1045 backgroundOpacity: slide.getAttribute( 'data-background-opacity' )
1046 };
1047
1048 if( data.background ) {
1049 // Auto-wrap image urls in url(...)
1050 if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) {
1051 slide.setAttribute( 'data-background-image', data.background );
1052 }
1053 else {
1054 element.style.background = data.background;
1055 }
1056 }
1057
1058 // Create a hash for this combination of background settings.
1059 // This is used to determine when two slide backgrounds are
1060 // the same.
1061 if( data.background || data.backgroundColor || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) {
1062 element.setAttribute( 'data-background-hash', data.background +
1063 data.backgroundSize +
1064 data.backgroundImage +
1065 data.backgroundVideo +
1066 data.backgroundIframe +
1067 data.backgroundColor +
1068 data.backgroundRepeat +
1069 data.backgroundPosition +
1070 data.backgroundTransition +
1071 data.backgroundOpacity );
1072 }
1073
1074 // Additional and optional background properties
1075 if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize );
1076 if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
1077 if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
1078
1079 // Background image options are set on the content wrapper
1080 if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;
1081 if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat;
1082 if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition;
1083 if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity;
1084
1085 // If this slide has a background color, add a class that
1086 // signals if it is light or dark. If the slide has no background
1087 // color, no class will be set
1088 var computedBackgroundStyle = window.getComputedStyle( element );
1089 if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) {
1090 var rgb = colorToRgb( computedBackgroundStyle.backgroundColor );
1091
1092 // Ignore fully transparent backgrounds. Some browsers return
1093 // rgba(0,0,0,0) when reading the computed background color of
1094 // an element with no background
1095 if( rgb && rgb.a !== 0 ) {
1096 if( colorBrightness( computedBackgroundStyle.backgroundColor ) < 128 ) {
1097 slide.classList.add( 'has-dark-background' );
1098 }
1099 else {
1100 slide.classList.add( 'has-light-background' );
1101 }
1102 }
1103 }
1104
1105 }
1106
1107 /**
1108 * Registers a listener to postMessage events, this makes it
1109 * possible to call all reveal.js API methods from another
1110 * window. For example:
1111 *
1112 * revealWindow.postMessage( JSON.stringify({
1113 * method: 'slide',
1114 * args: [ 2 ]
1115 * }), '*' );
1116 */
1117 function setupPostMessage() {
1118
1119 if( config.postMessage ) {
1120 window.addEventListener( 'message', function ( event ) {
1121 var data = event.data;
1122
1123 // Make sure we're dealing with JSON
1124 if( typeof data === 'string' && data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) {
1125 data = JSON.parse( data );
1126
1127 // Check if the requested method can be found
1128 if( data.method && typeof Reveal[data.method] === 'function' ) {
1129 Reveal[data.method].apply( Reveal, data.args );
1130 }
1131 }
1132 }, false );
1133 }
1134
1135 }
1136
1137 /**
1138 * Applies the configuration settings from the config
1139 * object. May be called multiple times.
1140 *
1141 * @param {object} options
1142 */
1143 function configure( options ) {
1144
1145 var oldTransition = config.transition;
1146
1147 // New config options may be passed when this method
1148 // is invoked through the API after initialization
1149 if( typeof options === 'object' ) extend( config, options );
1150
1151 // Abort if reveal.js hasn't finished loading, config
1152 // changes will be applied automatically once loading
1153 // finishes
1154 if( loaded === false ) return;
1155
1156 var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;
1157
1158 // Remove the previously configured transition class
1159 dom.wrapper.classList.remove( oldTransition );
1160
1161 // Force linear transition based on browser capabilities
1162 if( features.transforms3d === false ) config.transition = 'linear';
1163
1164 dom.wrapper.classList.add( config.transition );
1165
1166 dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
1167 dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );
1168
1169 dom.controls.style.display = config.controls ? 'block' : 'none';
1170 dom.progress.style.display = config.progress ? 'block' : 'none';
1171
1172 dom.controls.setAttribute( 'data-controls-layout', config.controlsLayout );
1173 dom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows );
1174
1175 if( config.shuffle ) {
1176 shuffle();
1177 }
1178
1179 if( config.rtl ) {
1180 dom.wrapper.classList.add( 'rtl' );
1181 }
1182 else {
1183 dom.wrapper.classList.remove( 'rtl' );
1184 }
1185
1186 if( config.center ) {
1187 dom.wrapper.classList.add( 'center' );
1188 }
1189 else {
1190 dom.wrapper.classList.remove( 'center' );
1191 }
1192
1193 // Exit the paused mode if it was configured off
1194 if( config.pause === false ) {
1195 resume();
1196 }
1197
1198 if( config.showNotes ) {
1199 dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' );
1200 }
1201
1202 if( config.mouseWheel ) {
1203 document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
1204 document.addEventListener( 'mousewheel', onDocumentMouseScroll, false );
1205 }
1206 else {
1207 document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
1208 document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false );
1209 }
1210
1211 // Rolling 3D links
1212 if( config.rollingLinks ) {
1213 enableRollingLinks();
1214 }
1215 else {
1216 disableRollingLinks();
1217 }
1218
1219 // Iframe link previews
1220 if( config.previewLinks ) {
1221 enablePreviewLinks();
1222 disablePreviewLinks( '[data-preview-link=false]' );
1223 }
1224 else {
1225 disablePreviewLinks();
1226 enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' );
1227 }
1228
1229 // Remove existing auto-slide controls
1230 if( autoSlidePlayer ) {
1231 autoSlidePlayer.destroy();
1232 autoSlidePlayer = null;
1233 }
1234
1235 // Generate auto-slide controls if needed
1236 if( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) {
1237 autoSlidePlayer = new Playback( dom.wrapper, function() {
1238 return Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 );
1239 } );
1240
1241 autoSlidePlayer.on( 'click', onAutoSlidePlayerClick );
1242 autoSlidePaused = false;
1243 }
1244
1245 // When fragments are turned off they should be visible
1246 if( config.fragments === false ) {
1247 toArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( function( element ) {
1248 element.classList.add( 'visible' );
1249 element.classList.remove( 'current-fragment' );
1250 } );
1251 }
1252
1253 // Slide numbers
1254 var slideNumberDisplay = 'none';
1255 if( config.slideNumber && !isPrintingPDF() ) {
1256 if( config.showSlideNumber === 'all' ) {
1257 slideNumberDisplay = 'block';
1258 }
1259 else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) {
1260 slideNumberDisplay = 'block';
1261 }
1262 }
1263
1264 dom.slideNumber.style.display = slideNumberDisplay;
1265
1266 sync();
1267
1268 }
1269
1270 /**
1271 * Binds all event listeners.
1272 */
1273 function addEventListeners() {
1274
1275 eventsAreBound = true;
1276
1277 window.addEventListener( 'hashchange', onWindowHashChange, false );
1278 window.addEventListener( 'resize', onWindowResize, false );
1279
1280 if( config.touch ) {
1281 if( 'onpointerdown' in window ) {
1282 // Use W3C pointer events
1283 dom.wrapper.addEventListener( 'pointerdown', onPointerDown, false );
1284 dom.wrapper.addEventListener( 'pointermove', onPointerMove, false );
1285 dom.wrapper.addEventListener( 'pointerup', onPointerUp, false );
1286 }
1287 else if( window.navigator.msPointerEnabled ) {
1288 // IE 10 uses prefixed version of pointer events
1289 dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false );
1290 dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false );
1291 dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false );
1292 }
1293 else {
1294 // Fall back to touch events
1295 dom.wrapper.addEventListener( 'touchstart', onTouchStart, false );
1296 dom.wrapper.addEventListener( 'touchmove', onTouchMove, false );
1297 dom.wrapper.addEventListener( 'touchend', onTouchEnd, false );
1298 }
1299 }
1300
1301 if( config.keyboard ) {
1302 document.addEventListener( 'keydown', onDocumentKeyDown, false );
1303 document.addEventListener( 'keypress', onDocumentKeyPress, false );
1304 }
1305
1306 if( config.progress && dom.progress ) {
1307 dom.progress.addEventListener( 'click', onProgressClicked, false );
1308 }
1309
1310 dom.resumeButton.addEventListener( 'click', resume, false );
1311
1312 if( config.focusBodyOnPageVisibilityChange ) {
1313 var visibilityChange;
1314
1315 if( 'hidden' in document ) {
1316 visibilityChange = 'visibilitychange';
1317 }
1318 else if( 'msHidden' in document ) {
1319 visibilityChange = 'msvisibilitychange';
1320 }
1321 else if( 'webkitHidden' in document ) {
1322 visibilityChange = 'webkitvisibilitychange';
1323 }
1324
1325 if( visibilityChange ) {
1326 document.addEventListener( visibilityChange, onPageVisibilityChange, false );
1327 }
1328 }
1329
1330 // Listen to both touch and click events, in case the device
1331 // supports both
1332 var pointerEvents = [ 'touchstart', 'click' ];
1333
1334 // Only support touch for Android, fixes double navigations in
1335 // stock browser
1336 if( UA.match( /android/gi ) ) {
1337 pointerEvents = [ 'touchstart' ];
1338 }
1339
1340 pointerEvents.forEach( function( eventName ) {
1341 dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } );
1342 dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } );
1343 dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } );
1344 dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } );
1345 dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } );
1346 dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } );
1347 } );
1348
1349 }
1350
1351 /**
1352 * Unbinds all event listeners.
1353 */
1354 function removeEventListeners() {
1355
1356 eventsAreBound = false;
1357
1358 document.removeEventListener( 'keydown', onDocumentKeyDown, false );
1359 document.removeEventListener( 'keypress', onDocumentKeyPress, false );
1360 window.removeEventListener( 'hashchange', onWindowHashChange, false );
1361 window.removeEventListener( 'resize', onWindowResize, false );
1362
1363 dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false );
1364 dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false );
1365 dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false );
1366
1367 dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false );
1368 dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false );
1369 dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false );
1370
1371 dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false );
1372 dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false );
1373 dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false );
1374
1375 dom.resumeButton.removeEventListener( 'click', resume, false );
1376
1377 if ( config.progress && dom.progress ) {
1378 dom.progress.removeEventListener( 'click', onProgressClicked, false );
1379 }
1380
1381 [ 'touchstart', 'click' ].forEach( function( eventName ) {
1382 dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } );
1383 dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } );
1384 dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } );
1385 dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } );
1386 dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } );
1387 dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } );
1388 } );
1389
1390 }
1391
1392 /**
1393 * Add a custom key binding with optional description to
1394 * be added to the help screen.
1395 */
1396 function addKeyBinding( binding, callback ) {
1397
1398 if( typeof binding === 'object' && binding.keyCode ) {
1399 registeredKeyBindings[binding.keyCode] = {
1400 callback: callback,
1401 key: binding.key,
1402 description: binding.description
1403 };
1404 }
1405 else {
1406 registeredKeyBindings[binding] = {
1407 callback: callback,
1408 key: null,
1409 description: null
1410 };
1411 }
1412
1413 }
1414
1415 /**
1416 * Removes the specified custom key binding.
1417 */
1418 function removeKeyBinding( keyCode ) {
1419
1420 delete registeredKeyBindings[keyCode];
1421
1422 }
1423
1424 /**
1425 * Extend object a with the properties of object b.
1426 * If there's a conflict, object b takes precedence.
1427 *
1428 * @param {object} a
1429 * @param {object} b
1430 */
1431 function extend( a, b ) {
1432
1433 for( var i in b ) {
1434 a[ i ] = b[ i ];
1435 }
1436
1437 return a;
1438
1439 }
1440
1441 /**
1442 * Converts the target object to an array.
1443 *
1444 * @param {object} o
1445 * @return {object[]}
1446 */
1447 function toArray( o ) {
1448
1449 return Array.prototype.slice.call( o );
1450
1451 }
1452
1453 /**
1454 * Utility for deserializing a value.
1455 *
1456 * @param {*} value
1457 * @return {*}
1458 */
1459 function deserialize( value ) {
1460
1461 if( typeof value === 'string' ) {
1462 if( value === 'null' ) return null;
1463 else if( value === 'true' ) return true;
1464 else if( value === 'false' ) return false;
1465 else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value );
1466 }
1467
1468 return value;
1469
1470 }
1471
1472 /**
1473 * Measures the distance in pixels between point a
1474 * and point b.
1475 *
1476 * @param {object} a point with x/y properties
1477 * @param {object} b point with x/y properties
1478 *
1479 * @return {number}
1480 */
1481 function distanceBetween( a, b ) {
1482
1483 var dx = a.x - b.x,
1484 dy = a.y - b.y;
1485
1486 return Math.sqrt( dx*dx + dy*dy );
1487
1488 }
1489
1490 /**
1491 * Applies a CSS transform to the target element.
1492 *
1493 * @param {HTMLElement} element
1494 * @param {string} transform
1495 */
1496 function transformElement( element, transform ) {
1497
1498 element.style.WebkitTransform = transform;
1499 element.style.MozTransform = transform;
1500 element.style.msTransform = transform;
1501 element.style.transform = transform;
1502
1503 }
1504
1505 /**
1506 * Applies CSS transforms to the slides container. The container
1507 * is transformed from two separate sources: layout and the overview
1508 * mode.
1509 *
1510 * @param {object} transforms
1511 */
1512 function transformSlides( transforms ) {
1513
1514 // Pick up new transforms from arguments
1515 if( typeof transforms.layout === 'string' ) slidesTransform.layout = transforms.layout;
1516 if( typeof transforms.overview === 'string' ) slidesTransform.overview = transforms.overview;
1517
1518 // Apply the transforms to the slides container
1519 if( slidesTransform.layout ) {
1520 transformElement( dom.slides, slidesTransform.layout + ' ' + slidesTransform.overview );
1521 }
1522 else {
1523 transformElement( dom.slides, slidesTransform.overview );
1524 }
1525
1526 }
1527
1528 /**
1529 * Injects the given CSS styles into the DOM.
1530 *
1531 * @param {string} value
1532 */
1533 function injectStyleSheet( value ) {
1534
1535 var tag = document.createElement( 'style' );
1536 tag.type = 'text/css';
1537 if( tag.styleSheet ) {
1538 tag.styleSheet.cssText = value;
1539 }
1540 else {
1541 tag.appendChild( document.createTextNode( value ) );
1542 }
1543 document.getElementsByTagName( 'head' )[0].appendChild( tag );
1544
1545 }
1546
1547 /**
1548 * Find the closest parent that matches the given
1549 * selector.
1550 *
1551 * @param {HTMLElement} target The child element
1552 * @param {String} selector The CSS selector to match
1553 * the parents against
1554 *
1555 * @return {HTMLElement} The matched parent or null
1556 * if no matching parent was found
1557 */
1558 function closestParent( target, selector ) {
1559
1560 var parent = target.parentNode;
1561
1562 while( parent ) {
1563
1564 // There's some overhead doing this each time, we don't
1565 // want to rewrite the element prototype but should still
1566 // be enough to feature detect once at startup...
1567 var matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector;
1568
1569 // If we find a match, we're all set
1570 if( matchesMethod && matchesMethod.call( parent, selector ) ) {
1571 return parent;
1572 }
1573
1574 // Keep searching
1575 parent = parent.parentNode;
1576
1577 }
1578
1579 return null;
1580
1581 }
1582
1583 /**
1584 * Converts various color input formats to an {r:0,g:0,b:0} object.
1585 *
1586 * @param {string} color The string representation of a color
1587 * @example
1588 * colorToRgb('#000');
1589 * @example
1590 * colorToRgb('#000000');
1591 * @example
1592 * colorToRgb('rgb(0,0,0)');
1593 * @example
1594 * colorToRgb('rgba(0,0,0)');
1595 *
1596 * @return {{r: number, g: number, b: number, [a]: number}|null}
1597 */
1598 function colorToRgb( color ) {
1599
1600 var hex3 = color.match( /^#([0-9a-f]{3})$/i );
1601 if( hex3 && hex3[1] ) {
1602 hex3 = hex3[1];
1603 return {
1604 r: parseInt( hex3.charAt( 0 ), 16 ) * 0x11,
1605 g: parseInt( hex3.charAt( 1 ), 16 ) * 0x11,
1606 b: parseInt( hex3.charAt( 2 ), 16 ) * 0x11
1607 };
1608 }
1609
1610 var hex6 = color.match( /^#([0-9a-f]{6})$/i );
1611 if( hex6 && hex6[1] ) {
1612 hex6 = hex6[1];
1613 return {
1614 r: parseInt( hex6.substr( 0, 2 ), 16 ),
1615 g: parseInt( hex6.substr( 2, 2 ), 16 ),
1616 b: parseInt( hex6.substr( 4, 2 ), 16 )
1617 };
1618 }
1619
1620 var rgb = color.match( /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i );
1621 if( rgb ) {
1622 return {
1623 r: parseInt( rgb[1], 10 ),
1624 g: parseInt( rgb[2], 10 ),
1625 b: parseInt( rgb[3], 10 )
1626 };
1627 }
1628
1629 var rgba = color.match( /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i );
1630 if( rgba ) {
1631 return {
1632 r: parseInt( rgba[1], 10 ),
1633 g: parseInt( rgba[2], 10 ),
1634 b: parseInt( rgba[3], 10 ),
1635 a: parseFloat( rgba[4] )
1636 };
1637 }
1638
1639 return null;
1640
1641 }
1642
1643 /**
1644 * Calculates brightness on a scale of 0-255.
1645 *
1646 * @param {string} color See colorToRgb for supported formats.
1647 * @see {@link colorToRgb}
1648 */
The diff has been truncated for viewing.