From 8732ff010d24e7df678dcbcd1f27c1a3dc930f7a Mon Sep 17 00:00:00 2001 From: Xavier Mouton-Dubosc Date: Wed, 19 Jun 2019 12:51:07 +0100 Subject: [PATCH 01/24] Missing tests in API related #77 --- tests/tests-api.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/tests-api.js b/tests/tests-api.js index 1ad4bae5..04acb2c2 100644 --- a/tests/tests-api.js +++ b/tests/tests-api.js @@ -41,6 +41,10 @@ window.addEventListener('load', function() { // those public values are assumed to have a constant name let expected = { 'keymove' : 5, + 'alternate_delay' : 500, + 'fast_factor' : 4, + 'repeat_delay' : 400, + 'repeat_factor' : 100, 'only_play_one_audiotag' : true, 'current_audiotag_playing' : null, 'global_controller' : null, @@ -101,7 +105,15 @@ window.addEventListener('load', function() { 'hide_throbber_later', 'show_interface', 'build_chapters', - 'build_playlist' + 'build_playlist', + 'add_plane', + 'remove_plane', + 'add_point', + 'remove_point', + 'clear_plane', + 'redraw_all_planes', + 'highlight_point', + 'remove_highlights_points' ]; for(let name of expected) { assert.equal(typeof cpuaudio_tag.CPU[name] , 'function', `CpuAudioElement.CPU.${name} method is still a function`); From c6a5839e879c6b2066509bef0ac4fc3041af5751 Mon Sep 17 00:00:00 2001 From: Xavier Mouton-Dubosc Date: Thu, 4 Jul 2019 22:27:10 +0100 Subject: [PATCH 02/24] Code converted back to tabs, this is a real accessibility issue Read this : https://www.reddit.com/r/javascript/comments/c8drjo/nobody_talks_about_the_real_reason_to_use_tabs/ --- RELEASE.md | 12 +- iframe_for_dimension.html | 20 +- live_config.html | 734 ++++----- make.sh | 134 +- src/10_i18n.js | 134 +- src/11_utils.js | 60 +- src/20_convert.js | 290 ++-- src/30_trigger.js | 1126 ++++++------- src/40_document_cpu.js | 572 +++---- src/45_element_cpu.js | 2554 ++++++++++++++--------------- src/50_media_element_extension.js | 24 +- src/70_cpu_controller.class.js | 118 +- src/71_cpu_audio.class.js | 78 +- src/90_main.js | 28 +- src/global.css | 58 +- src/scoped.css | 580 +++---- tests/tests-api.js | 22 +- 17 files changed, 3270 insertions(+), 3274 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index cbe83f4e..dc065b69 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -RELEASE NOTES Version 6.1 +RELEASE NOTES Version 6.2 ========================= New features @@ -10,17 +10,13 @@ Nothing Corrections ----------- - * Strange behaviour with `nth-child` into CSS prevented 3rd `` to blink correctly - * Optimized CSS styles - * Waiting line with a more convenient graphic glitch [#61](#61) - * Better, but more verbose, svg icon for share, via [Icons8](https://icons8.com/icons/set/share) - * Removing again poster in chapter editor (parameters are space separated instead of previously comma separated) - +Nothing + Back-end -------- -Nothing + * Code converted back to tabs, [this is a real accessibility issue](https://www.reddit.com/r/javascript/comments/c8drjo/nobody_talks_about_the_real_reason_to_use_tabs/) Making of diff --git a/iframe_for_dimension.html b/iframe_for_dimension.html index 437fa2c7..cb51bd23 100644 --- a/iframe_for_dimension.html +++ b/iframe_for_dimension.html @@ -1,17 +1,17 @@ - - - - + + + + - + title="Constrained size" + poster="https://dascritch.net/vrac/.blog2/entendu/.1404-SambaResille_m.jpg" + canonical="https://dascritch.net/post/2014/04/08/Au-Carnaval-avec-Samba-R%C3%A9sille"> + \ No newline at end of file diff --git a/live_config.html b/live_config.html index aff0bf6d..2defcaf7 100644 --- a/live_config.html +++ b/live_config.html @@ -3,118 +3,118 @@
- - + title="Choose an URL for source and give it a title"> + +

- Configure html - Generated html - Configure css - Generated css -   - - + Configure html + Generated html + Configure css + Generated css +   + +

- - - - + + + +
- - - - - - - - -

- Hide - - - - - - -

+ + + + + + + + +

+ Hide + + + + + + +

@@ -124,7 +124,7 @@
- Paste this HTML code where you want the player in your page + Paste this HTML code where you want the player in your page
 
