pageflow 12.2.0 → 12.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pageflow might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -92
- data/admins/pageflow/accounts.rb +1 -0
- data/app/assets/audios/pageflow/unmute.mp3 +0 -0
- data/app/assets/javascripts/pageflow/asset_urls.js.erb +1 -1
- data/app/assets/javascripts/pageflow/audio/multi_player.js +4 -0
- data/app/assets/javascripts/pageflow/audio_player.js +1 -1
- data/app/assets/javascripts/pageflow/background_media.js +22 -0
- data/app/assets/javascripts/pageflow/base.js +2 -0
- data/app/assets/javascripts/pageflow/browser/agent.js +92 -78
- data/app/assets/javascripts/pageflow/browser/autoplay_support.js +2 -2
- data/app/assets/javascripts/pageflow/cookie_notice.js +7 -0
- data/app/assets/javascripts/pageflow/dist/react.js +1312 -329
- data/app/assets/javascripts/pageflow/editor/models/configuration.js +5 -5
- data/app/assets/javascripts/pageflow/editor/templates/background_positioning_sliders.jst.ejs +8 -0
- data/app/assets/javascripts/pageflow/editor/views/background_positioning_sliders_view.js +37 -23
- data/app/assets/javascripts/pageflow/editor/views/background_positioning_view.js +2 -2
- data/app/assets/javascripts/pageflow/editor/views/configuration_editors/video.js +1 -1
- data/app/assets/javascripts/pageflow/editor/views/edit_widget_view.js +9 -0
- data/app/assets/javascripts/pageflow/editor/views/info_box_view.js +8 -0
- data/app/assets/javascripts/pageflow/editor/views/widget_types/cookie_notice_bar.js +15 -0
- data/app/assets/javascripts/pageflow/media_player.js +7 -3
- data/app/assets/javascripts/pageflow/media_player/handle_failed_play.js +34 -0
- data/app/assets/javascripts/pageflow/media_player/volume_fading/web_audio.js +29 -3
- data/app/assets/javascripts/pageflow/seed_entry_data.js +3 -3
- data/app/assets/javascripts/pageflow/slideshow.js +17 -9
- data/app/assets/javascripts/pageflow/slideshow/adjacent_pages.js +7 -2
- data/app/assets/javascripts/pageflow/slideshow/adjacent_preloader.js +26 -0
- data/app/assets/javascripts/pageflow/slideshow/atmo.js +23 -12
- data/app/assets/javascripts/pageflow/slideshow/lazy_page_widget.js +2 -2
- data/app/assets/javascripts/pageflow/slideshow/{adjacent_preparer.js → successor_preparer.js} +14 -11
- data/app/assets/javascripts/pageflow/ui/views/configuration_editor_view.js +2 -2
- data/app/assets/javascripts/pageflow/video_player/lazy.js +1 -1
- data/app/assets/javascripts/pageflow/visited.js +2 -0
- data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +34 -10
- data/app/assets/stylesheets/pageflow/page_types/video.scss +1 -4
- data/app/assets/stylesheets/pageflow/page_types/video/mobile_poster.scss +15 -0
- data/app/assets/stylesheets/pageflow/themes/default/background_media_unmute_button.scss +68 -0
- data/app/assets/stylesheets/pageflow/themes/default/base.scss +2 -0
- data/app/assets/stylesheets/pageflow/themes/default/cookie_notice_bar.scss +57 -0
- data/app/assets/stylesheets/pageflow/themes/default/page.scss +1 -0
- data/app/assets/stylesheets/pageflow/themes/default/page/hyphenate.scss +24 -0
- data/app/assets/stylesheets/pageflow/themes/default/slideshow.scss +1 -0
- data/app/controllers/pageflow/admin/initial_passwords_controller.rb +8 -0
- data/app/helpers/pageflow/common_entry_seed_helper.rb +1 -0
- data/app/helpers/pageflow/entries_helper.rb +20 -2
- data/app/helpers/pageflow/entry_json_seed_helper.rb +1 -1
- data/app/helpers/pageflow/public_i18n_helper.rb +6 -1
- data/app/views/admin/accounts/_form.html.erb +1 -0
- data/app/views/pageflow/admin/initial_passwords/edit.html.erb +16 -0
- data/app/views/pageflow/entry_json_seed/_entry.json.jbuilder +1 -1
- data/app/views/pageflow/user_mailer/invitation.html.erb +2 -2
- data/app/views/pageflow/user_mailer/invitation.text.erb +1 -1
- data/config/locales/de.yml +14 -6
- data/config/locales/en.yml +16 -8
- data/config/routes.rb +6 -0
- data/db/migrate/20180528144334_add_privacy_link_url_to_themings.rb +5 -0
- data/lib/pageflow/built_in_widget_type.rb +8 -0
- data/lib/pageflow/built_in_widget_types_plugin.rb +2 -0
- data/lib/pageflow/version.rb +1 -1
- data/vendor/assets/javascripts/audio5.min.js +280 -129
- metadata +19 -7
- data/app/assets/javascripts/pageflow/media_player/catch_play_promise.js +0 -23
- data/app/assets/javascripts/pageflow/slideshow/progressive_preload.js +0 -42
@@ -10,6 +10,7 @@ module Pageflow
|
|
10
10
|
{
|
11
11
|
locale: entry.locale,
|
12
12
|
slug: entry.slug,
|
13
|
+
theming: entry.theming.as_json(only: [:privacy_link_url]),
|
13
14
|
page_types: PageTypesSeed.new(config).as_json,
|
14
15
|
file_url_templates: FileUrlTemplatesSeed.new(config).as_json,
|
15
16
|
file_model_types: config.file_types
|
@@ -27,11 +27,29 @@ module Pageflow
|
|
27
27
|
|
28
28
|
def entry_global_links(entry)
|
29
29
|
links = []
|
30
|
+
|
30
31
|
if entry.theming.imprint_link_label.present? && entry.theming.imprint_link_url.present?
|
31
|
-
links << link_to(raw(entry.theming.imprint_link_label),
|
32
|
+
links << link_to(raw(entry.theming.imprint_link_label),
|
33
|
+
entry.theming.imprint_link_url,
|
34
|
+
target: '_blank',
|
35
|
+
tabindex: 2,
|
36
|
+
class: 'legal')
|
32
37
|
end
|
38
|
+
|
33
39
|
if entry.theming.copyright_link_label.present? && entry.theming.copyright_link_url.present?
|
34
|
-
links << link_to(raw(entry.theming.copyright_link_label),
|
40
|
+
links << link_to(raw(entry.theming.copyright_link_label),
|
41
|
+
entry.theming.copyright_link_url,
|
42
|
+
target: '_blank',
|
43
|
+
tabindex: 2,
|
44
|
+
class: 'copy')
|
45
|
+
end
|
46
|
+
|
47
|
+
if entry.theming.privacy_link_url.present?
|
48
|
+
links << link_to(I18n.t('pageflow.public.privacy_notice'),
|
49
|
+
"#{entry.theming.privacy_link_url}?lang=#{entry.locale}",
|
50
|
+
target: '_blank',
|
51
|
+
tabindex: 2,
|
52
|
+
class: 'privacy')
|
35
53
|
end
|
36
54
|
|
37
55
|
if links.any?
|
@@ -7,11 +7,16 @@ module Pageflow
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def public_i18n_translations(entry)
|
10
|
+
merge_ignoring_nil = lambda do |_, fallback, value|
|
11
|
+
value.presence || fallback
|
12
|
+
end
|
13
|
+
|
10
14
|
{
|
11
15
|
pageflow: {
|
12
16
|
public: I18n.t('pageflow.public', locale: I18n.default_locale)
|
13
17
|
.dup
|
14
|
-
.deep_merge(I18n.t('pageflow.public', locale: entry.locale)
|
18
|
+
.deep_merge(I18n.t('pageflow.public', locale: entry.locale),
|
19
|
+
&merge_ignoring_nil)
|
15
20
|
}
|
16
21
|
}
|
17
22
|
end
|
@@ -34,6 +34,7 @@
|
|
34
34
|
<%= theming.input :imprint_link_url %>
|
35
35
|
<%= theming.input :copyright_link_label %>
|
36
36
|
<%= theming.input :copyright_link_url %>
|
37
|
+
<%= theming.input :privacy_link_url %>
|
37
38
|
|
38
39
|
<% account_config.admin_form_inputs.find_all_for(:theming).each do |form_input| %>
|
39
40
|
<%= form_input.build(theming) %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div id="login">
|
2
|
+
<h2><%= render_or_call_method_or_proc_on(self, active_admin_application.site_title) %> - <%= title t('pageflow.initial_password.title') %></h2>
|
3
|
+
|
4
|
+
<%= devise_error_messages! %>
|
5
|
+
<%= active_admin_form_for(resource, as: resource_name, url: admin_initial_password_path, html: { method: :put }) do |f|
|
6
|
+
f.inputs do
|
7
|
+
f.input :password
|
8
|
+
f.input :password_confirmation
|
9
|
+
f.input :reset_password_token, as: :hidden, input_html: { value: resource.reset_password_token }
|
10
|
+
end
|
11
|
+
f.actions do
|
12
|
+
f.action :submit, label: t('pageflow.initial_password.submit'), button_html: { value: t('pageflow.initial_password.submit') }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
%>
|
16
|
+
</div>
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
<p><%= t('.instruction') %></p>
|
4
4
|
|
5
|
-
<p><%= link_to
|
6
|
-
|
5
|
+
<p><%= link_to edit_admin_initial_password_url(reset_password_token: @password_token),
|
6
|
+
edit_admin_initial_password_url(reset_password_token: @password_token) %></p>
|
7
7
|
|
8
8
|
<p><%= t('.ending') %></p>
|
9
9
|
|
data/config/locales/de.yml
CHANGED
@@ -268,6 +268,7 @@ de:
|
|
268
268
|
home_url: Redirect URL
|
269
269
|
imprint_link_label: Impressum-Link Text
|
270
270
|
imprint_link_url: Impressum-Link URL
|
271
|
+
privacy_link_url: Datenschutz-Link URL
|
271
272
|
theme_name: Theme
|
272
273
|
pageflow/video_file:
|
273
274
|
dimensions: Maße
|
@@ -756,7 +757,6 @@ de:
|
|
756
757
|
no_entries: Keine Beiträge
|
757
758
|
no_members: Keine Benutzer
|
758
759
|
theming_defaults_inline_help: Die folgenden Einstellungen werden als Standard für neue Beiträge des Kontos verwendet. Änderungen wirken sich nicht auf existierende Beiträge aus.
|
759
|
-
widgets_inline_help: DELETED
|
760
760
|
entries:
|
761
761
|
add_folder: Ordner hinzufügen
|
762
762
|
confirm_depublish: Soll der Beitrag wirklich depubliziert werden?
|
@@ -983,6 +983,8 @@ de:
|
|
983
983
|
captions: Ton-Ersatz-Textspur
|
984
984
|
descriptions: Bild-Ersatz-Textspur
|
985
985
|
subtitles: Übersetzungs-Textspur
|
986
|
+
cookie_notice_bar:
|
987
|
+
widget_type_name: Leiste am unteren Seitenrand
|
986
988
|
default_theme:
|
987
989
|
name: Standard
|
988
990
|
delayed_text_fade_in:
|
@@ -1017,6 +1019,8 @@ de:
|
|
1017
1019
|
header: Dies ist ein leerer Pageflow
|
1018
1020
|
intro: Pageflows bestehen aus Kapiteln und Seiten. In der Seitenleiste rechts findest Du die Gliederung.
|
1019
1021
|
outro: Hier siehst Du dann eine Vorschau deines Pageflows.
|
1022
|
+
cookie_notice_bar:
|
1023
|
+
widget_type_info_box_text: Der Hinweis wird beim ersten Öffnen von Beiträgen, die Cookies verwenden, am unteren Seitenrand angezeigt. Cookies werden gesetzt, falls ein Dienst zur Erhebnung von Nutzungsdaten (z.B. Google Analytics) verwendet wird oder wenn die Funktion 'Neue Seiten hervorheben' aktiv ist.
|
1020
1024
|
entries:
|
1021
1025
|
unsupported_browser_hint:
|
1022
1026
|
message: Die Version Ihres Browsers wird vom Pageflow Editor nicht unterstützt. Bitte benutzen Sie einen aktuellen Browser.
|
@@ -1216,10 +1220,6 @@ de:
|
|
1216
1220
|
title: Hilfe
|
1217
1221
|
help_button:
|
1218
1222
|
open_help: Hilfe anzeigen
|
1219
|
-
inputs:
|
1220
|
-
reference:
|
1221
|
-
edit: DELETED
|
1222
|
-
reset: DELETED
|
1223
1223
|
list_blank_slate:
|
1224
1224
|
text: "(Keine Einträge)"
|
1225
1225
|
list_item:
|
@@ -1652,6 +1652,9 @@ de:
|
|
1652
1652
|
image_rights: Bildrechte
|
1653
1653
|
highdef_video_encoding:
|
1654
1654
|
feature_name: Full HD und 4K Videos
|
1655
|
+
initial_password:
|
1656
|
+
submit: Speichern und anmelden
|
1657
|
+
title: Passwort festlegen
|
1655
1658
|
invalid_transition: Der gewünschte Statuswechsel ist nicht erlaubt.
|
1656
1659
|
languages:
|
1657
1660
|
ar: Arabisch
|
@@ -1718,6 +1721,7 @@ de:
|
|
1718
1721
|
ui:
|
1719
1722
|
configuration_editor:
|
1720
1723
|
tabs:
|
1724
|
+
cookie_notice_bar: Cookie Hinweis
|
1721
1725
|
files: Dateien
|
1722
1726
|
general: Allgemein
|
1723
1727
|
links: Verweise
|
@@ -1763,7 +1767,7 @@ de:
|
|
1763
1767
|
|
1764
1768
|
Alle Änderungen sind in der linken Frontend-Ansicht sichtbar, sobald das entsprechende Texteingabefeld im Editierbereich verlassen wurde.
|
1765
1769
|
thumbnail_image_id: Das Thumbnail ersetzt das ansonsten automatisch generierte Vorschaubild in der Navigation und in der Übersicht.
|
1766
|
-
transition: Wähle hier den Effekt, der beim Erreichen dieser
|
1770
|
+
transition: Wähle hier den Effekt, der beim Erreichen dieser Seite benutzt werden soll.
|
1767
1771
|
templates:
|
1768
1772
|
inputs:
|
1769
1773
|
file_input:
|
@@ -1803,6 +1807,8 @@ de:
|
|
1803
1807
|
url_hint: URL muss mit http:// beginnen.
|
1804
1808
|
url_hint_https: URL muss mit http:// oder https:// beginnen.
|
1805
1809
|
unauthorized: Sie sind nicht berechtigt diese Seite anzuzeigen.
|
1810
|
+
unmute_button:
|
1811
|
+
widget_type_name: "'Ton ein'-Button"
|
1806
1812
|
user_mailer:
|
1807
1813
|
invitation:
|
1808
1814
|
ending: Dankeschön und viel Spaß,
|
@@ -1825,6 +1831,8 @@ de:
|
|
1825
1831
|
none: "(Kein)"
|
1826
1832
|
roles:
|
1827
1833
|
analytics: Zählpixel
|
1834
|
+
background_media_control: Hintergrund-Media-Control
|
1835
|
+
cookie_notice: Cookie Hinweis
|
1828
1836
|
mobile_navigation: Mobile Navigation
|
1829
1837
|
navigation: Navigationsleiste
|
1830
1838
|
player_controls: Player Controls
|
data/config/locales/en.yml
CHANGED
@@ -268,6 +268,7 @@ en:
|
|
268
268
|
home_url: Redirect URL
|
269
269
|
imprint_link_label: Legal notice link label
|
270
270
|
imprint_link_url: Legal notice link URL
|
271
|
+
privacy_link_url: Privacy link URL
|
271
272
|
theme_name: Theme
|
272
273
|
pageflow/video_file:
|
273
274
|
dimensions: Dimensions
|
@@ -756,13 +757,12 @@ en:
|
|
756
757
|
no_entries: No Stories
|
757
758
|
no_members: No members
|
758
759
|
theming_defaults_inline_help: The following settings will be used as defaults for new entries in this account. Changes do not affect existing entries.
|
759
|
-
widgets_inline_help: DELETED
|
760
760
|
entries:
|
761
761
|
add_folder: Add folder
|
762
|
-
confirm_depublish:
|
762
|
+
confirm_depublish: Depublish this story?
|
763
763
|
confirm_duplicate: Duplicate this story?
|
764
764
|
confirm_restore: Restore story to the selected version? A snapshot will be created, so that you can roll back later.
|
765
|
-
depublish:
|
765
|
+
depublish: Depublish story
|
766
766
|
duplicate: Copy Story
|
767
767
|
edit: Edit
|
768
768
|
edit_config: Edit configuration
|
@@ -981,6 +981,8 @@ en:
|
|
981
981
|
captions: Audio Replacement Text Track
|
982
982
|
descriptions: Image Description Text Track
|
983
983
|
subtitles: Translation Text Track
|
984
|
+
cookie_notice_bar:
|
985
|
+
widget_type_name: Bar at lower page margin
|
984
986
|
default_theme:
|
985
987
|
name: Default
|
986
988
|
delayed_text_fade_in:
|
@@ -1017,6 +1019,8 @@ en:
|
|
1017
1019
|
header: This is an empty Pageflow
|
1018
1020
|
intro: Each Pageflow consist of chapters and pages. The editor panel to your right shows the outline of your story.
|
1019
1021
|
outro: In this area, a live preview will be shown.
|
1022
|
+
cookie_notice_bar:
|
1023
|
+
widget_type_info_box_text: The notice is displayed when visiting an entry that uses Cookies. Cookies are set, if an analytics integration is active or the 'Emphasize new pages' feature has been enabled.
|
1020
1024
|
entries:
|
1021
1025
|
unsupported_browser_hint:
|
1022
1026
|
message: The Pageflow editor does not support the browser version you are using. We recommend upgrading your browser to the most recent version.
|
@@ -1216,10 +1220,6 @@ en:
|
|
1216
1220
|
title: Help
|
1217
1221
|
help_button:
|
1218
1222
|
open_help: Open help
|
1219
|
-
inputs:
|
1220
|
-
reference:
|
1221
|
-
edit: DELETED
|
1222
|
-
reset: DELETED
|
1223
1223
|
list_blank_slate:
|
1224
1224
|
text: "(No items)"
|
1225
1225
|
list_item:
|
@@ -1559,7 +1559,7 @@ en:
|
|
1559
1559
|
click on a page type on the left column.
|
1560
1560
|
publishing:
|
1561
1561
|
menu_item: Publishing
|
1562
|
-
text: "# Publishing\n\nClick on the „Publish“ button to publish your Pageflow. A\nPageflow can be published at any point in time. \n\nBesides this, there is the possibility to publish Pageflows with \na password protection. Just activate „Protect with password“. \nPageflow uses the name of the account as user name and \nautomatically generates a password, which can be changed afterwords. \n\nAlready published stories can also be protected with a password by \npublishing another version - the other way round works as well. \nFor this please just click on the „Publishing“ button. The following \nsteps will then be shown and explained within the dialog window.\n\nPublished\nPageflows can also be
|
1562
|
+
text: "# Publishing\n\nClick on the „Publish“ button to publish your Pageflow. A\nPageflow can be published at any point in time. \n\nBesides this, there is the possibility to publish Pageflows with \na password protection. Just activate „Protect with password“. \nPageflow uses the name of the account as user name and \nautomatically generates a password, which can be changed afterwords. \n\nAlready published stories can also be protected with a password by \npublishing another version - the other way round works as well. \nFor this please just click on the „Publishing“ button. The following \nsteps will then be shown and explained within the dialog window.\n\nPublished\nPageflows can also be depublished manually at any time. If a\nPageflow has already been published, changes will not be\nshown until you publish your report again. A Pageflow can\nonly be edited by one author at one time.\n\nThumbnail for embedding the Pageflow on external websites\nAfter publishing your Pageflow, the system generates a\nthumbnail, which can be embedded as an iframe to the\nhyperlink of another website. By means of the code, the size\nof the iframe can be adjusted. The minimum size is a height\n150px and a width of 220px."
|
1563
1563
|
storylines:
|
1564
1564
|
menu_item: Storylines
|
1565
1565
|
text: "# Storylines\n\nIn addition to linear narrations from top to bottom, further storylines can be used as non linear excursions to enlarge upon parts of a story. A „Storyline“ consists of chapters and pages, which users scroll through. Distinguishable is the so called main storyline from further subordinate ones. The user starts off in the main storyline and can then navigate to different excursions.\n\nTo create such an excursion from the main storyline use one of the „Link“ pages (Mosaic, Collage, Hotspot) and create the desired connections via „Links“.\n\nBy choosing a „Parent page“ you can determine to which page users return after scrolling the storyline´s last page.\n\nTransitions to subordinate storylines are visually supported by a horizontal animation as the default setting for entering and leaving. \n\nStorylines are created by clicking onto the plus button next to the storyline menu. Thereby a new chapter opens automatically in which further pages can be added. \n\n\n### Parent pages\n\nIn every subordinate storyline a „Parent page“ must be determined to define where users land when scrolling back.\n\nThis target page can be chosen by clicking on the pen symbol next to the storyline menu. When a „Parent page“ is defined, a back button will be shown automatically in the subordinate storyline that will always lead users back to the „Parent page“.\n\n\n### Chapter hierarchy\n\nThe use of more than one „subordinary“ storylines and the definition of „Parent pages“ leads to a chapter hierarchy as known from books:\n\n1. (Main storyline), 1.1. (Superior sub storyline), 1.1.1. (Subordinate sub storyline) and so on.\n\nThis hierarchy can be edited afterwards by clicking on the pen symbol next to the storyline menu. A subordinate storyline can be turned into the main storyline as well.\n\n\n### Scroll successors\n\nIf you want to lead users from a last page of a subordinate chapter (1.1.1) to another position than\nthe „Parent page“ (e.g. to 2.1) you can define a „Scroll successor“. Therefore click on the blue pen symbol within the storyline settings. As an example the following sequence might be imaginable: 1 -> 1.1 -> 1.1.1 -> 2.1\n\nAttention: While editing only the selected chapter will be shown in the sidebar. To switch to the other chapters click onto the storyline menu."
|
@@ -1624,6 +1624,9 @@ en:
|
|
1624
1624
|
image_rights: Credits
|
1625
1625
|
highdef_video_encoding:
|
1626
1626
|
feature_name: Full HD and 4K videos
|
1627
|
+
initial_password:
|
1628
|
+
submit: Save and sign in
|
1629
|
+
title: Set your password
|
1627
1630
|
invalid_transition: Invalid transition
|
1628
1631
|
languages:
|
1629
1632
|
ar: Arabic
|
@@ -1690,6 +1693,7 @@ en:
|
|
1690
1693
|
ui:
|
1691
1694
|
configuration_editor:
|
1692
1695
|
tabs:
|
1696
|
+
cookie_notice_bar: Cookie Notice
|
1693
1697
|
files: Files
|
1694
1698
|
general: General
|
1695
1699
|
links: Links
|
@@ -1778,6 +1782,8 @@ en:
|
|
1778
1782
|
url_hint: 'URL must start with http:// '
|
1779
1783
|
url_hint_https: URL must start with http:// or https://
|
1780
1784
|
unauthorized: You are not authorized to view this page.
|
1785
|
+
unmute_button:
|
1786
|
+
widget_type_name: Unmute button
|
1781
1787
|
user_mailer:
|
1782
1788
|
invitation:
|
1783
1789
|
ending: Thank you and have fun,
|
@@ -1800,6 +1806,8 @@ en:
|
|
1800
1806
|
none: "(none)"
|
1801
1807
|
roles:
|
1802
1808
|
analytics: Tracking
|
1809
|
+
background_media_control: Background media control
|
1810
|
+
cookie_notice: Cookie Notice
|
1803
1811
|
mobile_navigation: Mobile navigation
|
1804
1812
|
navigation: Navigation bar
|
1805
1813
|
player_controls: Player Controls
|
data/config/routes.rb
CHANGED
@@ -30,6 +30,12 @@ Pageflow::Engine.routes.draw do
|
|
30
30
|
resource :edit_lock
|
31
31
|
end
|
32
32
|
|
33
|
+
namespace :admin do
|
34
|
+
devise_scope :user do
|
35
|
+
resource :initial_password, only: [:edit, :update]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
33
39
|
namespace :editor do
|
34
40
|
resources :entries, :only => :index, :shallow => true do
|
35
41
|
get :seed, :on => :member
|
@@ -31,5 +31,13 @@ module Pageflow
|
|
31
31
|
def self.slim_player_controls
|
32
32
|
new('slim_player_controls', ['player_controls'], 'pageflow/widgets/placeholder')
|
33
33
|
end
|
34
|
+
|
35
|
+
def self.cookie_notice_bar
|
36
|
+
Pageflow::React.create_widget_type('cookie_notice_bar', 'cookie_notice')
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.unmute_button
|
40
|
+
Pageflow::React.create_widget_type('unmute_button', 'background_media_control')
|
41
|
+
end
|
34
42
|
end
|
35
43
|
end
|
@@ -5,6 +5,8 @@ module Pageflow
|
|
5
5
|
config.widget_types.register(Pageflow::BuiltInWidgetType.mobile_navigation, default: true)
|
6
6
|
config.widget_types.register(Pageflow::BuiltInWidgetType.slim_player_controls)
|
7
7
|
config.widget_types.register(Pageflow::BuiltInWidgetType.classic_player_controls, default: true)
|
8
|
+
config.widget_types.register(Pageflow::BuiltInWidgetType.cookie_notice_bar)
|
9
|
+
config.widget_types.register(Pageflow::BuiltInWidgetType.unmute_button, default: true)
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
data/lib/pageflow/version.rb
CHANGED
@@ -4,9 +4,9 @@
|
|
4
4
|
* License MIT (c) Zohar Arad 2013
|
5
5
|
*/
|
6
6
|
(function ($win, ns, factory) {
|
7
|
+
"use strict";
|
7
8
|
/*global define */
|
8
9
|
/*global swfobject */
|
9
|
-
"use strict";
|
10
10
|
|
11
11
|
if (typeof (module) !== 'undefined' && module.exports) { // CommonJS
|
12
12
|
module.exports = factory(ns, $win);
|
@@ -134,17 +134,35 @@
|
|
134
134
|
trigger: function (evt) {
|
135
135
|
if (this.channels && this.channels.hasOwnProperty(evt)) {
|
136
136
|
var args = Array.prototype.slice.call(arguments, 1);
|
137
|
-
|
137
|
+
|
138
|
+
// temp array to store all subscribers for the channel evt that must be stored in state
|
139
|
+
var evtSubscribers = [];
|
140
|
+
// temp array to store all subscribers for the channel evt that is being triggered that must be executed.
|
141
|
+
var evtSubscribersForExec = [];
|
142
|
+
|
138
143
|
while(this.channels[evt].length > 0) {
|
139
144
|
var sub = this.channels[evt].shift();
|
140
|
-
|
141
|
-
sub.fn.apply(sub.ctx, args);
|
142
|
-
}
|
145
|
+
|
143
146
|
if ( !sub.once ){
|
144
|
-
|
147
|
+
// for any channel event triggers that are not marked as once only, we need to keep.
|
148
|
+
evtSubscribers.push(sub);
|
145
149
|
}
|
150
|
+
|
151
|
+
// if we have a function with the channel subscription, store in temp array ready to call after all proceeded
|
152
|
+
if (typeof (sub.fn) === 'function') {
|
153
|
+
// every subscriber we've processed will be executed
|
154
|
+
evtSubscribersForExec.push(sub);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
// before we execute any of the subscribers to the trigger, we must make sure keep this.channels[evt] up to date in case any sub fns call off
|
159
|
+
this.channels[evt] = evtSubscribers;
|
160
|
+
|
161
|
+
// run all the event subscribers that need executing
|
162
|
+
while(evtSubscribersForExec.length > 0) {
|
163
|
+
var eventSub = evtSubscribersForExec.shift();
|
164
|
+
eventSub.fn.apply(eventSub.ctx, args);
|
146
165
|
}
|
147
|
-
this.channels[evt] = a;
|
148
166
|
}
|
149
167
|
}
|
150
168
|
};
|
@@ -153,19 +171,21 @@
|
|
153
171
|
/**
|
154
172
|
* Flash embed code string with cross-browser support.
|
155
173
|
*/
|
156
|
-
|
174
|
+
flash_embed_code: function (id, swf_location, ts) {
|
157
175
|
var prefix;
|
158
|
-
var
|
176
|
+
var elemId = ns + id;
|
177
|
+
var s = '<param name="movie" value="' + swf_location + '?playerInstanceNumber=' + id + '&datetime=' + ts + '"/>' +
|
159
178
|
'<param name="wmode" value="transparent"/>' +
|
160
179
|
'<param name="allowscriptaccess" value="always" />' +
|
161
180
|
'</object>';
|
162
181
|
if (ActiveXObject) {
|
163
|
-
prefix = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="1" height="1" id="
|
182
|
+
prefix = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="1" height="1" id="' + elemId + '">';
|
164
183
|
} else {
|
165
|
-
prefix = '<object type="application/x-shockwave-flash" data="
|
184
|
+
prefix = '<object type="application/x-shockwave-flash" data="' + swf_location + '?playerInstanceNumber=' + id + '&datetime=' + ts + '" width="1" height="1" id="' + elemId + '" >';
|
166
185
|
}
|
186
|
+
|
167
187
|
return prefix + s;
|
168
|
-
}
|
188
|
+
},
|
169
189
|
/**
|
170
190
|
* Check if browser supports audio mime type.
|
171
191
|
* @param {String} mime_type audio mime type to check
|
@@ -175,30 +195,36 @@
|
|
175
195
|
var a = document.createElement('audio');
|
176
196
|
var mime_str;
|
177
197
|
switch (mime_type) {
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
}
|
197
|
-
if (mime_str === undefined) {
|
198
|
-
throw new Error('Unspecified Audio Mime Type');
|
199
|
-
} else {
|
200
|
-
return !!a.canPlayType && a.canPlayType(mime_str) !== '';
|
198
|
+
case 'mp3':
|
199
|
+
mime_str = 'audio/mpeg;';
|
200
|
+
break;
|
201
|
+
case 'vorbis':
|
202
|
+
mime_str = 'audio/ogg; codecs="vorbis"';
|
203
|
+
break;
|
204
|
+
case 'opus':
|
205
|
+
mime_str = 'audio/ogg; codecs="opus"';
|
206
|
+
break;
|
207
|
+
case 'webm':
|
208
|
+
mime_str = 'audio/webm; codecs="vorbis"';
|
209
|
+
break;
|
210
|
+
case 'mp4':
|
211
|
+
mime_str = 'audio/mp4; codecs="mp4a.40.5"';
|
212
|
+
break;
|
213
|
+
case 'wav':
|
214
|
+
mime_str = 'audio/wav; codecs="1"';
|
215
|
+
break;
|
201
216
|
}
|
217
|
+
if (mime_str !== undefined) {
|
218
|
+
if (mime_type === 'mp3' && navigator.userAgent.match(/Android/i) && navigator.userAgent.match(/Firefox/i)) {
|
219
|
+
return true;
|
220
|
+
}
|
221
|
+
try {
|
222
|
+
return !!a.canPlayType && a.canPlayType(mime_str) !== '';
|
223
|
+
} catch (e) {
|
224
|
+
return false;
|
225
|
+
}
|
226
|
+
}
|
227
|
+
return false;
|
202
228
|
},
|
203
229
|
/**
|
204
230
|
* Boolean flag indicating whether the browser has Flash installed or not
|
@@ -232,19 +258,17 @@
|
|
232
258
|
document.body.appendChild(d);
|
233
259
|
if(typeof($win.swfobject) === 'object'){
|
234
260
|
var fv = {
|
235
|
-
playerInstance: 'window.'+ ns + '_flash.instances[
|
261
|
+
playerInstance: 'window.'+ ns + '_flash.instances[\''+id+'\']'
|
236
262
|
};
|
237
263
|
var params = {
|
238
264
|
allowscriptaccess: 'always',
|
239
265
|
wmode: 'transparent'
|
240
266
|
};
|
241
|
-
d.innerHTML = '<div id="'+id+'"></div>';
|
242
|
-
swfobject.embedSWF(swf_location + '?ts='+(new Date().getTime() + Math.random()), id, "1", "1", "9.0.0", null, fv, params);
|
267
|
+
d.innerHTML = '<div id="'+ ns + id +'"></div>';
|
268
|
+
swfobject.embedSWF(swf_location + '?ts='+(new Date().getTime() + Math.random()), ns + id, "1", "1", "9.0.0", null, fv, params);
|
243
269
|
} else {
|
244
|
-
var
|
245
|
-
|
246
|
-
flashSource = flashSource.replace(/\$3/g, (new Date().getTime() + Math.random())); // Ensure swf is not pulled from cache
|
247
|
-
d.innerHTML = flashSource;
|
270
|
+
var ts = new Date().getTime() + Math.random(); // Ensure swf is not pulled from cache
|
271
|
+
d.innerHTML = this.flash_embed_code(id, swf_location, ts);
|
248
272
|
}
|
249
273
|
return document.getElementById(id);
|
250
274
|
},
|
@@ -281,7 +305,7 @@
|
|
281
305
|
duration: 0, /** {Float} audio duration (sec) */
|
282
306
|
position: 0, /** {Float} audio position (sec) */
|
283
307
|
load_percent: 0, /** {Float} audio file load percent (%) */
|
284
|
-
seekable:
|
308
|
+
seekable: false, /** {Boolean} is loaded audio seekable */
|
285
309
|
ready: null /** {Boolean} is loaded audio seekable */
|
286
310
|
};
|
287
311
|
|
@@ -291,8 +315,7 @@
|
|
291
315
|
* @type {Object}
|
292
316
|
*/
|
293
317
|
var globalAudio5Flash = $win[ns + '_flash'] = $win[ns + '_flash'] || {
|
294
|
-
instances:
|
295
|
-
count: 0 /** FlashAudioPlayer instance count */
|
318
|
+
instances: [] /** FlashAudioPlayer instance hash */
|
296
319
|
};
|
297
320
|
|
298
321
|
/**
|
@@ -311,9 +334,8 @@
|
|
311
334
|
* @param {String} swf_src path to audio player SWF file
|
312
335
|
*/
|
313
336
|
init: function (swf_src) {
|
314
|
-
globalAudio5Flash.
|
315
|
-
this.id =
|
316
|
-
globalAudio5Flash.instances[this.id] = this;
|
337
|
+
globalAudio5Flash.instances.push(this);
|
338
|
+
this.id = globalAudio5Flash.instances.length - 1;
|
317
339
|
this.embed(swf_src);
|
318
340
|
},
|
319
341
|
/**
|
@@ -327,7 +349,7 @@
|
|
327
349
|
* ExternalInterface callback indicating SWF is ready
|
328
350
|
*/
|
329
351
|
eiReady: function () {
|
330
|
-
this.audio = document.getElementById(this.id);
|
352
|
+
this.audio = document.getElementById(ns + this.id);
|
331
353
|
this.trigger('ready');
|
332
354
|
},
|
333
355
|
/**
|
@@ -363,9 +385,13 @@
|
|
363
385
|
/**
|
364
386
|
* ExternalInterface download progress callback. Fires as long as audio file is downloaded by browser.
|
365
387
|
* @param {Float} percent audio download percent
|
388
|
+
* @param {Float} duration audio total duration (sec)
|
389
|
+
* * @param {Boolean} seekable is audio seekable or not (download or streaming)
|
366
390
|
*/
|
367
|
-
eiProgress: function (percent) {
|
391
|
+
eiProgress: function (percent, duration, seekable) {
|
368
392
|
this.load_percent = percent;
|
393
|
+
this.duration = duration;
|
394
|
+
this.seekable = seekable;
|
369
395
|
this.trigger('progress', percent);
|
370
396
|
},
|
371
397
|
/**
|
@@ -381,6 +407,7 @@
|
|
381
407
|
eiPlay: function () {
|
382
408
|
this.playing = true;
|
383
409
|
this.trigger('play');
|
410
|
+
this.trigger('playing');
|
384
411
|
},
|
385
412
|
/**
|
386
413
|
* ExternalInterface audio pause callback. Fires when audio is paused.
|
@@ -393,7 +420,7 @@
|
|
393
420
|
* ExternalInterface audio ended callback. Fires when audio playback ended.
|
394
421
|
*/
|
395
422
|
eiEnded: function () {
|
396
|
-
this.
|
423
|
+
this.pause();
|
397
424
|
this.trigger('ended');
|
398
425
|
},
|
399
426
|
/**
|
@@ -459,6 +486,24 @@
|
|
459
486
|
this.audio.seekTo(position);
|
460
487
|
this.position = position;
|
461
488
|
} catch (e) {}
|
489
|
+
},
|
490
|
+
/**
|
491
|
+
* This feature was not implemented for Flash
|
492
|
+
*/
|
493
|
+
rate: function () {
|
494
|
+
// Not implemented
|
495
|
+
},
|
496
|
+
/**
|
497
|
+
* Destroy audio object and remove from DOM
|
498
|
+
*/
|
499
|
+
destroyAudio: function() {
|
500
|
+
if(this.audio){
|
501
|
+
this.pause();
|
502
|
+
this.audio.parentNode.removeChild(this.audio);
|
503
|
+
delete globalAudio5Flash.instances[this.id];
|
504
|
+
globalAudio5Flash.instances.splice(this.id, 1);
|
505
|
+
delete this.audio;
|
506
|
+
}
|
462
507
|
}
|
463
508
|
};
|
464
509
|
|
@@ -475,52 +520,91 @@
|
|
475
520
|
/**
|
476
521
|
* Initialize the player instance
|
477
522
|
*/
|
478
|
-
init: function (
|
479
|
-
this.
|
523
|
+
init: function () {
|
524
|
+
this._rate = 1;
|
480
525
|
this.trigger('ready');
|
481
526
|
},
|
527
|
+
/**
|
528
|
+
* Create new audio instance
|
529
|
+
*/
|
482
530
|
createAudio: function(){
|
483
|
-
this.audio =
|
531
|
+
this.audio = new Audio();
|
484
532
|
this.audio.autoplay = false;
|
485
533
|
this.audio.preload = 'auto';
|
486
534
|
this.audio.autobuffer = true;
|
535
|
+
this.audio.playbackRate = this._rate;
|
487
536
|
|
488
537
|
this.audio.setAttribute('crossorigin', 'anonymous');
|
489
538
|
|
490
539
|
this.bindEvents();
|
491
540
|
},
|
541
|
+
/**
|
542
|
+
* Destroy current audio instance
|
543
|
+
*/
|
492
544
|
destroyAudio: function(){
|
493
545
|
if(this.audio){
|
546
|
+
this.pause();
|
494
547
|
this.unbindEvents();
|
495
|
-
|
548
|
+
try {
|
549
|
+
this.audio.setAttribute('src', '');
|
550
|
+
} finally {
|
551
|
+
delete this.audio;
|
552
|
+
}
|
496
553
|
}
|
497
554
|
},
|
555
|
+
/**
|
556
|
+
* Sets up audio event listeners once so adding / removing event listeners is always done
|
557
|
+
* on the same callbacks.
|
558
|
+
*/
|
559
|
+
setupEventListeners: function(){
|
560
|
+
this.listeners = {
|
561
|
+
loadstart: this.onLoadStart.bind(this),
|
562
|
+
canplay: this.onLoad.bind(this),
|
563
|
+
loadedmetadata: this.onLoadedMetadata.bind(this),
|
564
|
+
play: this.onPlay.bind(this),
|
565
|
+
playing: this.onPlaying.bind(this),
|
566
|
+
pause: this.onPause.bind(this),
|
567
|
+
ended: this.onEnded.bind(this),
|
568
|
+
error: this.onError.bind(this),
|
569
|
+
timeupdate: this.onTimeUpdate.bind(this),
|
570
|
+
seeking: this.onSeeking.bind(this),
|
571
|
+
seeked: this.onSeeked.bind(this)
|
572
|
+
};
|
573
|
+
},
|
498
574
|
/**
|
499
575
|
* Bind DOM events to Audio object
|
500
576
|
*/
|
501
|
-
bindEvents: function
|
502
|
-
this.
|
503
|
-
|
504
|
-
|
505
|
-
this.audio.addEventListener('
|
506
|
-
this.audio.addEventListener('
|
507
|
-
this.audio.addEventListener('
|
508
|
-
this.audio.addEventListener('
|
509
|
-
this.audio.addEventListener('
|
510
|
-
this.audio.addEventListener('
|
511
|
-
this.audio.addEventListener('
|
512
|
-
|
513
|
-
|
514
|
-
this.audio.
|
515
|
-
this.audio.
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
this.audio.removeEventListener('
|
522
|
-
this.audio.removeEventListener('
|
523
|
-
this.audio.removeEventListener('
|
577
|
+
bindEvents: function() {
|
578
|
+
if(this.listeners === undefined){
|
579
|
+
this.setupEventListeners();
|
580
|
+
}
|
581
|
+
this.audio.addEventListener('loadstart', this.listeners.loadstart, false);
|
582
|
+
this.audio.addEventListener('canplay', this.listeners.canplay, false);
|
583
|
+
this.audio.addEventListener('loadedmetadata', this.listeners.loadedmetadata, false);
|
584
|
+
this.audio.addEventListener('play', this.listeners.play, false);
|
585
|
+
this.audio.addEventListener('playing', this.listeners.playing, false);
|
586
|
+
this.audio.addEventListener('pause', this.listeners.pause, false);
|
587
|
+
this.audio.addEventListener('ended', this.listeners.ended, false);
|
588
|
+
this.audio.addEventListener('error', this.listeners.error, false);
|
589
|
+
this.audio.addEventListener('timeupdate', this.listeners.timeupdate, false);
|
590
|
+
this.audio.addEventListener('seeking', this.listeners.seeking, false);
|
591
|
+
this.audio.addEventListener('seeked', this.listeners.seeked, false);
|
592
|
+
},
|
593
|
+
/**
|
594
|
+
* Unbind DOM events from Audio object
|
595
|
+
*/
|
596
|
+
unbindEvents: function() {
|
597
|
+
this.audio.removeEventListener('loadstart', this.listeners.loadstart);
|
598
|
+
this.audio.removeEventListener('canplay', this.listeners.canplay);
|
599
|
+
this.audio.removeEventListener('loadedmetadata', this.listeners.loadedmetadata);
|
600
|
+
this.audio.removeEventListener('play', this.listeners.play);
|
601
|
+
this.audio.removeEventListener('playing', this.listeners.playing);
|
602
|
+
this.audio.removeEventListener('pause', this.listeners.pause);
|
603
|
+
this.audio.removeEventListener('ended', this.listeners.ended);
|
604
|
+
this.audio.removeEventListener('error', this.listeners.error);
|
605
|
+
this.audio.removeEventListener('timeupdate', this.listeners.timeupdate);
|
606
|
+
this.audio.removeEventListener('seeking', this.listeners.seeking);
|
607
|
+
this.audio.removeEventListener('seeked', this.listeners.seeked);
|
524
608
|
},
|
525
609
|
/**
|
526
610
|
* Audio load start event handler. Triggered when audio starts loading
|
@@ -533,6 +617,9 @@
|
|
533
617
|
* Resets player parameters and starts audio download progress timer.
|
534
618
|
*/
|
535
619
|
onLoad: function () {
|
620
|
+
if(!this.audio){
|
621
|
+
return setTimeout(this.onLoad.bind(this), 100);
|
622
|
+
}
|
536
623
|
this.seekable = this.audio.seekable && this.audio.seekable.length > 0;
|
537
624
|
if (this.seekable) {
|
538
625
|
this.timer = setInterval(this.onProgress.bind(this), 250);
|
@@ -549,12 +636,21 @@
|
|
549
636
|
* Audio play event handler. Triggered when audio starts playing.
|
550
637
|
*/
|
551
638
|
onPlay: function () {
|
639
|
+
this.playing = true;
|
552
640
|
this.trigger('play');
|
553
641
|
},
|
642
|
+
/**
|
643
|
+
* Audio play event handler. Triggered when audio starts playing.
|
644
|
+
*/
|
645
|
+
onPlaying: function () {
|
646
|
+
this.playing = true;
|
647
|
+
this.trigger('playing');
|
648
|
+
},
|
554
649
|
/**
|
555
650
|
* Audio pause event handler. Triggered when audio is paused.
|
556
651
|
*/
|
557
652
|
onPause: function () {
|
653
|
+
this.playing = false;
|
558
654
|
this.trigger('pause');
|
559
655
|
},
|
560
656
|
/**
|
@@ -568,9 +664,11 @@
|
|
568
664
|
* Audio timeupdate event handler. Triggered as long as playhead position is updated (audio is being played).
|
569
665
|
*/
|
570
666
|
onTimeUpdate: function () {
|
571
|
-
if (this.audio
|
572
|
-
|
573
|
-
|
667
|
+
if (this.audio && this.playing) {
|
668
|
+
try{
|
669
|
+
this.position = this.audio.currentTime;
|
670
|
+
this.duration = this.audio.duration === Infinity ? null : this.audio.duration;
|
671
|
+
} catch (e){}
|
574
672
|
this.trigger('timeupdate', this.position, this.duration);
|
575
673
|
}
|
576
674
|
},
|
@@ -580,8 +678,9 @@
|
|
580
678
|
* Cancelled when audio has fully download or when a new audio file has been loaded to the player.
|
581
679
|
*/
|
582
680
|
onProgress: function () {
|
583
|
-
if (this.audio.buffered !== null) {
|
584
|
-
this.
|
681
|
+
if (this.audio && this.audio.buffered !== null && this.audio.buffered.length) {
|
682
|
+
this.duration = this.audio.duration === Infinity ? null : this.audio.duration;
|
683
|
+
this.load_percent = parseInt(((this.audio.buffered.end(this.audio.buffered.length - 1) / this.duration) * 100), 10);
|
585
684
|
this.trigger('progress', this.load_percent);
|
586
685
|
if (this.load_percent >= 100) {
|
587
686
|
this.clearLoadProgress();
|
@@ -632,8 +731,11 @@
|
|
632
731
|
*/
|
633
732
|
load: function (url) {
|
634
733
|
this.reset();
|
635
|
-
this.
|
636
|
-
this.
|
734
|
+
this.trigger('pause');
|
735
|
+
//this.destroyAudio();
|
736
|
+
if(this.audio === undefined){
|
737
|
+
this.createAudio();
|
738
|
+
}
|
637
739
|
this.audio.setAttribute('src', url);
|
638
740
|
this.audio.load();
|
639
741
|
},
|
@@ -641,15 +743,19 @@
|
|
641
743
|
* Play audio
|
642
744
|
*/
|
643
745
|
play: function () {
|
644
|
-
this.
|
645
|
-
|
746
|
+
if(this.audio) {
|
747
|
+
var playPromise = this.audio.play();
|
748
|
+
this.audio.playbackRate = this._rate;
|
749
|
+
return playPromise;
|
750
|
+
}
|
646
751
|
},
|
647
752
|
/**
|
648
753
|
* Pause audio
|
649
754
|
*/
|
650
755
|
pause: function () {
|
651
|
-
this.
|
652
|
-
|
756
|
+
if(this.audio) {
|
757
|
+
this.audio.pause();
|
758
|
+
}
|
653
759
|
},
|
654
760
|
/**
|
655
761
|
* Get / Set audio volume
|
@@ -676,10 +782,23 @@
|
|
676
782
|
if (playing) {
|
677
783
|
this.play();
|
678
784
|
} else {
|
679
|
-
if (this.audio.buffered !== null) {
|
785
|
+
if (this.audio.buffered !== null && this.audio.buffered.length) {
|
680
786
|
this.trigger('timeupdate', this.position, this.duration);
|
681
787
|
}
|
682
788
|
}
|
789
|
+
},
|
790
|
+
/**
|
791
|
+
* Define the playback rate
|
792
|
+
* @param {Float} v playback rate value to be set
|
793
|
+
*/
|
794
|
+
rate: function (v) {
|
795
|
+
if (v === undefined || isNaN(parseFloat(v))) {
|
796
|
+
return this._rate;
|
797
|
+
}
|
798
|
+
this._rate = v;
|
799
|
+
if (this.audio) {
|
800
|
+
this.audio.playbackRate = v;
|
801
|
+
}
|
683
802
|
}
|
684
803
|
};
|
685
804
|
|
@@ -748,7 +867,7 @@
|
|
748
867
|
if (this.settings.use_flash) {
|
749
868
|
this.audio.init(s.swf_path);
|
750
869
|
} else {
|
751
|
-
this.audio.init(
|
870
|
+
this.audio.init();
|
752
871
|
}
|
753
872
|
},
|
754
873
|
/**
|
@@ -757,27 +876,35 @@
|
|
757
876
|
* @return {FlashAudioPlayer,HTML5AudioPlayer} audio player instance
|
758
877
|
*/
|
759
878
|
getPlayer: function () {
|
760
|
-
var i, l, player;
|
761
|
-
|
762
|
-
|
763
|
-
if (Audio5js.can_play(codec)) {
|
764
|
-
player = new HTML5AudioPlayer();
|
765
|
-
this.settings.use_flash = false;
|
766
|
-
this.settings.player = {
|
767
|
-
engine: 'html',
|
768
|
-
codec: codec
|
769
|
-
};
|
770
|
-
break;
|
771
|
-
}
|
772
|
-
}
|
773
|
-
if (player === undefined) {
|
774
|
-
// here we double check for mp3 support instead of defaulting to Flash in case user overrode the settings.codecs array with an empty array.
|
775
|
-
this.settings.use_flash = !Audio5js.can_play('mp3');
|
776
|
-
player = this.settings.use_flash ? new FlashAudioPlayer() : new HTML5AudioPlayer();
|
879
|
+
var i, l, player, codec;
|
880
|
+
if(this.settings.use_flash){
|
881
|
+
player = new FlashAudioPlayer();
|
777
882
|
this.settings.player = {
|
778
|
-
engine:
|
883
|
+
engine: 'flash',
|
779
884
|
codec: 'mp3'
|
780
885
|
};
|
886
|
+
} else {
|
887
|
+
for (i = 0, l = this.settings.codecs.length; i < l; i++) {
|
888
|
+
codec = this.settings.codecs[i];
|
889
|
+
if (Audio5js.can_play(codec)) {
|
890
|
+
player = new HTML5AudioPlayer();
|
891
|
+
this.settings.use_flash = false;
|
892
|
+
this.settings.player = {
|
893
|
+
engine: 'html',
|
894
|
+
codec: codec
|
895
|
+
};
|
896
|
+
break;
|
897
|
+
}
|
898
|
+
}
|
899
|
+
if (player === undefined) {
|
900
|
+
// here we double check for mp3 support instead of defaulting to Flash in case user overrode the settings.codecs array with an empty array.
|
901
|
+
this.settings.use_flash = !Audio5js.can_play('mp3');
|
902
|
+
player = this.settings.use_flash ? new FlashAudioPlayer() : new HTML5AudioPlayer();
|
903
|
+
this.settings.player = {
|
904
|
+
engine: (this.settings.use_flash ? 'flash' : 'html'),
|
905
|
+
codec: 'mp3'
|
906
|
+
};
|
907
|
+
}
|
781
908
|
}
|
782
909
|
return player;
|
783
910
|
},
|
@@ -798,18 +925,36 @@
|
|
798
925
|
this.audio.on('seeking', this.onSeeking, this);
|
799
926
|
this.audio.on('seeked', this.onSeeked, this);
|
800
927
|
},
|
928
|
+
/**
|
929
|
+
* Bind events from audio object to internal callbacks
|
930
|
+
*/
|
931
|
+
unbindAudioEvents: function () {
|
932
|
+
this.audio.off('ready', this.onReady);
|
933
|
+
this.audio.off('loadstart', this.onLoadStart);
|
934
|
+
this.audio.off('loadedmetadata', this.onLoadedMetadata);
|
935
|
+
this.audio.off('play', this.onPlay);
|
936
|
+
this.audio.off('pause', this.onPause);
|
937
|
+
this.audio.off('ended', this.onEnded);
|
938
|
+
this.audio.off('canplay', this.onCanPlay);
|
939
|
+
this.audio.off('timeupdate', this.onTimeUpdate);
|
940
|
+
this.audio.off('progress', this.onProgress);
|
941
|
+
this.audio.off('error', this.onError);
|
942
|
+
this.audio.off('seeking', this.onSeeking);
|
943
|
+
this.audio.off('seeked', this.onSeeked);
|
944
|
+
},
|
801
945
|
/**
|
802
946
|
* Load audio from URL
|
803
947
|
* @param {String} url URL of audio to load
|
804
948
|
*/
|
805
949
|
load: function (url) {
|
950
|
+
var that = this;
|
806
951
|
var f = function(u){
|
807
|
-
|
808
|
-
|
809
|
-
}
|
952
|
+
that.audio.load(u);
|
953
|
+
that.trigger('load');
|
954
|
+
};
|
810
955
|
|
811
956
|
if(this.ready){
|
812
|
-
f();
|
957
|
+
f(url);
|
813
958
|
} else {
|
814
959
|
this.on('ready', f);
|
815
960
|
}
|
@@ -819,8 +964,7 @@
|
|
819
964
|
*/
|
820
965
|
play: function () {
|
821
966
|
if(!this.playing){
|
822
|
-
this.
|
823
|
-
this.audio.play();
|
967
|
+
return this.audio.play();
|
824
968
|
}
|
825
969
|
},
|
826
970
|
/**
|
@@ -828,7 +972,6 @@
|
|
828
972
|
*/
|
829
973
|
pause: function () {
|
830
974
|
if(this.playing){
|
831
|
-
this.playing = false;
|
832
975
|
this.audio.pause();
|
833
976
|
}
|
834
977
|
},
|
@@ -859,6 +1002,20 @@
|
|
859
1002
|
this.audio.seek(position);
|
860
1003
|
this.position = position;
|
861
1004
|
},
|
1005
|
+
/**
|
1006
|
+
* Define the playback rate
|
1007
|
+
* @param {Float} value playback rate value to be set
|
1008
|
+
*/
|
1009
|
+
rate: function (value) {
|
1010
|
+
return this.audio.rate(value);
|
1011
|
+
},
|
1012
|
+
/**
|
1013
|
+
* Destroy audio object and remove from DOM
|
1014
|
+
*/
|
1015
|
+
destroy: function() {
|
1016
|
+
this.unbindAudioEvents();
|
1017
|
+
this.audio.destroyAudio();
|
1018
|
+
},
|
862
1019
|
/**
|
863
1020
|
* Callback for audio ready event. Indicates audio is ready for playback.
|
864
1021
|
* Looks for ready callback in settings object and invokes it in the context of player instance
|
@@ -886,27 +1043,22 @@
|
|
886
1043
|
* Audio play event handler
|
887
1044
|
*/
|
888
1045
|
onPlay: function () {
|
1046
|
+
this.playing = true;
|
889
1047
|
this.trigger('play');
|
890
1048
|
},
|
891
1049
|
/**
|
892
1050
|
* Audio pause event handler
|
893
1051
|
*/
|
894
1052
|
onPause: function () {
|
1053
|
+
this.playing = false;
|
895
1054
|
this.trigger('pause');
|
896
1055
|
},
|
897
1056
|
/**
|
898
1057
|
* Playback end event handler
|
899
1058
|
*/
|
900
1059
|
onEnded: function () {
|
901
|
-
|
902
|
-
|
903
|
-
if (this.settings.loop && wasPlaying) {
|
904
|
-
this.audio.play();
|
905
|
-
}
|
906
|
-
else {
|
907
|
-
this.playing = false;
|
908
|
-
this.trigger('ended');
|
909
|
-
}
|
1060
|
+
this.playing = false;
|
1061
|
+
this.trigger('ended');
|
910
1062
|
},
|
911
1063
|
/**
|
912
1064
|
* Audio error event handler
|
@@ -944,9 +1096,7 @@
|
|
944
1096
|
*/
|
945
1097
|
onTimeUpdate: function (position, duration) {
|
946
1098
|
this.position = this.settings.format_time ? util.formatTime(position) : position;
|
947
|
-
|
948
|
-
this.duration = this.settings.format_time && duration !== null ? util.formatTime(duration) : duration;
|
949
|
-
}
|
1099
|
+
this.duration = this.settings.format_time && duration !== null ? util.formatTime(duration) : duration;
|
950
1100
|
this.trigger('timeupdate', this.position, this.duration);
|
951
1101
|
},
|
952
1102
|
/**
|
@@ -954,6 +1104,7 @@
|
|
954
1104
|
* @param {Float} loaded audio download percent
|
955
1105
|
*/
|
956
1106
|
onProgress: function (loaded) {
|
1107
|
+
this.duration = this.audio.duration;
|
957
1108
|
this.load_percent = loaded;
|
958
1109
|
this.trigger('progress', loaded);
|
959
1110
|
}
|
@@ -964,4 +1115,4 @@
|
|
964
1115
|
|
965
1116
|
return Audio5js;
|
966
1117
|
|
967
|
-
}));
|
1118
|
+
}));
|