@@ -134,86 +134,86 @@
- - + +
- - - - - - - + + + + + + +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Breakpointswidersmaller
Time indicator width
Font size
Font small size
Height and width of the square buttons
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Breakpointswidersmaller
Time indicator width
Font size
Font small size
Height and width of the square buttons
@@ -224,7 +224,7 @@
- Copy and paste this CSS code into your stylesheet. Or write it : + Copy and paste this CSS code into your stylesheet. Or write it :
@@ -238,230 +238,230 @@ function configurator_html(event) { - let cpu_audio_attributes = ''; - let audio_sources = ''; - let has_one_source = false; - - function esc(chain) { - return chain.replace('&','&').replace('"','"').replace('<','<').replace('>','>'); - } - - function adjust_attributes_cpu_audio() { - for (let attr of ['mode', 'title', 'poster', 'waveform', 'canonical', 'duration', 'twitter']) { - let value = form_html.querySelector(`[name="meta_${attr}"]`).value; - if (value) { - cpu_audio_attributes += ` ${attr}="${esc(value)}"`; - } - } - values_hide = [] - for (let checkbox of Array.from(document.querySelectorAll('[name="meta_hide[]"]'))) { - if (checkbox.checked) { - values_hide.push(checkbox.value); - } - } - if (values_hide.length > 0) { - cpu_audio_attributes += ` hide="${values_hide.join(' ')}"`; - } - } - function adjust_audio_sources() { - for (let attr of ['1', '2', '3', 'vtt']) { - let value = form_html.querySelector(`[name="source_${attr}"]`).value; - if (value) { - if (attr === 'vtt') { - audio_sources += `\n `; - } else { - let kind = form_html.querySelector(`[for="source_${attr}"] select`); - has_one_source = true; - audio_sources += `\n `; - } - } - } - } - - adjust_attributes_cpu_audio(); - adjust_audio_sources(); - - if (!has_one_source) { - // ajouter les erreurs - } - - let audio_attribs = ''; - if (cpu_audio_attributes.indexOf(' duration="') > -1) { - audio_attribs = ' preload="none"'; - } - - let code = ` - + let cpu_audio_attributes = ''; + let audio_sources = ''; + let has_one_source = false; + + function esc(chain) { + return chain.replace('&','&').replace('"','"').replace('<','<').replace('>','>'); + } + + function adjust_attributes_cpu_audio() { + for (let attr of ['mode', 'title', 'poster', 'waveform', 'canonical', 'duration', 'twitter']) { + let value = form_html.querySelector(`[name="meta_${attr}"]`).value; + if (value) { + cpu_audio_attributes += ` ${attr}="${esc(value)}"`; + } + } + values_hide = [] + for (let checkbox of Array.from(document.querySelectorAll('[name="meta_hide[]"]'))) { + if (checkbox.checked) { + values_hide.push(checkbox.value); + } + } + if (values_hide.length > 0) { + cpu_audio_attributes += ` hide="${values_hide.join(' ')}"`; + } + } + function adjust_audio_sources() { + for (let attr of ['1', '2', '3', 'vtt']) { + let value = form_html.querySelector(`[name="source_${attr}"]`).value; + if (value) { + if (attr === 'vtt') { + audio_sources += `\n `; + } else { + let kind = form_html.querySelector(`[for="source_${attr}"] select`); + has_one_source = true; + audio_sources += `\n `; + } + } + } + } + + adjust_attributes_cpu_audio(); + adjust_audio_sources(); + + if (!has_one_source) { + // ajouter les erreurs + } + + let audio_attribs = ''; + if (cpu_audio_attributes.indexOf(' duration="') > -1) { + audio_attribs = ' preload="none"'; + } + + let code = ` + `; - document.getElementById('demo').innerHTML = code; - document.getElementById('code').innerText = code; - cpu_audio = document.querySelector('cpu-audio'); - - if (event) { - event.preventDefault(); - } + document.getElementById('demo').innerHTML = code; + document.getElementById('code').innerText = code; + cpu_audio = document.querySelector('cpu-audio'); + + if (event) { + event.preventDefault(); + } } function configurator_css(event) { - function input_changed(element) { - return element.value !== element.attributes.value.value; - } - - let css = ''; - let attrs = ['font-family', 'inner-shadow' ,'color-transitions','background-transitions' ]; - for (let key of ['','error-','playing-','popup-']) { - for (let attr of ['color','background']) { - attrs.push(`${key}${attr}`); - } - } - - for (let attr of attrs) { - let element = form_css.querySelector(`[name="css_${attr}"]`); - let value = element.value; - if ((input_changed(element)) && (value !== '')) { - - // duration need unit - if (['color-transitions', 'background-transitions'].indexOf(attr)>-1) { - value = value +'s'; - } - - css += `\n\t--cpu-${attr} : ${value};`; - } - } - - if (css !== '') { - css = `body {\n${css}\n}`; - } - - for (let level of ['0', '1', '2']) { - let rules_breakpoint = ''; - for(let attr of ['elapse-width', 'font-size', 'font-small-size', 'height']) { - let element = form_css.querySelector(`[name="css_${attr}[${level}]"]`); - let value = element.value; - if ((input_changed(element)) && (value !== '')) { - rules_breakpoint += `\n\t\t--cpu-${attr} : ${value};`; - } - } - - if (rules_breakpoint !== '') { - if (level === '0') { - css += `\n\nbody, #interface {\n${rules_breakpoint}\n}`; - } else { - let max_width = form_css.querySelector(`[name="css_breakpoint[${level}]"]`).value; - if (max_width !== '') { - css += `\n\n@media (max-width: ${max_width}) , @element #interface and (max-width: ${max_width}) {\n\tbody, #interface {${rules_breakpoint}\n\t}\n} `; - } - } - } - } - - document.getElementById('style').innerHTML = css; - if (event) { - event.preventDefault(); - } + function input_changed(element) { + return element.value !== element.attributes.value.value; + } + + let css = ''; + let attrs = ['font-family', 'inner-shadow' ,'color-transitions','background-transitions' ]; + for (let key of ['','error-','playing-','popup-']) { + for (let attr of ['color','background']) { + attrs.push(`${key}${attr}`); + } + } + + for (let attr of attrs) { + let element = form_css.querySelector(`[name="css_${attr}"]`); + let value = element.value; + if ((input_changed(element)) && (value !== '')) { + + // duration need unit + if (['color-transitions', 'background-transitions'].indexOf(attr)>-1) { + value = value +'s'; + } + + css += `\n\t--cpu-${attr} : ${value};`; + } + } + + if (css !== '') { + css = `body {\n${css}\n}`; + } + + for (let level of ['0', '1', '2']) { + let rules_breakpoint = ''; + for(let attr of ['elapse-width', 'font-size', 'font-small-size', 'height']) { + let element = form_css.querySelector(`[name="css_${attr}[${level}]"]`); + let value = element.value; + if ((input_changed(element)) && (value !== '')) { + rules_breakpoint += `\n\t\t--cpu-${attr} : ${value};`; + } + } + + if (rules_breakpoint !== '') { + if (level === '0') { + css += `\n\nbody, #interface {\n${rules_breakpoint}\n}`; + } else { + let max_width = form_css.querySelector(`[name="css_breakpoint[${level}]"]`).value; + if (max_width !== '') { + css += `\n\n@media (max-width: ${max_width}) , @element #interface and (max-width: ${max_width}) {\n\tbody, #interface {${rules_breakpoint}\n\t}\n} `; + } + } + } + } + + document.getElementById('style').innerHTML = css; + if (event) { + event.preventDefault(); + } } function noop(event) { - document.location.hash = '#' + event.target.action.split('#')[1]; - event.preventDefault(); + document.location.hash = '#' + event.target.action.split('#')[1]; + event.preventDefault(); } function reset(event) { - let event_change = new Event('change'); - setTimeout( function(){ event.target.dispatchEvent(event_change); }, 100); + let event_change = new Event('change'); + setTimeout( function(){ event.target.dispatchEvent(event_change); }, 100); } document.addEventListener('DOMContentLoaded', function(){ - for (let event of ['input', 'change' ]) { - form_html.addEventListener(event, configurator_html); - form_css.addEventListener(event, configurator_css); - } - form_html.addEventListener('reset', reset); - form_css.addEventListener('reset', reset); - form_html.addEventListener('submit', noop); - form_css.addEventListener('submit', noop); - configurator_html(); - document.location.hash = '#'+form_html.id; - - document.querySelector('#do_waiting').addEventListener('click', function() { - cpu_audio.CPU.audiotag.pause(); - cpu_audio.CPU.set_act_container('loading'); - }); - let in_error = false; - document.querySelector('#do_error').addEventListener('click', function() { - if (!in_error) { - cpu_audio.CPU.show_interface('error'); - cpu_audio.CPU.elements.pageerror.innerText = 'This is an error message.'; - in_error = true; - } else { - cpu_audio.CPU.show_interface('main'); - cpu_audio.CPU.elements.pageerror.innerText = ''; - in_error = false; - } - }); + for (let event of ['input', 'change' ]) { + form_html.addEventListener(event, configurator_html); + form_css.addEventListener(event, configurator_css); + } + form_html.addEventListener('reset', reset); + form_css.addEventListener('reset', reset); + form_html.addEventListener('submit', noop); + form_css.addEventListener('submit', noop); + configurator_html(); + document.location.hash = '#'+form_html.id; + + document.querySelector('#do_waiting').addEventListener('click', function() { + cpu_audio.CPU.audiotag.pause(); + cpu_audio.CPU.set_act_container('loading'); + }); + let in_error = false; + document.querySelector('#do_error').addEventListener('click', function() { + if (!in_error) { + cpu_audio.CPU.show_interface('error'); + cpu_audio.CPU.elements.pageerror.innerText = 'This is an error message.'; + in_error = true; + } else { + cpu_audio.CPU.show_interface('main'); + cpu_audio.CPU.elements.pageerror.innerText = ''; + in_error = false; + } + }); }, false); diff --git a/make.sh b/make.sh index b1fb9ce3..264aef98 100755 --- a/make.sh +++ b/make.sh @@ -26,63 +26,63 @@ PROJECT_DIR=$(readlink -f $(dirname ${0})) JS_COMPILATION_LEVEL='SIMPLE_OPTIMIZATIONS' while [ '-' == "${1:0:1}" ] ; do - case "${1}" in - -h|--help) - echo "${HELP}" - exit 0 - ;; - --) - shift - break - ;; - *) - echo "Invalid \"${1}\" option. See ${0} --help" - exit 1 - ;; - esac - shift + case "${1}" in + -h|--help) + echo "${HELP}" + exit 0 + ;; + --) + shift + break + ;; + *) + echo "Invalid \"${1}\" option. See ${0} --help" + exit 1 + ;; + esac + shift done DESTINATION=${1} function _clean() { - mkdir -p ${PROJECT_DIR}/tmp - rm ${PROJECT_DIR}/dist/* - rm ${PROJECT_DIR}/tmp/* + mkdir -p ${PROJECT_DIR}/tmp + rm ${PROJECT_DIR}/dist/* + rm ${PROJECT_DIR}/tmp/* } function _remove_spaces() { - from=${1} - to=${2} + from=${1} + to=${2} - cat "${from}" | sed -r 's/\/\*.*\*\// /g' | tr '\n' ' ' | sed -r 's/[\t ]+/ /g' > "${to}" + cat "${from}" | sed -r 's/\/\*.*\*\// /g' | tr '\n' ' ' | sed -r 's/[\t ]+/ /g' > "${to}" } function _build_template() { - for file in 'template.html' 'global.css' 'scoped.css' - do - _remove_spaces "${PROJECT_DIR}/src/${file}" "${PROJECT_DIR}/tmp/${file}" - done - - global_css=$(cat "${PROJECT_DIR}/tmp/global.css") - scoped_css=$(cat "${PROJECT_DIR}/tmp/scoped.css") - template_html=$(cat "${PROJECT_DIR}/tmp/template.html") - - echo "function _insert(){ - let style = document.createElement('style'); - style.innerHTML = \`${global_css}\`; - document.head.appendChild(style); - - let template = document.createElement('template'); - template.id = 'CPU__template'; - template.innerHTML = \`${template_html}\`; - document.head.appendChild(template); - } - if (document.head !== null) { - _insert(); - } else { - document.addEventListener('DOMContentLoaded', _insert, false); - }" > "${PROJECT_DIR}/tmp/insert_template.js" + for file in 'template.html' 'global.css' 'scoped.css' + do + _remove_spaces "${PROJECT_DIR}/src/${file}" "${PROJECT_DIR}/tmp/${file}" + done + + global_css=$(cat "${PROJECT_DIR}/tmp/global.css") + scoped_css=$(cat "${PROJECT_DIR}/tmp/scoped.css") + template_html=$(cat "${PROJECT_DIR}/tmp/template.html") + + echo "function _insert(){ + let style = document.createElement('style'); + style.innerHTML = \`${global_css}\`; + document.head.appendChild(style); + + let template = document.createElement('template'); + template.id = 'CPU__template'; + template.innerHTML = \`${template_html}\`; + document.head.appendChild(template); + } + if (document.head !== null) { + _insert(); + } else { + document.addEventListener('DOMContentLoaded', _insert, false); + }" > "${PROJECT_DIR}/tmp/insert_template.js" } @@ -97,31 +97,31 @@ $(cat ${PROJECT_DIR}/src/license.txt) function _build_component_js() { - component_file_js="./dist/cpu-audio.js" # ${PROJECT_DIR} - - java -jar /usr/share/java/closure-compiler.jar \ - --compilation_level ${JS_COMPILATION_LEVEL} \ - --use_types_for_optimization=true \ - --summary_detail_level=3 \ - --isolation_mode=IIFE \ - --assume_function_wrapper \ - --js ./src/license.txt \ - --js ./src/{00_prologue,10_i18n,../tmp/insert_template,11_utils,20_convert,30_trigger,40_document_cpu,45_element_cpu,50_media_element_extension,70_cpu_controller.class,71_cpu_audio.class,90_main}.js \ - --entry_point "./src/90_main.js" \ - --language_in ECMASCRIPT_2017 \ - --module_resolution BROWSER \ - --js_module_root src --jscomp_off internetExplorerChecks \ - --strict_mode_input \ - --js_output_file "${component_file_js}" \ - --language_out ECMASCRIPT_2017 \ - --create_source_map "${component_file_js}.map" + component_file_js="./dist/cpu-audio.js" # ${PROJECT_DIR} + + java -jar /usr/share/java/closure-compiler.jar \ + --compilation_level ${JS_COMPILATION_LEVEL} \ + --use_types_for_optimization=true \ + --summary_detail_level=3 \ + --isolation_mode=IIFE \ + --assume_function_wrapper \ + --js ./src/license.txt \ + --js ./src/{00_prologue,10_i18n,../tmp/insert_template,11_utils,20_convert,30_trigger,40_document_cpu,45_element_cpu,50_media_element_extension,70_cpu_controller.class,71_cpu_audio.class,90_main}.js \ + --entry_point "./src/90_main.js" \ + --language_in ECMASCRIPT_2017 \ + --module_resolution BROWSER \ + --js_module_root src --jscomp_off internetExplorerChecks \ + --strict_mode_input \ + --js_output_file "${component_file_js}" \ + --language_out ECMASCRIPT_2017 \ + --create_source_map "${component_file_js}.map" } function _copy_to_server() { - if [ '' != "${DESTINATION}" ] ; then - scp ${PROJECT_DIR}/dist/cpu-audio.html ${DESTINATION}/cpu-audio.html - scp ${PROJECT_DIR}/dist/cpu-audio.js ${DESTINATION}/cpu-audio.js - fi + if [ '' != "${DESTINATION}" ] ; then + scp ${PROJECT_DIR}/dist/cpu-audio.html ${DESTINATION}/cpu-audio.html + scp ${PROJECT_DIR}/dist/cpu-audio.js ${DESTINATION}/cpu-audio.js + fi } diff --git a/src/10_i18n.js b/src/10_i18n.js index d65f919f..c7ac7e7f 100644 --- a/src/10_i18n.js +++ b/src/10_i18n.js @@ -1,56 +1,56 @@ const sources_i18n = { - 'fr' : { - 'loading' : 'Chargement en cours…', - 'pause' : 'Pause', - 'play' : 'Lecture', - 'canonical' : 'Lien vers la fiche du sonore', - 'moment' : 'Lien vers ce moment', + 'fr' : { + 'loading' : 'Chargement en cours…', + 'pause' : 'Pause', + 'play' : 'Lecture', + 'canonical' : 'Lien vers la fiche du sonore', + 'moment' : 'Lien vers ce moment', - 'untitled' : '(sans titre)', - 'cover' : 'pochette', - 'more' : 'Actions', - 'share' : 'Partager', - 'twitter' : 'Partager sur Twitter', - 'facebook' : 'Partager sur Facebook', - 'e_mail' : 'Partager par e-mail', - 'download' : 'Télécharger', - 'back' : 'Annuler', + 'untitled' : '(sans titre)', + 'cover' : 'pochette', + 'more' : 'Actions', + 'share' : 'Partager', + 'twitter' : 'Partager sur Twitter', + 'facebook' : 'Partager sur Facebook', + 'e_mail' : 'Partager par e-mail', + 'download' : 'Télécharger', + 'back' : 'Annuler', - 'chapters' : 'Chapitres', - 'playlist' : 'Playlist', + 'chapters' : 'Chapitres', + 'playlist' : 'Playlist', - 'media_err_aborted' : 'Vous avez annulé la lecture.', - 'media_err_network' : 'Une erreur réseau a causé l\'interruption du téléchargement.', - 'media_err_decode' : 'La lecture du sonore a été annulée suite à des problèmes de corruption ou de fonctionnalités non supportés par votre navigateur.', - 'media_err_src_not_supported' : 'Le sonore n\'a pu être chargé, soit à cause de sourcis sur le serveur, le réseau ou parce que le format n\'est pas supporté.', - 'media_err_unknow' : 'Erreur due à une raison inconnue.' - }, - 'en' : { - 'loading' : 'Loading…', - 'pause' : 'Pause', - 'play' : 'Play', - 'canonical' : 'Link to the sound\'s page', - 'moment' : 'Link to this time', + 'media_err_aborted' : 'Vous avez annulé la lecture.', + 'media_err_network' : 'Une erreur réseau a causé l\'interruption du téléchargement.', + 'media_err_decode' : 'La lecture du sonore a été annulée suite à des problèmes de corruption ou de fonctionnalités non supportés par votre navigateur.', + 'media_err_src_not_supported' : 'Le sonore n\'a pu être chargé, soit à cause de sourcis sur le serveur, le réseau ou parce que le format n\'est pas supporté.', + 'media_err_unknow' : 'Erreur due à une raison inconnue.' + }, + 'en' : { + 'loading' : 'Loading…', + 'pause' : 'Pause', + 'play' : 'Play', + 'canonical' : 'Link to the sound\'s page', + 'moment' : 'Link to this time', - 'untitled' : '(untitled)', - 'cover' : 'cover', - 'more' : 'Actions', - 'share' : 'Share', - 'twitter' : 'Share on Twitter', - 'facebook' : 'Share on Facebook', - 'e_mail' : 'Share via e-mail', - 'download' : 'Download', - 'back' : 'Back', + 'untitled' : '(untitled)', + 'cover' : 'cover', + 'more' : 'Actions', + 'share' : 'Share', + 'twitter' : 'Share on Twitter', + 'facebook' : 'Share on Facebook', + 'e_mail' : 'Share via e-mail', + 'download' : 'Download', + 'back' : 'Back', - 'chapters' : 'Chapters', - 'playlist' : 'Playlist', + 'chapters' : 'Chapters', + 'playlist' : 'Playlist', - 'media_err_aborted' : 'You have aborted the play.', - 'media_err_network' : 'A network error broke the download.', - 'media_err_decode' : 'Play was canceled due to file corruption or a not supported function in your browser.', - 'media_err_src_not_supported' : 'The media cannot be downloaded due to server problems, network problems or unsupported by your browser.', - 'media_err_unknow' : 'Error of unknown cause.' - }, + 'media_err_aborted' : 'You have aborted the play.', + 'media_err_network' : 'A network error broke the download.', + 'media_err_decode' : 'Play was canceled due to file corruption or a not supported function in your browser.', + 'media_err_src_not_supported' : 'The media cannot be downloaded due to server problems, network problems or unsupported by your browser.', + 'media_err_unknow' : 'Error of unknown cause.' + }, }; @@ -59,28 +59,28 @@ let prefered_language = document.querySelector('html').lang; if ((!prefered_language.length) || (!(prefered_language in sources_i18n))) { - // Inexisting ? - prefered_language = 'en'; - // We will use english in last resort + // Inexisting ? + prefered_language = 'en'; + // We will use english in last resort - // trying to find the browser preferences - let languages = window.navigator.languages; - languages = (languages !== undefined) ? - languages : - [(navigator.language || navigator.browserLanguage)]; - let added = false; - for (let entry in languages) { - // for each entry - let line = languages[entry]; - if (line.split) { - // we will only look (yes, this is bad) at the first level xx of any locale xx-YY code - let code = line.split('-')[0]; - if ( (!added) && (code in sources_i18n)) { - // we still don't have a locale selected and this one is in our register, we can use it - prefered_language = code; - } - } - } + // trying to find the browser preferences + let languages = window.navigator.languages; + languages = (languages !== undefined) ? + languages : + [(navigator.language || navigator.browserLanguage)]; + let added = false; + for (let entry in languages) { + // for each entry + let line = languages[entry]; + if (line.split) { + // we will only look (yes, this is bad) at the first level xx of any locale xx-YY code + let code = line.split('-')[0]; + if ( (!added) && (code in sources_i18n)) { + // we still don't have a locale selected and this one is in our register, we can use it + prefered_language = code; + } + } + } } const __ = sources_i18n[prefered_language]; \ No newline at end of file diff --git a/src/11_utils.js b/src/11_utils.js index 56950973..74ba5491 100644 --- a/src/11_utils.js +++ b/src/11_utils.js @@ -1,67 +1,67 @@ function onDebug(callback_fx) { - // may be used as a noop(); - if (typeof callback_fx === 'function') { - // this is needed for testing, as we now run in async tests - callback_fx(); - } + // may be used as a noop(); + if (typeof callback_fx === 'function') { + // this is needed for testing, as we now run in async tests + callback_fx(); + } } function querySelector_apply(selector, callback, subtree) { - subtree = subtree === undefined ? document : subtree; - Array.from( - subtree.querySelectorAll(selector) - ).forEach(callback); + subtree = subtree === undefined ? document : subtree; + Array.from( + subtree.querySelectorAll(selector) + ).forEach(callback); } function is_decent_browser_for_webcomponents() { - return window.customElements !== undefined; + return window.customElements !== undefined; } function absolutize_url(url) { - let test_element = document.createElement('a'); - test_element.href = typeof url !== 'string' ? url : url.split('#')[0]; - return test_element.href; + let test_element = document.createElement('a'); + test_element.href = typeof url !== 'string' ? url : url.split('#')[0]; + return test_element.href; } function not_screen_context() { - return !window.matchMedia("screen").matches; + return !window.matchMedia("screen").matches; } function prevent_link_on_same_page(event) { - if (absolutize_url(window.location.href) !== absolutize_url(event.target.href)) { - return ; - } - event.preventDefault(); + if (absolutize_url(window.location.href) !== absolutize_url(event.target.href)) { + return ; + } + event.preventDefault(); } function element_prevent_link_on_same_page(element) { - element.addEventListener('click', prevent_link_on_same_page); + element.addEventListener('click', prevent_link_on_same_page); } function _isEvent(event) { - // is this event really triggered via a native event ? - return event.preventDefault !== undefined; + // is this event really triggered via a native event ? + return event.preventDefault !== undefined; } function escapeHTML(text) { - // will truly escape HTML tags and entities. No hazardous regexes or replaces - let burn_after_reading = document.createElement('span'); - burn_after_reading.innerText = text; - let out = burn_after_reading.innerHTML; - burn_after_reading.remove(); - return out; + // will truly escape HTML tags and entities. No hazardous regexes or replaces + let burn_after_reading = document.createElement('span'); + burn_after_reading.innerText = text; + let out = burn_after_reading.innerHTML; + burn_after_reading.remove(); + return out; } function info(message) { - window.console.info(`${CpuAudioTagName}: `,message); + window.console.info(`${CpuAudioTagName}: `,message); } function warn(message) { - window.console.warn(`${CpuAudioTagName}: `,message); + window.console.warn(`${CpuAudioTagName}: `,message); } function error(message) { - window.console.error(`${CpuAudioTagName}: `,message); + window.console.error(`${CpuAudioTagName}: `,message); } \ No newline at end of file diff --git a/src/20_convert.js b/src/20_convert.js index c555f640..40a9d1ee 100644 --- a/src/20_convert.js +++ b/src/20_convert.js @@ -1,154 +1,154 @@ const convert = { - units : { - 'd' : 86400, - 'h' : 3600, - 'm' : 60, - 's' : 1 - }, - _is_only_numeric : /^\d+$/, - _any_not_numeric : /\D*/g, + units : { + 'd' : 86400, + 'h' : 3600, + 'm' : 60, + 's' : 1 + }, + _is_only_numeric : /^\d+$/, + _any_not_numeric : /\D*/g, - /** - * @brief convert a string empty, with a number, with a colon-coded or an - * human-coded timecode in seconds - * - * @public - * - * @class TimeInSeconds (name) - * @param {string} givenTime The given time - * @return {number} time in seconds - */ - TimeInSeconds : function(givenTime) { - let seconds = 0; - if (givenTime !== '') { - if (convert._is_only_numeric.test(givenTime)) { - seconds = Number(givenTime); - } else { - seconds = (givenTime.indexOf(':') === -1) ? - this.SubunitTimeInSeconds(givenTime) : - this.ColonTimeInSeconds(givenTime) ; - } - } - return seconds; - }, + /** + * @brief convert a string empty, with a number, with a colon-coded or an + * human-coded timecode in seconds + * + * @public + * + * @class TimeInSeconds (name) + * @param {string} givenTime The given time + * @return {number} time in seconds + */ + TimeInSeconds : function(givenTime) { + let seconds = 0; + if (givenTime !== '') { + if (convert._is_only_numeric.test(givenTime)) { + seconds = Number(givenTime); + } else { + seconds = (givenTime.indexOf(':') === -1) ? + this.SubunitTimeInSeconds(givenTime) : + this.ColonTimeInSeconds(givenTime) ; + } + } + return seconds; + }, - /** - * @brief convert a human-coded (`1h2m3s`) time in seconds - * - * @public - * - * @class SubunitTimeInSeconds (name) - * @param {string} givenTime The given time - * @return {number} seconds - */ - SubunitTimeInSeconds : function(givenTime) { - let seconds = 0; - for(let key in convert.units) { - if ( (convert.units.hasOwnProperty(key)) && (givenTime.indexOf(key) !== -1) ) { - let atoms = givenTime.split(key); - seconds += Number(atoms[0].replace(convert._any_not_numeric,'' )) * convert.units[key]; - givenTime = atoms[1]; - } - } - return seconds; - }, + /** + * @brief convert a human-coded (`1h2m3s`) time in seconds + * + * @public + * + * @class SubunitTimeInSeconds (name) + * @param {string} givenTime The given time + * @return {number} seconds + */ + SubunitTimeInSeconds : function(givenTime) { + let seconds = 0; + for(let key in convert.units) { + if ( (convert.units.hasOwnProperty(key)) && (givenTime.indexOf(key) !== -1) ) { + let atoms = givenTime.split(key); + seconds += Number(atoms[0].replace(convert._any_not_numeric,'' )) * convert.units[key]; + givenTime = atoms[1]; + } + } + return seconds; + }, - /** - * @brief convert a colon-coded (`01:02:03`) time in seconds - * - * @public - * - * @class ColonTimeInSeconds (name) - * @param {string} givenTime The given time - * @return {number} { seconds } - */ - ColonTimeInSeconds : function(givenTime) { - let seconds = 0; - let atoms = givenTime.split(':'); - let convert = [1, 60, 3600, 86400]; - for (let pos = 0 ; pos < atoms.length ; pos++) { - seconds += Number(atoms[pos]) * convert[((atoms.length-1) - pos)]; - } - return seconds; - }, + /** + * @brief convert a colon-coded (`01:02:03`) time in seconds + * + * @public + * + * @class ColonTimeInSeconds (name) + * @param {string} givenTime The given time + * @return {number} { seconds } + */ + ColonTimeInSeconds : function(givenTime) { + let seconds = 0; + let atoms = givenTime.split(':'); + let convert = [1, 60, 3600, 86400]; + for (let pos = 0 ; pos < atoms.length ; pos++) { + seconds += Number(atoms[pos]) * convert[((atoms.length-1) - pos)]; + } + return seconds; + }, - /** - * @brief convert a time in seconds in a human-coded time (`1h2m3s`). Zero is `0s`. - * - * @public - * - * @class SecondsInTime (name) - * @param {number} givenSeconds The given seconds - * @return {boolean} { description_of_the_return_value } - */ - SecondsInTime : function(givenSeconds) { - let converted = ''; - let inned = false; - for(let key in convert.units) { - if (convert.units.hasOwnProperty(key)) { - let multiply = convert.units[key]; - if ((givenSeconds >= multiply) || (inned)) { - inned = true; - let digits = Math.floor(givenSeconds / multiply); - converted += digits + key; - givenSeconds -= digits * multiply; - } - } - } - return converted === '' ? '0s' : converted; - }, + /** + * @brief convert a time in seconds in a human-coded time (`1h2m3s`). Zero is `0s`. + * + * @public + * + * @class SecondsInTime (name) + * @param {number} givenSeconds The given seconds + * @return {boolean} { description_of_the_return_value } + */ + SecondsInTime : function(givenSeconds) { + let converted = ''; + let inned = false; + for(let key in convert.units) { + if (convert.units.hasOwnProperty(key)) { + let multiply = convert.units[key]; + if ((givenSeconds >= multiply) || (inned)) { + inned = true; + let digits = Math.floor(givenSeconds / multiply); + converted += digits + key; + givenSeconds -= digits * multiply; + } + } + } + return converted === '' ? '0s' : converted; + }, - /** - * @brief convert a time in seconds in a colon-coded time (`1:02:03s`). Zero - * is `0:00`. - * - * @public - * - * @class SecondsInColonTime (name) - * @param {number} givenSeconds The given seconds - * @return {(boolean|string)} { description_of_the_return_value } - */ - SecondsInColonTime : function(givenSeconds) { - let converted = ''; - let inned = false; - for (let key in convert.units) { - if (convert.units.hasOwnProperty(key)) { - let multiply = convert.units[key]; - if ((givenSeconds >= multiply) || (inned)) { - inned = true; - let digits = Math.floor(givenSeconds / multiply); - converted += (converted === '' ? '' : ':'); - converted += ( ((digits<10) && (converted !== '')) ? '0' : '') + digits ; - givenSeconds -= digits * multiply; - } - } - } - if (converted.length === 1) { - // between 0 and 9 seconds - return '0:0' + converted; - } - if (converted.length === 2) { - // between 10 and 59 seconds - return '0:' + converted; - } - - return converted === '' ? '0:00' : converted; - }, + /** + * @brief convert a time in seconds in a colon-coded time (`1:02:03s`). Zero + * is `0:00`. + * + * @public + * + * @class SecondsInColonTime (name) + * @param {number} givenSeconds The given seconds + * @return {(boolean|string)} { description_of_the_return_value } + */ + SecondsInColonTime : function(givenSeconds) { + let converted = ''; + let inned = false; + for (let key in convert.units) { + if (convert.units.hasOwnProperty(key)) { + let multiply = convert.units[key]; + if ((givenSeconds >= multiply) || (inned)) { + inned = true; + let digits = Math.floor(givenSeconds / multiply); + converted += (converted === '' ? '' : ':'); + converted += ( ((digits<10) && (converted !== '')) ? '0' : '') + digits ; + givenSeconds -= digits * multiply; + } + } + } + if (converted.length === 1) { + // between 0 and 9 seconds + return '0:0' + converted; + } + if (converted.length === 2) { + // between 10 and 59 seconds + return '0:' + converted; + } + + return converted === '' ? '0:00' : converted; + }, - /** - * @brief same as `SecondsInColonTime`, but suited for ``. Zero is `00:00:00`. - * - * @public - * - * @class SecondsInPaddledColonTime (name) - * @param {} givenSeconds The given seconds - * @return {string} { description_of_the_return_value } - */ - SecondsInPaddledColonTime : function(givenSeconds) { - // principaly needed by whom needs a really precise HH:MM:SS format - let colon_time = convert.SecondsInColonTime(givenSeconds); - return '00:00:00'.substr(0, 8 - colon_time.length ) + colon_time; - } + /** + * @brief same as `SecondsInColonTime`, but suited for ``. Zero is `00:00:00`. + * + * @public + * + * @class SecondsInPaddledColonTime (name) + * @param {} givenSeconds The given seconds + * @return {string} { description_of_the_return_value } + */ + SecondsInPaddledColonTime : function(givenSeconds) { + // principaly needed by whom needs a really precise HH:MM:SS format + let colon_time = convert.SecondsInColonTime(givenSeconds); + return '00:00:00'.substr(0, 8 - colon_time.length ) + colon_time; + } } \ No newline at end of file diff --git a/src/30_trigger.js b/src/30_trigger.js index cb30c013..fb53c2c1 100644 --- a/src/30_trigger.js +++ b/src/30_trigger.js @@ -3,568 +3,568 @@ const KEY_RIGHT_ARROW = 39; const trigger = { - _timecode_start : false, - _timecode_end : false, - - // wrongly placed information. Should be in Element.CPU - _remove_timecode_outofborders : function(at) { - if ( ((trigger._timecode_start !== false) && (at < trigger._timecode_start)) - || ((trigger._timecode_end !== false) && (at > trigger._timecode_end)) ) { - trigger._timecode_start = false; - trigger._timecode_end = false; - } - }, - - - /** - * @brief Interprets the hash part of the URL, when loaded or changed - * - * @private - * - * @param {string} hashcode Called hashcode - * @param {function} callback_fx When done, call a function to end the tests - * @return {boolean} understood - */ - hashOrder : function(hashcode, callback_fx) { - - let at_start = true; - if (typeof hashcode !== 'string') { - at_start = 'at_start' in hashcode; - hashcode = location.hash.substr(1); - } - let hash = ''; - let timecode = ''; - let segments = hashcode.split('&'); - let autoplay = false; - - for (let _id in segments) { - let parameter = segments[_id]; - if ((parameter.indexOf('=') === -1) && (hash === '')) { - // should reference to the ID of the element - hash = parameter; - } else { - // should be a key=value parameter - let atoms = parameter.split('='); - let p_key = atoms[0]; - let p_value = atoms[1]; - switch (p_key) { - case 't': - // is a time index - p_value = (p_value !== '') ? p_value : '0'; - timecode = p_value; - // we make autoplay at requested timecode, simplier of the user - autoplay = true; - break; - case 'autoplay': - // is a card from a social network, run now - autoplay = p_value === '1'; - break; - case 'auto_play': - // is a card from a social network, run now - autoplay = p_value === 'true'; - break; - } - } - } - - if ((timecode === '') || ((at_start) && (!autoplay))) { - // this is a normal anchor call. Go back to normal behaviour - onDebug(callback_fx); - return false; - } - - // we may have a begin,end notation - let times = timecode.split(','); - let timecode_start = times[0]; - trigger._timecode_start = convert.TimeInSeconds(timecode_start); - trigger._timecode_end = times.length > 1 ? convert.TimeInSeconds(times[1]) : false; - if (trigger._timecode_end !== false) { - trigger._timecode_end = (trigger._timecode_end > trigger._timecode_start) ? - trigger._timecode_end : - false; - } - - document.CPU.jumpIdAt(hash, timecode_start, callback_fx); - - if (document.CPU.global_controller) { - document.CPU.global_controller.build_playlist(); - } - // scroll to the audio element. Should be reworked, or parametrable - // window.location.hash = `#${hash}`; - return true; - }, - - /** - * Update throbber position when hovering the timeline interface - * - * @param {} event The event - */ - hover : function(event) { - let target = event.target; - let container = document.CPU.find_container(target); - - let target_rect = target.getClientRects()[0]; - let relLeft = target_rect.left; - let ratio = event.offsetX / target.clientWidth; - let seeked_time = ratio * container.audiotag.duration; - - container.show_throbber_at(seeked_time); - }, - - /** - * Hide the throbber when leaving the timeline interface - * - * @param {} event The event - */ - out : function(event) { - let container = document.CPU.find_container(event.target); - container.hide_throbber(); - }, - - /*preview_hover : function(event) { - - },*/ - - /** - * Highlight the playable positions when hovering a marked link - * - * @param {} event The event - */ - preview_container_hover : function(event) { - let target = event.target; - if (!target.id) { - target = target.closest('[id]'); - } - if (target === null) { - return; - } - - let container = document.CPU.find_container(target); - let names = container.get_point_names_from_id(target.id); - container.highlight_point(names[0], names[1]); - }, - - - /** - * Change play position of a audio tag - * - * @param {} event The event - */ - throbble : function(event) { - let at = 0; - let target = event.target; - let container = document.CPU.find_container(target); - let audiotag = container.audiotag; - if (event.at !== undefined) { - at = event.at; - } else { - // normal usage - let ratio = event.offsetX / target.clientWidth; - at = ratio * audiotag.duration; - } - - trigger._remove_timecode_outofborders(at); - - document.CPU.seekElementAt(audiotag, at); - trigger.play(event); - }, - - /** - * Do pause - * - * @param {} event The event - * @param {} audiotag The audiotag - */ - pause : function(event, audiotag) { - if (audiotag === undefined) { - let target = event.target; - audiotag = (target.tagName === 'AUDIO') ? target : document.CPU.find_container(target).audiotag; - } - audiotag.pause(); - document.CPU.current_audiotag_playing = null; - trigger.update({target : audiotag}); - window.localStorage.removeItem(audiotag.currentSrc); - }, - - /** - * Change referenced playing audio, pause the previous one - * - * @param {} event The event - */ - play_once : function(event) { - let audiotag = event.target; - - document.CPU.last_used = audiotag; - - if ( (document.CPU.only_play_one_audiotag) && (document.CPU.current_audiotag_playing) && (!document.CPU.is_audiotag_playing(audiotag)) ) { - trigger.pause(undefined, document.CPU.current_audiotag_playing); - - } - document.CPU.current_audiotag_playing = audiotag; - }, - - /** - * Do play an audio tag - * - * @param {} event The event - * @param {} audiotag The audiotag - */ - play : function(event, audiotag) { - if (audiotag === undefined) { - audiotag = document.CPU.find_container(event.target).audiotag; - } - - if ((document.CPU.global_controller) && (!audiotag.isEqualNode(document.CPU.global_controller.audiotag))) { - let global_controller = document.CPU.global_controller; - global_controller.attach_audiotag_to_controller(audiotag); - global_controller.audiotag = audiotag; - global_controller.show_main(); - global_controller.redraw_all_planes(); - global_controller.build_playlist(); - } - - try { - trigger._remove_timecode_outofborders(audiotag.currentTime); - audiotag.play(); - trigger.update({target : audiotag}); - } catch (e) { - - } - }, - - /** - * Interprets pressed key - * - * @param {} event The event - * @param {number} mult Multiply the keypressed act - */ - key : function(event, mult) { - mult = mult === undefined ? 1 : mult; - let container = document.CPU.find_container(event.target); - let audiotag = container.audiotag; - - function seek_relative(seconds) { - event.at = container.audiotag.currentTime + seconds; - container.show_throbber_at(event.at); - trigger.throbble(event); - container.hide_throbber_later(); - } - - switch (event.keyCode) { - // can't use enter : standard usage - case 27 : // esc - trigger.restart(event); - trigger.pause(undefined, audiotag); - break; - case 32 : // space - audiotag.paused ? - trigger.play(undefined, audiotag) : - trigger.pause(undefined, audiotag); - break; - case 35 : // end - document.CPU.seekElementAt(audiotag, audiotag.duration); - break; - case 36 : // home - trigger.restart(event); - break; - case KEY_LEFT_ARROW : // ← - seek_relative(- (document.CPU.keymove * mult)); - break; - case KEY_RIGHT_ARROW : // → - seek_relative(+ (document.CPU.keymove * mult)); - break; - default: - return ; - } - event.preventDefault(); - }, - - /** - * Interprets keypress on the play/pause button - * - * @param {} event The event - */ - keydownplay : function(event) { - if (event.keyCode !== 13 ) { - return; - } - let container = document.CPU.find_container(event.target); - let audiotag = container.audiotag; - - audiotag.paused ? - trigger.play(undefined, audiotag) : - trigger.pause(undefined, audiotag); - - event.preventDefault(); - }, - - /** - * Pressing restart button, Rewind at start the audio tag - * - * @param {} event The event - */ - restart : function(event) { - let container = document.CPU.find_container(event.target); - document.CPU.seekElementAt(container.audiotag, 0); - }, - /** - * Pressing reward button - * - * @param {} event The event - */ - reward : function(event) { - event.keyCode = KEY_LEFT_ARROW; - trigger.key(event); - }, - /** - * Pressing foward button - * Function associated, see below, DO NOT RENAME - * - * @param {} event The event - */ - foward : function(event) { - event.keyCode = KEY_RIGHT_ARROW; - trigger.key(event); - }, - /** - * Pressing fastreward button - * Function associated, see below, DO NOT RENAME - * - * @param {} event The event - */ - fastreward : function(event) { - event.keyCode = KEY_LEFT_ARROW; - trigger.key(event, document.CPU.fast_factor); - }, - /** - * Pressing fastfoward button - * Function associated, see below, DO NOT RENAME - * - * @param {} event The event - */ - fastfoward : function(event) { - event.keyCode = KEY_RIGHT_ARROW; - trigger.key(event, document.CPU.fast_factor); - }, - - - _hand_on : null, // Repeated event allocation - /* Start handheld navigation button press */ - _press_button : function(event) { - let target = event.target.id ? event.target : event.target.closest('button'); - let acceptable_actions = ['fastreward', 'reward', 'foward', 'fastfoward']; - if ( (!target.id) || (acceptable_actions.indexOf(target.id) === -1)) { - // we have been misleaded - return; - } - // execute the associated function - trigger[target.id](event); - if (trigger._hand_on !== null) { - window.clearTimeout(trigger._hand_on); - } - - let mini_event = { - target : target, - preventDefault : onDebug - }; - trigger._hand_on = window.setTimeout(trigger._repeat_button, document.CPU.repeat_delay, mini_event); - event.preventDefault(); - }, - - /* Repeat during pressing handheld navigation button */ - _repeat_button : function(event) { - // - trigger[event.target.id](event); - // next call : repetition are closest - trigger._hand_on = window.setTimeout(trigger._repeat_button, document.CPU.repeat_factor, event); - }, - - /* Release handheld navigation button */ - _release_button : function(event) { - window.clearTimeout(trigger._hand_on); - trigger._hand_on = null; - event.preventDefault(); - }, - - - /** - * in fine-position handheld interface, changing the time field - * INVALIDED, see https://github.com/dascritch/cpu-audio/issues/63 - * - * @param {} event The event - * - input_time_change : function(event) { - let target = event.target; - let container = document.CPU.find_container(target); - let seconds = convert.ColonTimeInSeconds(target.value); - container.show_throbber_at(seconds); - document.CPU.seekElementAt(container.audiotag, seconds); - }, - */ - - - /** - * Refresh the interface when changing chapter - * - * @param {} event The event - * @param {} element_interface The element interface - */ - cuechange : function(event, element_interface) { - document.body.classList.remove(document.CPU.body_className_playing_cue); - // when the position in a media element goes out of the current - if (element_interface === undefined) { - return; - } - let class_name = 'active-cue'; - let container = document.CPU.find_container(element_interface); - let plane_name = '_chapters'; - container.remove_highlights_points(class_name); - - if (event.target.activeCues.length === 0) { - // too early, we need to keep this case from Chrome - return; - } - - let cue_id = event.target.activeCues[0].id; - // giving a class to document.body, with a syntax according to https://www.w3.org/TR/CSS21/syndata.html#characters - let current_audiotag = document.CPU.current_audiotag_playing; - if (current_audiotag !== null) { - document.CPU.body_className_playing_cue = `cpu_playing_tag_«${document.CPU.current_audiotag_playing.id}»_cue_«${cue_id}»`; - document.body.classList.add(document.CPU.body_className_playing_cue); - } - - container.highlight_point(plane_name, cue_id, class_name); - }, - - - /** - * Updatting time position. Pause if a end position is defined - * - * @param {} event The event - */ - update : function(event) { - let audiotag = event.target; - - if ((trigger._timecode_end !== false) && (audiotag.currentTime > trigger._timecode_end)) { - trigger.pause(undefined, audiotag); - } - - audiotag.CPU_update(); - if (!audiotag.paused) { - window.localStorage.setItem(audiotag.currentSrc, String(audiotag.currentTime)); - } - }, - - /** - * When an audiotag is ended, advance in playlist - * - * @param {} event The event - * @param {string} audiotag The audiotag - */ - ended : function(event, audiotag) { - // the media element reached its end - if (audiotag === undefined) { - audiotag = event.target; - } - if (!('playlist' in audiotag.dataset)) { - return; - } - // and is in a declarated playlist - let playlist_name = audiotag.dataset.playlist; - let playlist = document.CPU.playlists[playlist_name]; - if (playlist === undefined) { - warn(`Named playlist ${playlist_name} not created. WTF ?`); - return; - } - let playlist_index = playlist.indexOf(audiotag.id); - if (playlist_index === -1) { - warn(`Audiotag ${audiotag.id} not in playlist ${playlist_name}. WTF ?`); - return; - } - if ((playlist_index +1) === playlist.length) { - // end of playlist - return; - } - let next_id = playlist[playlist_index+1]; - let next_audiotag = document.getElementById(next_id); - if (next_audiotag === null) { - warn(`Audiotag #${next_id} doesn't exists. WTF ?`); - return; - } - // Play the next media in playlist, starting at zero - document.CPU.seekElementAt(next_audiotag, 0); - trigger.play(undefined, next_audiotag); - }, - - /** - * Interprets if element is modified - * - * @param {} mutationsList The mutations list - */ - observer_cpuaudio : function(mutationsList) { - let container = document.CPU.find_container(mutationsList[0].target); - - let media_tagname = 'audio'; - let audio_element = container.element.querySelector(media_tagname) - if (audio_element === null) { - info(`<${media_tagname}> element was removed.`) - container.element.remove(); - return; - } - container.element.copy_attributes_to_media_dataset(); - }, - /** - * Interprets if