@oat-sa/tao-core-ui 1.64.1 → 1.64.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +339 -339
- package/README.md +22 -22
- package/dist/actionbar.js +92 -92
- package/dist/adder.js +109 -109
- package/dist/animable/absorbable/absorbable.js +29 -29
- package/dist/animable/absorbable/css/absorb.css +7 -7
- package/dist/animable/absorbable/css/absorb.css.map +1 -1
- package/dist/animable/pulsable/css/pulse.css +7 -7
- package/dist/animable/pulsable/css/pulse.css.map +1 -1
- package/dist/animable/pulsable/pulsable.js +23 -23
- package/dist/areaBroker.js +51 -51
- package/dist/autocomplete/css/autocomplete.css +7 -7
- package/dist/autocomplete/css/autocomplete.css.map +1 -1
- package/dist/autocomplete.js +384 -384
- package/dist/autoscroll.js +22 -22
- package/dist/badge/badge.js +35 -35
- package/dist/badge/css/badge.css +7 -7
- package/dist/badge/css/badge.css.map +1 -1
- package/dist/breadcrumbs/css/breadcrumbs.css +7 -7
- package/dist/breadcrumbs/css/breadcrumbs.css.map +1 -1
- package/dist/breadcrumbs.js +57 -57
- package/dist/btngrouper.js +64 -64
- package/dist/bulkActionPopup/css/bulkActionPopup.css +7 -7
- package/dist/bulkActionPopup/css/bulkActionPopup.css.map +1 -1
- package/dist/bulkActionPopup.js +91 -91
- package/dist/button.js +89 -89
- package/dist/calculator/css/calculator.css +10 -10
- package/dist/calculator/css/calculator.css.map +1 -1
- package/dist/calculator.js +51 -51
- package/dist/cascadingComboBox.js +34 -34
- package/dist/ckeditor/ckConfigurator.js +49 -49
- package/dist/ckeditor/dtdHandler.js +110 -110
- package/dist/class/css/selector.css +7 -7
- package/dist/class/css/selector.css.map +1 -1
- package/dist/class/selector.js +98 -98
- package/dist/component/alignable.js +81 -81
- package/dist/component/containable.js +37 -37
- package/dist/component/css/components.css +7 -7
- package/dist/component/css/components.css.map +1 -1
- package/dist/component/css/windowComponent.css +7 -7
- package/dist/component/css/windowComponent.css.map +1 -1
- package/dist/component/draggable.js +25 -25
- package/dist/component/placeable.js +70 -70
- package/dist/component/resizable.js +61 -61
- package/dist/component/stackable.js +20 -20
- package/dist/component/windowed.js +46 -46
- package/dist/component.js +140 -140
- package/dist/container.js +76 -76
- package/dist/contextualPopup/css/contextualPopup.css +7 -7
- package/dist/contextualPopup/css/contextualPopup.css.map +1 -1
- package/dist/contextualPopup.js +100 -100
- package/dist/dashboard/css/dashboard.css +7 -7
- package/dist/dashboard/css/dashboard.css.map +1 -1
- package/dist/dashboard.js +62 -62
- package/dist/datalist/css/datalist.css +7 -7
- package/dist/datalist/css/datalist.css.map +1 -1
- package/dist/datalist.js +147 -147
- package/dist/datatable/css/datatable.css +7 -7
- package/dist/datatable/css/datatable.css.map +1 -1
- package/dist/datatable/filterStrategy/filterStrategy.js +27 -27
- package/dist/datatable/filterStrategy/multiple.js +25 -25
- package/dist/datatable/filterStrategy/single.js +25 -25
- package/dist/datatable.js +377 -377
- package/dist/dateRange/css/dateRange.css +7 -7
- package/dist/dateRange/css/dateRange.css.map +1 -1
- package/dist/dateRange/dateRange.js +97 -97
- package/dist/datetime/css/picker.css +7 -7
- package/dist/datetime/css/picker.css.map +1 -1
- package/dist/datetime/picker.js +161 -161
- package/dist/deleter.js +79 -79
- package/dist/destination/css/selector.css +7 -7
- package/dist/destination/css/selector.css.map +1 -1
- package/dist/destination/selector.js +52 -52
- package/dist/dialog/alert.js +26 -26
- package/dist/dialog/confirm.js +27 -27
- package/dist/dialog/confirmDelete.js +31 -31
- package/dist/dialog.js +141 -141
- package/dist/disabler.js +90 -90
- package/dist/documentViewer/css/documentViewer.css +7 -7
- package/dist/documentViewer/css/documentViewer.css.map +1 -1
- package/dist/documentViewer/providers/pdfViewer/fallback/viewer.js +30 -30
- package/dist/documentViewer/providers/pdfViewer/pdfjs/areaBroker.js +25 -25
- package/dist/documentViewer/providers/pdfViewer/pdfjs/findBar.js +120 -120
- package/dist/documentViewer/providers/pdfViewer/pdfjs/pageView.js +89 -89
- package/dist/documentViewer/providers/pdfViewer/pdfjs/pagesManager.js +52 -52
- package/dist/documentViewer/providers/pdfViewer/pdfjs/searchEngine.js +112 -112
- package/dist/documentViewer/providers/pdfViewer/pdfjs/textManager.js +67 -67
- package/dist/documentViewer/providers/pdfViewer/pdfjs/viewer.js +81 -81
- package/dist/documentViewer/providers/pdfViewer/pdfjs/wrapper.js +111 -111
- package/dist/documentViewer/providers/pdfViewer.js +29 -29
- package/dist/documentViewer/viewerFactory.js +71 -71
- package/dist/documentViewer.js +86 -86
- package/dist/dropdown/css/dropdown.css +7 -7
- package/dist/dropdown/css/dropdown.css.map +1 -1
- package/dist/dropdown.js +84 -84
- package/dist/durationer.js +58 -58
- package/dist/dynamicComponent/css/dynamicComponent.css +7 -7
- package/dist/dynamicComponent/css/dynamicComponent.css.map +1 -1
- package/dist/dynamicComponent.js +103 -103
- package/dist/feedback.js +84 -84
- package/dist/figure/FigureStateActive.js +111 -111
- package/dist/filesender.js +26 -26
- package/dist/filter.js +34 -34
- package/dist/form/css/dropdownForm.css +7 -7
- package/dist/form/css/dropdownForm.css.map +1 -1
- package/dist/form/css/form.css +7 -7
- package/dist/form/css/form.css.map +1 -1
- package/dist/form/dropdownForm.js +99 -99
- package/dist/form/form.js +248 -248
- package/dist/form/simpleForm.js +71 -71
- package/dist/form/validator/css/validator.css +7 -7
- package/dist/form/validator/css/validator.css.map +1 -1
- package/dist/form/validator/renderer.js +52 -52
- package/dist/form/validator/validator.js +103 -103
- package/dist/form/widget/css/widget.css +7 -7
- package/dist/form/widget/css/widget.css.map +1 -1
- package/dist/form/widget/definitions.js +24 -24
- package/dist/form/widget/loader.js +16 -16
- package/dist/form/widget/providers/checkBox.js +62 -62
- package/dist/form/widget/providers/comboBox.js +46 -46
- package/dist/form/widget/providers/default.js +35 -35
- package/dist/form/widget/providers/hidden.js +37 -37
- package/dist/form/widget/providers/hiddenBox.js +58 -58
- package/dist/form/widget/providers/radioBox.js +57 -57
- package/dist/form/widget/providers/textArea.js +35 -35
- package/dist/form/widget/providers/textBox.js +34 -34
- package/dist/form/widget/widget.js +141 -141
- package/dist/form.js +10 -10
- package/dist/formValidator/formValidator.js +61 -61
- package/dist/formValidator/highlighters/highlighter.js +41 -41
- package/dist/formValidator/highlighters/message.js +29 -29
- package/dist/formValidator/highlighters/tooltip.js +32 -32
- package/dist/generis/form/css/form.css +7 -7
- package/dist/generis/form/css/form.css.map +1 -1
- package/dist/generis/form/form.js +73 -73
- package/dist/generis/validator/css/validator.css +7 -7
- package/dist/generis/validator/css/validator.css.map +1 -1
- package/dist/generis/validator/validator.js +38 -38
- package/dist/generis/widget/checkBox/checkBox.js +39 -39
- package/dist/generis/widget/comboBox/comboBox.js +32 -32
- package/dist/generis/widget/css/widget.css +7 -7
- package/dist/generis/widget/css/widget.css.map +1 -1
- package/dist/generis/widget/hiddenBox/hiddenBox.js +40 -40
- package/dist/generis/widget/loader.js +20 -20
- package/dist/generis/widget/textBox/textBox.js +27 -27
- package/dist/generis/widget/widget.js +47 -47
- package/dist/groupedComboBox.js +36 -36
- package/dist/groupvalidator.js +19 -19
- package/dist/hider.js +41 -41
- package/dist/highlighter.js +278 -278
- package/dist/image/ImgStateActive/extractLabel.js +20 -20
- package/dist/image/ImgStateActive/helper.js +16 -16
- package/dist/image/ImgStateActive/initHelper.js +85 -85
- package/dist/image/ImgStateActive/initMediaEditor.js +48 -48
- package/dist/image/ImgStateActive/mediaSizer.js +32 -32
- package/dist/image/ImgStateActive.js +104 -104
- package/dist/incrementer.js +58 -58
- package/dist/inplacer.js +87 -87
- package/dist/interactUtils.js +42 -42
- package/dist/itemButtonList/css/item-button-list.css +23 -23
- package/dist/itemButtonList/css/item-button-list.css.map +1 -1
- package/dist/itemButtonList.js +102 -102
- package/dist/keyNavigation/navigableDomElement.js +76 -76
- package/dist/keyNavigation/navigator.js +158 -158
- package/dist/listbox/css/listbox.css +7 -7
- package/dist/listbox/css/listbox.css.map +1 -1
- package/dist/listbox.js +84 -84
- package/dist/liststyler.js +57 -57
- package/dist/loadingButton/css/button.css +7 -7
- package/dist/loadingButton/css/button.css.map +1 -1
- package/dist/loadingButton/loadingButton.js +35 -35
- package/dist/lock.js +112 -112
- package/dist/login/login.js +87 -87
- package/dist/maths/calculator/basicCalculator.js +50 -50
- package/dist/maths/calculator/calculatorComponent.js +29 -29
- package/dist/maths/calculator/core/areaBroker.js +25 -25
- package/dist/maths/calculator/core/board.js +300 -300
- package/dist/maths/calculator/core/expression.js +98 -98
- package/dist/maths/calculator/core/labels.js +16 -16
- package/dist/maths/calculator/core/plugin.js +19 -19
- package/dist/maths/calculator/core/terms.js +26 -26
- package/dist/maths/calculator/core/tokenizer.js +98 -98
- package/dist/maths/calculator/core/tokens.js +75 -75
- package/dist/maths/calculator/css/calculator.css +7 -7
- package/dist/maths/calculator/css/calculator.css.map +1 -1
- package/dist/maths/calculator/defaultCalculator.js +23 -23
- package/dist/maths/calculator/plugins/core/degrad.js +22 -22
- package/dist/maths/calculator/plugins/core/history.js +45 -45
- package/dist/maths/calculator/plugins/core/remind.js +22 -22
- package/dist/maths/calculator/plugins/core/stepNavigation.js +31 -31
- package/dist/maths/calculator/plugins/keyboard/templateKeyboard/templateKeyboard.js +48 -48
- package/dist/maths/calculator/plugins/modifiers/pow10.js +35 -35
- package/dist/maths/calculator/plugins/modifiers/sign.js +90 -90
- package/dist/maths/calculator/plugins/screen/simpleScreen/simpleScreen.js +47 -47
- package/dist/maths/calculator/pluginsLoader.js +21 -21
- package/dist/maths/calculator/scientificCalculator.js +78 -78
- package/dist/mediaEditor/mediaEditorComponent.js +51 -51
- package/dist/mediaEditor/plugins/mediaAlignment/helper.js +16 -16
- package/dist/mediaEditor/plugins/mediaAlignment/mediaAlignmentComponent.js +47 -47
- package/dist/mediaEditor/plugins/mediaAlignment/style.css +7 -7
- package/dist/mediaEditor/plugins/mediaDimension/helper.js +70 -70
- package/dist/mediaEditor/plugins/mediaDimension/mediaDimensionComponent.js +140 -140
- package/dist/mediaEditor/plugins/mediaDimension/style.css +141 -141
- package/dist/mediaplayer/css/player.css +7 -7
- package/dist/mediaplayer/css/player.css.map +1 -1
- package/dist/mediaplayer/players/html5.js +52 -52
- package/dist/mediaplayer/players/youtube.js +39 -39
- package/dist/mediaplayer/players.js +18 -18
- package/dist/mediaplayer/support.js +55 -55
- package/dist/mediaplayer/utils/reminder.js +100 -100
- package/dist/mediaplayer/utils/timeObserver.js +92 -92
- package/dist/mediaplayer/youtubeManager.js +51 -51
- package/dist/mediaplayer.js +447 -447
- package/dist/mediasizer.js +122 -122
- package/dist/modal.js +87 -87
- package/dist/movableComponent.js +35 -35
- package/dist/pageSizeSelector.js +31 -31
- package/dist/pageStatus.js +33 -33
- package/dist/pagination/css/pagination.css +7 -7
- package/dist/pagination/css/pagination.css.map +1 -1
- package/dist/pagination/paginationStrategy.js +23 -23
- package/dist/pagination/providers/pages.js +24 -24
- package/dist/pagination/providers/simple.js +22 -22
- package/dist/pagination.js +45 -45
- package/dist/previewer.js +67 -67
- package/dist/progressbar.js +58 -58
- package/dist/propertySelector/css/propertySelector.css +7 -7
- package/dist/propertySelector/css/propertySelector.css.map +1 -1
- package/dist/propertySelector/propertySelector.js +86 -86
- package/dist/report.js +73 -73
- package/dist/resource/css/selector.css +7 -7
- package/dist/resource/css/selector.css.map +1 -1
- package/dist/resource/filters.js +60 -60
- package/dist/resource/list.js +53 -53
- package/dist/resource/selectable.js +92 -92
- package/dist/resource/selector.js +182 -182
- package/dist/resource/tree.js +91 -91
- package/dist/resourcemgr/css/resourcemgr.css +7 -7
- package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
- package/dist/resourcemgr/fileBrowser.js +75 -75
- package/dist/resourcemgr/fileSelector.js +45 -45
- package/dist/resourcemgr/util/updatePermissions.js +4 -4
- package/dist/resourcemgr.js +49 -49
- package/dist/scroller.js +26 -26
- package/dist/searchModal/advancedSearch.js +155 -155
- package/dist/searchModal/css/advancedSearch.css +7 -7
- package/dist/searchModal/css/advancedSearch.css.map +1 -1
- package/dist/searchModal/css/searchModal.css +7 -7
- package/dist/searchModal/css/searchModal.css.map +1 -1
- package/dist/searchModal.js +134 -134
- package/dist/selecter.js +6 -6
- package/dist/stacker.js +43 -43
- package/dist/switch/css/switch.css +7 -7
- package/dist/switch/css/switch.css.map +1 -1
- package/dist/switch/switch.js +62 -62
- package/dist/tableModel.js +33 -33
- package/dist/tabs/css/tabs.css +12 -12
- package/dist/tabs/css/tabs.css.map +1 -1
- package/dist/tabs.js +220 -220
- package/dist/taskQueue/css/taskQueue.css +7 -7
- package/dist/taskQueue/css/taskQueue.css.map +1 -1
- package/dist/taskQueue/status.js +59 -59
- package/dist/taskQueue/table.js +54 -54
- package/dist/taskQueue/taskQueue.js +18 -18
- package/dist/taskQueue/taskQueueModel.js +102 -102
- package/dist/taskQueue.js +47 -47
- package/dist/taskQueueButton/css/taskable.css +7 -7
- package/dist/taskQueueButton/css/taskable.css.map +1 -1
- package/dist/taskQueueButton/css/treeButton.css +7 -7
- package/dist/taskQueueButton/css/treeButton.css.map +1 -1
- package/dist/taskQueueButton/standardButton.js +39 -39
- package/dist/taskQueueButton/taskable.js +41 -41
- package/dist/taskQueueButton/treeButton.js +43 -43
- package/dist/themeLoader.js +75 -75
- package/dist/themes.js +84 -84
- package/dist/toggler.js +57 -57
- package/dist/tooltip.js +41 -41
- package/dist/tooltipster.js +17 -17
- package/dist/transformer.js +117 -117
- package/dist/tristateCheckboxGroup/css/tristateCheckboxGroup.css +7 -7
- package/dist/tristateCheckboxGroup/css/tristateCheckboxGroup.css.map +1 -1
- package/dist/tristateCheckboxGroup.js +62 -62
- package/dist/uploader.js +145 -145
- package/dist/validator/validators.js +48 -48
- package/dist/validator.js +23 -23
- package/dist/waitForMedia.js +33 -33
- package/dist/waitingDialog/css/waitingDialog.css +7 -7
- package/dist/waitingDialog/css/waitingDialog.css.map +1 -1
- package/dist/waitingDialog/waitingDialog.js +54 -54
- package/package.json +110 -110
- package/scss/basic.scss +16 -16
- package/scss/ckeditor/skins/tao/scss/inc/_ck-icons.scss +60 -60
- package/scss/ckeditor/skins/tao/scss/inc/_tao.scss +59 -59
- package/scss/font/tao/tao.svg +235 -235
- package/scss/inc/_base.scss +495 -495
- package/scss/inc/_bootstrap.scss +6 -6
- package/scss/inc/_buttons.scss +114 -114
- package/scss/inc/_colors.scss +93 -93
- package/scss/inc/_feedback.scss +154 -154
- package/scss/inc/_flex-grid.scss +15 -15
- package/scss/inc/_fonts.scss +4 -4
- package/scss/inc/_forms.scss +832 -832
- package/scss/inc/_functions.scss +283 -283
- package/scss/inc/_jquery.nouislider.scss +254 -254
- package/scss/inc/_normalize.scss +528 -528
- package/scss/inc/_report.scss +67 -67
- package/scss/inc/_secondary-properties.scss +89 -89
- package/scss/inc/_select2.scss +634 -634
- package/scss/inc/_toolbars.scss +155 -155
- package/scss/inc/_tooltip.scss +312 -312
- package/scss/inc/_variables.scss +21 -21
- package/scss/inc/base/_highlight.scss +5 -5
- package/scss/inc/base/_list-style.scss +58 -58
- package/scss/inc/base/_svg.scss +3 -3
- package/scss/inc/base/_table.scss +62 -62
- package/scss/inc/fonts/_source-sans-pro.scss +29 -29
- package/scss/inc/fonts/_tao-icon-classes.scss +227 -227
- package/scss/inc/fonts/_tao-icon-def.scss +12 -12
- package/scss/inc/fonts/_tao-icon-vars.scss +241 -241
- package/src/actionbar/tpl/main.tpl +8 -8
- package/src/actionbar.js +251 -251
- package/src/adder.js +250 -250
- package/src/animable/absorbable/absorbable.js +134 -134
- package/src/animable/absorbable/css/absorb.css +7 -7
- package/src/animable/absorbable/css/absorb.css.map +1 -1
- package/src/animable/absorbable/scss/absorb.scss +37 -37
- package/src/animable/pulsable/css/pulse.css +7 -7
- package/src/animable/pulsable/css/pulse.css.map +1 -1
- package/src/animable/pulsable/pulsable.js +90 -90
- package/src/animable/pulsable/scss/pulse.scss +22 -22
- package/src/areaBroker.js +160 -160
- package/src/autocomplete/css/autocomplete.css +7 -7
- package/src/autocomplete/css/autocomplete.css.map +1 -1
- package/src/autocomplete/scss/autocomplete.scss +37 -37
- package/src/autocomplete.js +1027 -1027
- package/src/autoscroll.js +57 -57
- package/src/badge/badge.js +119 -119
- package/src/badge/css/badge.css +7 -7
- package/src/badge/css/badge.css.map +1 -1
- package/src/badge/scss/badge.scss +92 -92
- package/src/badge/tpl/badge.tpl +4 -4
- package/src/breadcrumbs/css/breadcrumbs.css +7 -7
- package/src/breadcrumbs/css/breadcrumbs.css.map +1 -1
- package/src/breadcrumbs/scss/breadcrumbs.scss +52 -52
- package/src/breadcrumbs/tpl/breadcrumbs.tpl +20 -20
- package/src/breadcrumbs.js +99 -99
- package/src/btngrouper.js +213 -213
- package/src/bulkActionPopup/css/bulkActionPopup.css +7 -7
- package/src/bulkActionPopup/css/bulkActionPopup.css.map +1 -1
- package/src/bulkActionPopup/scss/bulkActionPopup.scss +63 -63
- package/src/bulkActionPopup/tpl/layout.tpl +76 -76
- package/src/bulkActionPopup/tpl/select.tpl +8 -8
- package/src/bulkActionPopup.js +274 -274
- package/src/button/tpl/button.tpl +4 -4
- package/src/button.js +135 -135
- package/src/calculator/css/calculator.css +10 -10
- package/src/calculator/css/calculator.css.map +1 -1
- package/src/calculator/scss/calculator.scss +139 -139
- package/src/calculator.js +188 -188
- package/src/cascadingComboBox.js +126 -126
- package/src/ckeditor/ckConfigurator.js +742 -742
- package/src/ckeditor/dtdHandler.js +1030 -1030
- package/src/class/css/selector.css +7 -7
- package/src/class/css/selector.css.map +1 -1
- package/src/class/scss/selector.scss +101 -101
- package/src/class/selector.js +329 -329
- package/src/class/tpl/listItem.tpl +9 -9
- package/src/class/tpl/selector.tpl +10 -10
- package/src/component/alignable.js +274 -274
- package/src/component/containable.js +122 -122
- package/src/component/css/components.css +7 -7
- package/src/component/css/components.css.map +1 -1
- package/src/component/css/windowComponent.css +7 -7
- package/src/component/css/windowComponent.css.map +1 -1
- package/src/component/draggable.js +104 -104
- package/src/component/placeable.js +233 -233
- package/src/component/resizable.js +195 -195
- package/src/component/scss/components.scss +507 -507
- package/src/component/scss/windowComponent.scss +62 -62
- package/src/component/stackable.js +67 -67
- package/src/component/tpl/window.tpl +7 -7
- package/src/component/windowed.js +206 -206
- package/src/component.js +401 -401
- package/src/container.js +200 -200
- package/src/contextualPopup/css/contextualPopup.css +7 -7
- package/src/contextualPopup/css/contextualPopup.css.map +1 -1
- package/src/contextualPopup/scss/contextualPopup.scss +78 -78
- package/src/contextualPopup/tpl/popup.tpl +10 -10
- package/src/contextualPopup.js +294 -294
- package/src/css/basic.css +104 -104
- package/src/css/basic.css.map +1 -1
- package/src/dashboard/css/dashboard.css +7 -7
- package/src/dashboard/css/dashboard.css.map +1 -1
- package/src/dashboard/scss/dashboard.scss +93 -93
- package/src/dashboard/tpl/dashboard.tpl +16 -16
- package/src/dashboard/tpl/dashboardMetricsList.tpl +15 -15
- package/src/dashboard.js +179 -179
- package/src/datalist/css/datalist.css +7 -7
- package/src/datalist/css/datalist.css.map +1 -1
- package/src/datalist/scss/datalist.scss +116 -116
- package/src/datalist/tpl/list.tpl +24 -24
- package/src/datalist/tpl/main.tpl +44 -44
- package/src/datalist.js +500 -500
- package/src/datatable/css/datatable.css +7 -7
- package/src/datatable/css/datatable.css.map +1 -1
- package/src/datatable/filterStrategy/filterStrategy.js +70 -70
- package/src/datatable/filterStrategy/multiple.js +126 -126
- package/src/datatable/filterStrategy/single.js +108 -108
- package/src/datatable/scss/datatable.scss +149 -149
- package/src/datatable/tpl/button.tpl +6 -6
- package/src/datatable/tpl/layout.tpl +158 -158
- package/src/datatable.js +1056 -1056
- package/src/dateRange/css/dateRange.css +7 -7
- package/src/dateRange/css/dateRange.css.map +1 -1
- package/src/dateRange/dateRange.js +341 -341
- package/src/dateRange/scss/dateRange.scss +7 -7
- package/src/dateRange/tpl/select.tpl +18 -18
- package/src/datetime/css/picker.css +7 -7
- package/src/datetime/css/picker.css.map +1 -1
- package/src/datetime/picker.js +576 -576
- package/src/datetime/scss/picker.scss +192 -192
- package/src/datetime/tpl/picker.tpl +18 -18
- package/src/deleter/undo.tpl +6 -6
- package/src/deleter.js +296 -296
- package/src/destination/css/selector.css +7 -7
- package/src/destination/css/selector.css.map +1 -1
- package/src/destination/scss/selector.scss +36 -36
- package/src/destination/selector.js +195 -195
- package/src/destination/tpl/selector.tpl +13 -13
- package/src/dialog/alert.js +70 -70
- package/src/dialog/confirm.js +85 -85
- package/src/dialog/confirmDelete.js +95 -95
- package/src/dialog/tpl/body.tpl +24 -24
- package/src/dialog/tpl/buttons.tpl +6 -6
- package/src/dialog/tpl/checkbox.tpl +5 -5
- package/src/dialog.js +517 -517
- package/src/disabler.js +230 -230
- package/src/documentViewer/css/documentViewer.css +7 -7
- package/src/documentViewer/css/documentViewer.css.map +1 -1
- package/src/documentViewer/providers/pdfViewer/fallback/viewer.js +69 -69
- package/src/documentViewer/providers/pdfViewer/pdfjs/areaBroker.js +41 -41
- package/src/documentViewer/providers/pdfViewer/pdfjs/findBar.js +475 -475
- package/src/documentViewer/providers/pdfViewer/pdfjs/findBar.tpl +20 -20
- package/src/documentViewer/providers/pdfViewer/pdfjs/match.tpl +1 -1
- package/src/documentViewer/providers/pdfViewer/pdfjs/page.tpl +4 -4
- package/src/documentViewer/providers/pdfViewer/pdfjs/pageView.js +318 -318
- package/src/documentViewer/providers/pdfViewer/pdfjs/pagesManager.js +167 -167
- package/src/documentViewer/providers/pdfViewer/pdfjs/searchEngine.js +451 -451
- package/src/documentViewer/providers/pdfViewer/pdfjs/textManager.js +252 -252
- package/src/documentViewer/providers/pdfViewer/pdfjs/viewer.js +299 -299
- package/src/documentViewer/providers/pdfViewer/pdfjs/viewer.tpl +16 -16
- package/src/documentViewer/providers/pdfViewer/pdfjs/wrapper.js +351 -351
- package/src/documentViewer/providers/pdfViewer.js +93 -93
- package/src/documentViewer/scss/documentViewer.scss +184 -184
- package/src/documentViewer/viewerFactory.js +191 -191
- package/src/documentViewer.js +238 -238
- package/src/dropdown/css/dropdown.css +7 -7
- package/src/dropdown/css/dropdown.css.map +1 -1
- package/src/dropdown/scss/dropdown.scss +99 -99
- package/src/dropdown/tpl/dropdown.tpl +8 -8
- package/src/dropdown/tpl/list-item.tpl +4 -4
- package/src/dropdown.js +255 -255
- package/src/durationer.js +220 -220
- package/src/dynamicComponent/css/dynamicComponent.css +7 -7
- package/src/dynamicComponent/css/dynamicComponent.css.map +1 -1
- package/src/dynamicComponent/scss/dynamicComponent.scss +98 -98
- package/src/dynamicComponent/tpl/layout.tpl +17 -17
- package/src/dynamicComponent.js +553 -553
- package/src/feedback/feedback.tpl +7 -7
- package/src/feedback.js +295 -295
- package/src/figure/FigureStateActive.js +184 -184
- package/src/filesender.js +112 -112
- package/src/filter/template.tpl +5 -5
- package/src/filter.js +129 -129
- package/src/form/css/dropdownForm.css +7 -7
- package/src/form/css/dropdownForm.css.map +1 -1
- package/src/form/css/form.css +7 -7
- package/src/form/css/form.css.map +1 -1
- package/src/form/dropdownForm.js +281 -281
- package/src/form/form.js +688 -688
- package/src/form/scss/dropdownForm.scss +60 -60
- package/src/form/scss/form.scss +25 -25
- package/src/form/simpleForm.js +125 -125
- package/src/form/tpl/dropdownForm.tpl +4 -4
- package/src/form/tpl/form.tpl +7 -7
- package/src/form/validator/css/validator.css +7 -7
- package/src/form/validator/css/validator.css.map +1 -1
- package/src/form/validator/renderer.js +118 -118
- package/src/form/validator/scss/validator.scss +14 -14
- package/src/form/validator/tpl/message.tpl +1 -1
- package/src/form/validator/tpl/validator.tpl +1 -1
- package/src/form/validator/validator.js +220 -220
- package/src/form/widget/css/widget.css +7 -7
- package/src/form/widget/css/widget.css.map +1 -1
- package/src/form/widget/definitions.js +51 -51
- package/src/form/widget/loader.js +40 -40
- package/src/form/widget/providers/checkBox.js +138 -138
- package/src/form/widget/providers/comboBox.js +63 -63
- package/src/form/widget/providers/default.js +90 -90
- package/src/form/widget/providers/hidden.js +62 -62
- package/src/form/widget/providers/hiddenBox.js +152 -152
- package/src/form/widget/providers/radioBox.js +99 -99
- package/src/form/widget/providers/textArea.js +52 -52
- package/src/form/widget/providers/textBox.js +48 -48
- package/src/form/widget/scss/widget.scss +55 -55
- package/src/form/widget/tpl/checkBox.tpl +25 -25
- package/src/form/widget/tpl/comboBox.tpl +13 -13
- package/src/form/widget/tpl/hidden.tpl +1 -1
- package/src/form/widget/tpl/hiddenBox.tpl +17 -17
- package/src/form/widget/tpl/label.tpl +6 -6
- package/src/form/widget/tpl/radioBox.tpl +25 -25
- package/src/form/widget/tpl/textArea.tpl +8 -8
- package/src/form/widget/tpl/widget.tpl +8 -8
- package/src/form/widget/widget.js +372 -372
- package/src/form.js +47 -47
- package/src/formValidator/formValidator.js +253 -253
- package/src/formValidator/highlighters/highlighter.js +103 -103
- package/src/formValidator/highlighters/message.js +68 -68
- package/src/formValidator/highlighters/tooltip.js +78 -78
- package/src/generis/form/css/form.css +7 -7
- package/src/generis/form/css/form.css.map +1 -1
- package/src/generis/form/form.js +239 -239
- package/src/generis/form/readme.md +70 -70
- package/src/generis/form/scss/form.scss +23 -23
- package/src/generis/form/tpl/form.tpl +16 -16
- package/src/generis/validator/css/validator.css +7 -7
- package/src/generis/validator/css/validator.css.map +1 -1
- package/src/generis/validator/readme.md +46 -46
- package/src/generis/validator/scss/validator.scss +13 -13
- package/src/generis/validator/validator.js +128 -128
- package/src/generis/widget/checkBox/checkBox.js +112 -112
- package/src/generis/widget/checkBox/checkBox.tpl +18 -18
- package/src/generis/widget/comboBox/comboBox.js +66 -66
- package/src/generis/widget/comboBox/comboBox.tpl +12 -12
- package/src/generis/widget/css/widget.css +7 -7
- package/src/generis/widget/css/widget.css.map +1 -1
- package/src/generis/widget/hiddenBox/hiddenBox.js +131 -131
- package/src/generis/widget/hiddenBox/hiddenBox.tpl +16 -16
- package/src/generis/widget/loader.js +49 -49
- package/src/generis/widget/readme.md +59 -59
- package/src/generis/widget/scss/widget.scss +61 -61
- package/src/generis/widget/textBox/textBox.js +64 -64
- package/src/generis/widget/textBox/textBox.tpl +7 -7
- package/src/generis/widget/widget.js +164 -164
- package/src/generis/widget/widget.tpl +5 -5
- package/src/groupedComboBox.js +99 -99
- package/src/groupvalidator.js +84 -84
- package/src/hider.js +88 -88
- package/src/highlighter.js +1192 -1192
- package/src/image/ImgStateActive/extractLabel.js +29 -29
- package/src/image/ImgStateActive/helper.js +36 -36
- package/src/image/ImgStateActive/initHelper.js +137 -137
- package/src/image/ImgStateActive/initMediaEditor.js +92 -92
- package/src/image/ImgStateActive/mediaSizer.js +63 -63
- package/src/image/ImgStateActive.js +115 -115
- package/src/incrementer.js +309 -309
- package/src/inplacer.js +315 -315
- package/src/interactUtils.js +140 -140
- package/src/itemButtonList/css/item-button-list.css +23 -23
- package/src/itemButtonList/css/item-button-list.css.map +1 -1
- package/src/itemButtonList/scss/item-button-list.scss +236 -236
- package/src/itemButtonList/tpl/itemButtonList.tpl +21 -21
- package/src/itemButtonList.js +274 -274
- package/src/keyNavigation/navigableDomElement.js +285 -285
- package/src/keyNavigation/navigator.js +535 -535
- package/src/listbox/css/listbox.css +7 -7
- package/src/listbox/css/listbox.css.map +1 -1
- package/src/listbox/scss/listbox.scss +116 -116
- package/src/listbox/tpl/list.tpl +14 -14
- package/src/listbox/tpl/main.tpl +9 -9
- package/src/listbox.js +252 -252
- package/src/liststyler.js +155 -155
- package/src/loadingButton/css/button.css +7 -7
- package/src/loadingButton/css/button.css.map +1 -1
- package/src/loadingButton/loadingButton.js +110 -110
- package/src/loadingButton/scss/button.scss +41 -41
- package/src/loadingButton/tpl/button.tpl +5 -5
- package/src/lock/lock.tpl +16 -16
- package/src/lock.js +393 -393
- package/src/login/login.js +317 -317
- package/src/login/tpl/login.tpl +29 -29
- package/src/login/tpl/passwordReveal.tpl +7 -7
- package/src/maths/calculator/basicCalculator.js +55 -55
- package/src/maths/calculator/calculatorComponent.js +128 -128
- package/src/maths/calculator/core/areaBroker.js +38 -38
- package/src/maths/calculator/core/board.js +841 -841
- package/src/maths/calculator/core/expression.js +430 -430
- package/src/maths/calculator/core/labels.js +116 -116
- package/src/maths/calculator/core/plugin.js +40 -40
- package/src/maths/calculator/core/terms.js +459 -459
- package/src/maths/calculator/core/tokenizer.js +245 -245
- package/src/maths/calculator/core/tokens.js +178 -178
- package/src/maths/calculator/core/tpl/board.tpl +4 -4
- package/src/maths/calculator/css/calculator.css +7 -7
- package/src/maths/calculator/css/calculator.css.map +1 -1
- package/src/maths/calculator/defaultCalculator.js +66 -66
- package/src/maths/calculator/plugins/core/degrad.js +90 -90
- package/src/maths/calculator/plugins/core/history.js +166 -166
- package/src/maths/calculator/plugins/core/remind.js +96 -96
- package/src/maths/calculator/plugins/core/stepNavigation.js +175 -175
- package/src/maths/calculator/plugins/keyboard/templateKeyboard/defaultTemplate.tpl +36 -36
- package/src/maths/calculator/plugins/keyboard/templateKeyboard/templateKeyboard.js +91 -91
- package/src/maths/calculator/plugins/modifiers/pow10.js +143 -143
- package/src/maths/calculator/plugins/modifiers/sign.js +339 -339
- package/src/maths/calculator/plugins/screen/simpleScreen/defaultTemplate.tpl +3 -3
- package/src/maths/calculator/plugins/screen/simpleScreen/history.tpl +3 -3
- package/src/maths/calculator/plugins/screen/simpleScreen/simpleScreen.js +190 -190
- package/src/maths/calculator/pluginsLoader.js +46 -46
- package/src/maths/calculator/scientificCalculator.js +74 -74
- package/src/maths/calculator/scss/calculator.scss +396 -396
- package/src/maths/calculator/tpl/basicKeyboard.tpl +37 -37
- package/src/maths/calculator/tpl/basicScreen.tpl +2 -2
- package/src/maths/calculator/tpl/scientificKeyboard.tpl +61 -61
- package/src/maths/calculator/tpl/scientificScreen.tpl +3 -3
- package/src/mediaEditor/mediaEditorComponent.js +141 -141
- package/src/mediaEditor/plugins/mediaAlignment/helper.js +110 -110
- package/src/mediaEditor/plugins/mediaAlignment/mediaAlignmentComponent.js +99 -99
- package/src/mediaEditor/plugins/mediaAlignment/style.css +7 -7
- package/src/mediaEditor/plugins/mediaAlignment/tpl/mediaAlignment.tpl +25 -25
- package/src/mediaEditor/plugins/mediaDimension/helper.js +190 -190
- package/src/mediaEditor/plugins/mediaDimension/mediaDimensionComponent.js +561 -561
- package/src/mediaEditor/plugins/mediaDimension/style.css +141 -141
- package/src/mediaEditor/plugins/mediaDimension/tpl/mediaDimension.tpl +55 -55
- package/src/mediaEditor/tpl/editor.tpl +4 -4
- package/src/mediaplayer/css/player.css +7 -7
- package/src/mediaplayer/css/player.css.map +1 -1
- package/src/mediaplayer/players/html5.js +564 -564
- package/src/mediaplayer/players/youtube.js +323 -323
- package/src/mediaplayer/players.js +29 -29
- package/src/mediaplayer/readme.md +305 -305
- package/src/mediaplayer/scss/player.scss +569 -569
- package/src/mediaplayer/support.js +126 -126
- package/src/mediaplayer/tpl/audio.tpl +6 -6
- package/src/mediaplayer/tpl/player.tpl +37 -37
- package/src/mediaplayer/tpl/source.tpl +1 -1
- package/src/mediaplayer/tpl/video.tpl +6 -6
- package/src/mediaplayer/tpl/youtube.tpl +1 -1
- package/src/mediaplayer/utils/reminder.js +184 -184
- package/src/mediaplayer/utils/timeObserver.js +143 -143
- package/src/mediaplayer/youtubeManager.js +161 -161
- package/src/mediaplayer.js +1606 -1606
- package/src/mediasizer/mediasizer.tpl +55 -55
- package/src/mediasizer.js +635 -635
- package/src/modal.js +365 -365
- package/src/movableComponent.js +78 -78
- package/src/pageSizeSelector/tpl/pageSizeSelector.tpl +9 -9
- package/src/pageSizeSelector.js +106 -106
- package/src/pageStatus.js +147 -147
- package/src/pagination/css/pagination.css +7 -7
- package/src/pagination/css/pagination.css.map +1 -1
- package/src/pagination/paginationStrategy.js +53 -53
- package/src/pagination/providers/pages.js +161 -161
- package/src/pagination/providers/simple.js +74 -74
- package/src/pagination/providers/tpl/pages/page.tpl +1 -1
- package/src/pagination/providers/tpl/pages.tpl +8 -8
- package/src/pagination/providers/tpl/simple.tpl +7 -7
- package/src/pagination/scss/pagination.scss +111 -111
- package/src/pagination.js +237 -237
- package/src/previewer.js +300 -300
- package/src/progressbar.js +165 -165
- package/src/propertySelector/css/propertySelector.css +7 -7
- package/src/propertySelector/css/propertySelector.css.map +1 -1
- package/src/propertySelector/propertySelector.js +286 -286
- package/src/propertySelector/scss/propertySelector.scss +66 -66
- package/src/propertySelector/tpl/property-description.tpl +12 -12
- package/src/propertySelector/tpl/property-selector.tpl +6 -6
- package/src/report/feedback.tpl +11 -11
- package/src/report/layout.tpl +10 -10
- package/src/report.js +184 -184
- package/src/resource/css/selector.css +7 -7
- package/src/resource/css/selector.css.map +1 -1
- package/src/resource/filters.js +208 -208
- package/src/resource/list.js +200 -200
- package/src/resource/scss/_filters.scss +26 -26
- package/src/resource/scss/_resource-list.scss +107 -107
- package/src/resource/scss/_resource-tree.scss +205 -205
- package/src/resource/scss/selector.scss +187 -187
- package/src/resource/selectable.js +322 -322
- package/src/resource/selector.js +871 -871
- package/src/resource/tpl/filters.tpl +2 -2
- package/src/resource/tpl/list.tpl +7 -7
- package/src/resource/tpl/listNode.tpl +4 -4
- package/src/resource/tpl/selector.tpl +46 -46
- package/src/resource/tpl/tree.tpl +4 -4
- package/src/resource/tpl/treeNode.tpl +30 -30
- package/src/resource/tree.js +398 -398
- package/src/resourcemgr/css/resourcemgr.css +7 -7
- package/src/resourcemgr/css/resourcemgr.css.map +1 -1
- package/src/resourcemgr/fileBrowser.js +381 -381
- package/src/resourcemgr/filePreview.js +73 -73
- package/src/resourcemgr/fileSelector.js +322 -322
- package/src/resourcemgr/scss/resourcemgr.scss +254 -254
- package/src/resourcemgr/tpl/fileSelect.tpl +39 -39
- package/src/resourcemgr/tpl/folder.tpl +11 -11
- package/src/resourcemgr/tpl/layout.tpl +84 -84
- package/src/resourcemgr/tpl/rootFolder.tpl +13 -13
- package/src/resourcemgr/util/updatePermissions.js +53 -53
- package/src/resourcemgr.js +216 -216
- package/src/scroller.js +94 -94
- package/src/scss/basic.scss +16 -16
- package/src/searchModal/advancedSearch.js +638 -638
- package/src/searchModal/css/advancedSearch.css +7 -7
- package/src/searchModal/css/advancedSearch.css.map +1 -1
- package/src/searchModal/css/searchModal.css +7 -7
- package/src/searchModal/css/searchModal.css.map +1 -1
- package/src/searchModal/scss/advancedSearch.scss +171 -171
- package/src/searchModal/scss/searchModal.scss +393 -393
- package/src/searchModal/tpl/advanced-search.tpl +9 -9
- package/src/searchModal/tpl/info-message.tpl +3 -3
- package/src/searchModal/tpl/invalid-criteria-warning.tpl +10 -10
- package/src/searchModal/tpl/layout.tpl +27 -27
- package/src/searchModal/tpl/list-checkbox-criterion.tpl +17 -17
- package/src/searchModal/tpl/list-select-criterion.tpl +12 -12
- package/src/searchModal/tpl/text-criterion.tpl +11 -11
- package/src/searchModal.js +760 -760
- package/src/selecter.js +43 -43
- package/src/stacker.js +133 -133
- package/src/switch/css/switch.css +7 -7
- package/src/switch/css/switch.css.map +1 -1
- package/src/switch/scss/switch.scss +83 -83
- package/src/switch/switch.js +195 -195
- package/src/switch/tpl/switch.tpl +7 -7
- package/src/tableModel.js +112 -112
- package/src/tabs/css/tabs.css +12 -12
- package/src/tabs/css/tabs.css.map +1 -1
- package/src/tabs/scss/tabs.scss +50 -50
- package/src/tabs/tpl/panel.tpl +3 -3
- package/src/tabs/tpl/tabs.tpl +10 -10
- package/src/tabs.js +528 -528
- package/src/taskQueue/css/taskQueue.css +7 -7
- package/src/taskQueue/css/taskQueue.css.map +1 -1
- package/src/taskQueue/scss/taskQueue.scss +47 -47
- package/src/taskQueue/status.js +228 -228
- package/src/taskQueue/table.js +350 -350
- package/src/taskQueue/taskQueue.js +33 -33
- package/src/taskQueue/taskQueueModel.js +548 -548
- package/src/taskQueue/tpl/statusMessage.tpl +7 -7
- package/src/taskQueue.js +216 -216
- package/src/taskQueueButton/css/taskable.css +7 -7
- package/src/taskQueueButton/css/taskable.css.map +1 -1
- package/src/taskQueueButton/css/treeButton.css +7 -7
- package/src/taskQueueButton/css/treeButton.css.map +1 -1
- package/src/taskQueueButton/scss/taskable.scss +4 -4
- package/src/taskQueueButton/scss/treeButton.scss +34 -34
- package/src/taskQueueButton/standardButton.js +108 -108
- package/src/taskQueueButton/taskable.js +202 -202
- package/src/taskQueueButton/tpl/report.tpl +5 -5
- package/src/taskQueueButton/tpl/treeButton.tpl +6 -6
- package/src/taskQueueButton/treeButton.js +108 -108
- package/src/themeLoader.js +252 -252
- package/src/themes.js +162 -162
- package/src/toggler.js +200 -200
- package/src/tooltip/default.tpl +3 -3
- package/src/tooltip.js +160 -160
- package/src/tooltipster.js +25 -25
- package/src/transformer.js +327 -327
- package/src/tristateCheckboxGroup/css/tristateCheckboxGroup.css +7 -7
- package/src/tristateCheckboxGroup/css/tristateCheckboxGroup.css.map +1 -1
- package/src/tristateCheckboxGroup/scss/tristateCheckboxGroup.scss +15 -15
- package/src/tristateCheckboxGroup/tpl/li.tpl +6 -6
- package/src/tristateCheckboxGroup.js +207 -207
- package/src/uploader/fileEntry.tpl +6 -6
- package/src/uploader/uploader.tpl +32 -32
- package/src/uploader.js +594 -594
- package/src/validator/Report.js +10 -10
- package/src/validator/Validator.js +108 -108
- package/src/validator/validators.js +217 -217
- package/src/validator.js +262 -262
- package/src/waitForMedia.js +82 -82
- package/src/waitingDialog/css/waitingDialog.css +7 -7
- package/src/waitingDialog/css/waitingDialog.css.map +1 -1
- package/src/waitingDialog/scss/waitingDialog.scss +34 -34
- package/src/waitingDialog/waitingDialog.js +240 -240
package/src/mediaplayer.js
CHANGED
|
@@ -1,1606 +1,1606 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This program is free software; you can redistribute it and/or
|
|
3
|
-
* modify it under the terms of the GNU General Public License
|
|
4
|
-
* as published by the Free Software Foundation; under version 2
|
|
5
|
-
* of the License (non-upgradable).
|
|
6
|
-
*
|
|
7
|
-
* This program is distributed in the hope that it will be useful,
|
|
8
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
-
* GNU General Public License for more details.
|
|
11
|
-
*
|
|
12
|
-
* You should have received a copy of the GNU General Public License
|
|
13
|
-
* along with this program; if not, write to the Free Software
|
|
14
|
-
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
-
*
|
|
16
|
-
* Copyright (c) 2015-2021 (original work) Open Assessment Technologies SA ;
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import $ from 'jquery';
|
|
20
|
-
import _ from 'lodash';
|
|
21
|
-
import async from 'async';
|
|
22
|
-
import UrlParser from 'util/urlParser';
|
|
23
|
-
import eventifier from 'core/eventifier';
|
|
24
|
-
import mimetype from 'core/mimetype';
|
|
25
|
-
import store from 'core/store';
|
|
26
|
-
import support from 'ui/mediaplayer/support';
|
|
27
|
-
import players from 'ui/mediaplayer/players';
|
|
28
|
-
import playerTpl from 'ui/mediaplayer/tpl/player';
|
|
29
|
-
import 'ui/mediaplayer/css/player.css';
|
|
30
|
-
import 'nouislider';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* CSS namespace
|
|
34
|
-
* @type {String}
|
|
35
|
-
*/
|
|
36
|
-
const ns = '.mediaplayer';
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Minimum value of the volume
|
|
40
|
-
* @type {Number}
|
|
41
|
-
*/
|
|
42
|
-
const volumeMin = 0;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Maximum value of the volume
|
|
46
|
-
* @type {Number}
|
|
47
|
-
*/
|
|
48
|
-
const volumeMax = 100;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Threshold (minimum required space above the player) to display the volume
|
|
52
|
-
* above the bar.
|
|
53
|
-
* @type {Number}
|
|
54
|
-
*/
|
|
55
|
-
const volumePositionThreshold = 200;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Some default values
|
|
59
|
-
* @type {Object}
|
|
60
|
-
*/
|
|
61
|
-
const defaults = {
|
|
62
|
-
type: 'video/mp4',
|
|
63
|
-
video: {
|
|
64
|
-
width: '100%',
|
|
65
|
-
height: 'auto'
|
|
66
|
-
},
|
|
67
|
-
audio: {
|
|
68
|
-
width: '100%',
|
|
69
|
-
height: 'auto'
|
|
70
|
-
},
|
|
71
|
-
youtube: {
|
|
72
|
-
width: 640,
|
|
73
|
-
height: 360
|
|
74
|
-
},
|
|
75
|
-
options: {
|
|
76
|
-
volume: 80,
|
|
77
|
-
startMuted: false,
|
|
78
|
-
maxPlays: 0,
|
|
79
|
-
replayTimeout: 0,
|
|
80
|
-
canPause: true,
|
|
81
|
-
canSeek: true,
|
|
82
|
-
loop: false,
|
|
83
|
-
autoStart: false,
|
|
84
|
-
preview: true,
|
|
85
|
-
debug: false
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Ensures a value is a number
|
|
91
|
-
* @param {Number|String} value
|
|
92
|
-
* @returns {Number}
|
|
93
|
-
*/
|
|
94
|
-
const ensureNumber = value => {
|
|
95
|
-
const floatValue = parseFloat(value);
|
|
96
|
-
return isFinite(floatValue) ? floatValue : 0;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Format a number to string with leading zeros
|
|
101
|
-
* @param {Number} n
|
|
102
|
-
* @param {Number} len
|
|
103
|
-
* @returns {String}
|
|
104
|
-
*/
|
|
105
|
-
const leadingZero = (n, len) => {
|
|
106
|
-
let value = n.toString();
|
|
107
|
-
while (value.length < len) {
|
|
108
|
-
value = `0${value}`;
|
|
109
|
-
}
|
|
110
|
-
return value;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Formats a time value to string
|
|
115
|
-
* @param {Number} time
|
|
116
|
-
* @returns {String}
|
|
117
|
-
*/
|
|
118
|
-
const timerFormat = time => {
|
|
119
|
-
const seconds = Math.floor(time % 60);
|
|
120
|
-
const minutes = Math.floor(time / 60) % 60;
|
|
121
|
-
const hours = Math.floor(time / 3600);
|
|
122
|
-
const parts = [];
|
|
123
|
-
|
|
124
|
-
if (hours) {
|
|
125
|
-
parts.push(hours);
|
|
126
|
-
}
|
|
127
|
-
parts.push(leadingZero(minutes, 2));
|
|
128
|
-
parts.push(leadingZero(seconds, 2));
|
|
129
|
-
|
|
130
|
-
return parts.join(':');
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Checks if a type needs to be adjusted
|
|
135
|
-
* @param {String} type
|
|
136
|
-
* @returns {Boolean}
|
|
137
|
-
*/
|
|
138
|
-
const needTypeAdjust = type => {
|
|
139
|
-
return 'string' === typeof type && type.indexOf('application') === 0;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Adjust bad type by apllying heuristic on URI
|
|
144
|
-
* @param {Object|String} source
|
|
145
|
-
* @returns {String}
|
|
146
|
-
*/
|
|
147
|
-
const getAdjustedType = source => {
|
|
148
|
-
let type = 'video/ogg';
|
|
149
|
-
const url = (source && source.src) || source;
|
|
150
|
-
const ext = url && url.substr(-4);
|
|
151
|
-
if (ext === '.ogg' || ext === '.oga') {
|
|
152
|
-
type = 'audio/ogg';
|
|
153
|
-
}
|
|
154
|
-
return type;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Extract a list of media sources from a config object
|
|
159
|
-
* @param {Object} config
|
|
160
|
-
* @returns {Array}
|
|
161
|
-
*/
|
|
162
|
-
const configToSources = config => {
|
|
163
|
-
let sources = config.sources || [];
|
|
164
|
-
let url = config.url;
|
|
165
|
-
|
|
166
|
-
if (!_.isArray(sources)) {
|
|
167
|
-
sources = [sources];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (url) {
|
|
171
|
-
if (!_.isArray(config.url)) {
|
|
172
|
-
url = [url];
|
|
173
|
-
}
|
|
174
|
-
sources = sources.concat(url);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return sources;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Checks if the browser can play media
|
|
182
|
-
* @param {String} sizeProps Width or Height
|
|
183
|
-
* @returns {Boolean}
|
|
184
|
-
*/
|
|
185
|
-
const isResponsiveSize = sizeProps => {
|
|
186
|
-
return /%/.test(sizeProps) || sizeProps === 'auto';
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Builds a media player instance
|
|
191
|
-
* @param {Object} config
|
|
192
|
-
* @param {String} config.type - The type of media to play, say `audio`, `video`, or `youtube`. The default is `video`.
|
|
193
|
-
* It might also contain the MIME type of the media as a shorthand.
|
|
194
|
-
* @param {String|Array} [config.url] - The URL to the media. If several media are proposed as alternatives,
|
|
195
|
-
* please look at the `sources` option instead.
|
|
196
|
-
* @param {String} [config.mimeType] - The MIME type of the media. If omitted, the player will try to extract it
|
|
197
|
-
* from the `type` property, otherwise it will request the server to get the content-type.
|
|
198
|
-
* @param {Array} [config.sources] - A list of URL if several media can be proposed. Each entry may be either a
|
|
199
|
-
* string (single URL), or an object containing both the URL and the MIME type ({src: string, type: string}).
|
|
200
|
-
* @param {String|jQuery|HTMLElement} [config.renderTo] - An optional container in which renders the player
|
|
201
|
-
* @param {Boolean} [config.canSeek] - The player allows to reach an arbitrary position within the media using the duration bar
|
|
202
|
-
* @param {Boolean} [config.loop] - The media will be played continuously
|
|
203
|
-
* @param {Boolean} [config.canPause] - The player can be paused
|
|
204
|
-
* @param {Boolean} [config.startMuted] - The player should be initially muted
|
|
205
|
-
* @param {Boolean} [config.autoStart] - The player starts as soon as it is displayed
|
|
206
|
-
* @param {Number} [config.autoStartAt] - The time position at which the player should start
|
|
207
|
-
* @param {Number} [config.maxPlays] - Sets a few number of plays (default: infinite)
|
|
208
|
-
* @param {Number} [config.replayTimeout] - disable the possibility to replay a media after this timeout, in seconds (default: 0)
|
|
209
|
-
* @param {Number} [config.volume] - Sets the sound volume (default: 80)
|
|
210
|
-
* @param {Number} [config.width] - Sets the width of the player (default: depends on media type)
|
|
211
|
-
* @param {Number} [config.height] - Sets the height of the player (default: depends on media type)
|
|
212
|
-
* @param {Boolean} [config.preview] - Enables the media preview (load media metadata)
|
|
213
|
-
* @param {Boolean} [config.debug] - Enables the debug mode
|
|
214
|
-
* @param {number} [config.config.stalledDetectionDelay] - The delay before considering a media is stalled
|
|
215
|
-
* @event render - Event triggered when the player is rendering
|
|
216
|
-
* @event error - Event triggered when the player throws an unrecoverable error
|
|
217
|
-
* @event ready - Event triggered when the player is fully ready
|
|
218
|
-
* @event play - Event triggered when the playback is starting
|
|
219
|
-
* @event update - Event triggered while the player is playing
|
|
220
|
-
* @event pause - Event triggered when the playback is paused
|
|
221
|
-
* @event ended - Event triggered when the playback is ended
|
|
222
|
-
* @event limitreached - Event triggered when the play limit has been reached
|
|
223
|
-
* @event destroy - Event triggered when the player is destroying
|
|
224
|
-
* @returns {mediaplayer}
|
|
225
|
-
*/
|
|
226
|
-
function mediaplayerFactory(config) {
|
|
227
|
-
/**
|
|
228
|
-
* Defines a media player object
|
|
229
|
-
* @type {Object}
|
|
230
|
-
*/
|
|
231
|
-
const mediaplayer = {
|
|
232
|
-
/**
|
|
233
|
-
* Initializes the media player
|
|
234
|
-
* @param {Object} mediaPlayerConfig
|
|
235
|
-
* @returns {mediaplayer}
|
|
236
|
-
*/
|
|
237
|
-
init(mediaPlayerConfig) {
|
|
238
|
-
// load the config set, discard null values in order to allow defaults to be set
|
|
239
|
-
this.config = _.omit(mediaPlayerConfig || {}, value => typeof value === 'undefined' || value === null);
|
|
240
|
-
_.defaults(this.config, defaults.options);
|
|
241
|
-
if (!this.config.mimeType && 'string' === typeof this.config.type && this.config.type.indexOf('/') > 0) {
|
|
242
|
-
this.config.mimeType = this.config.type;
|
|
243
|
-
}
|
|
244
|
-
this._setType(this.config.type || defaults.type);
|
|
245
|
-
|
|
246
|
-
this._reset();
|
|
247
|
-
this._updateVolumeFromStore();
|
|
248
|
-
this._initEvents();
|
|
249
|
-
this._initSources(() => {
|
|
250
|
-
if (!this.is('youtube')) {
|
|
251
|
-
_.forEach(this.config.sources, source => {
|
|
252
|
-
if (source && source.type && source.type.indexOf('audio') === 0) {
|
|
253
|
-
this._setType(source.type);
|
|
254
|
-
this._initType();
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
if (this.config.renderTo) {
|
|
260
|
-
_.defer(() => this.render());
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
return this;
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Uninstalls the media player
|
|
269
|
-
* @returns {mediaplayer}
|
|
270
|
-
*/
|
|
271
|
-
destroy() {
|
|
272
|
-
/**
|
|
273
|
-
* Triggers a destroy event
|
|
274
|
-
* @event mediaplayer#destroy
|
|
275
|
-
*/
|
|
276
|
-
this.trigger('destroy');
|
|
277
|
-
|
|
278
|
-
if (this.player) {
|
|
279
|
-
this.player.destroy();
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (this.$component) {
|
|
283
|
-
this._unbindEvents();
|
|
284
|
-
this._destroySlider(this.$seekSlider);
|
|
285
|
-
this._destroySlider(this.$volumeSlider);
|
|
286
|
-
|
|
287
|
-
this.$component.remove();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
this._reset();
|
|
291
|
-
|
|
292
|
-
return this;
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Renders the media player according to the media type
|
|
297
|
-
* @param {String|jQuery|HTMLElement} [to]
|
|
298
|
-
* @returns {mediaplayer}
|
|
299
|
-
*/
|
|
300
|
-
render(to) {
|
|
301
|
-
const renderTo = to || this.config.renderTo || this.$container;
|
|
302
|
-
|
|
303
|
-
if (this.$component) {
|
|
304
|
-
this.destroy();
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
this._initState();
|
|
308
|
-
this._buildDom();
|
|
309
|
-
if (this.config.preview) {
|
|
310
|
-
this._updateDuration(0);
|
|
311
|
-
this._updatePosition(0);
|
|
312
|
-
}
|
|
313
|
-
this._bindEvents();
|
|
314
|
-
this._playingState(false, true);
|
|
315
|
-
this._initPlayer();
|
|
316
|
-
this._initSize();
|
|
317
|
-
|
|
318
|
-
// Resize for old items with defined height to avoid big jump
|
|
319
|
-
if (this.config.height && this.config.height !== 'auto') {
|
|
320
|
-
this.resize('100%', 'auto');
|
|
321
|
-
} else {
|
|
322
|
-
this.resize(this.config.width, this.config.height);
|
|
323
|
-
}
|
|
324
|
-
this.config.is.rendered = true;
|
|
325
|
-
|
|
326
|
-
if (renderTo) {
|
|
327
|
-
this.$container = $(renderTo).append(this.$component);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// add class if it is stalled
|
|
331
|
-
if (this.is('stalled')) {
|
|
332
|
-
this._setState('stalled', true);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Triggers a render event
|
|
337
|
-
* @event mediaplayer#render
|
|
338
|
-
* @param {jQuery} $component
|
|
339
|
-
*/
|
|
340
|
-
this.trigger('render', this.$component);
|
|
341
|
-
|
|
342
|
-
return this;
|
|
343
|
-
},
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Reloads media player after it was stalled
|
|
347
|
-
*/
|
|
348
|
-
reload() {
|
|
349
|
-
/**
|
|
350
|
-
* Triggers a reload event
|
|
351
|
-
* @event mediaplayer#reload
|
|
352
|
-
*/
|
|
353
|
-
this.trigger('reload');
|
|
354
|
-
|
|
355
|
-
if (this.player) {
|
|
356
|
-
this.player.recover();
|
|
357
|
-
}
|
|
358
|
-
this._setState('stalled', false);
|
|
359
|
-
this.setInitialStates();
|
|
360
|
-
},
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Set initial states
|
|
364
|
-
*/
|
|
365
|
-
setInitialStates() {
|
|
366
|
-
if (!this.is('stalled')) {
|
|
367
|
-
this._setState('ready', true);
|
|
368
|
-
}
|
|
369
|
-
this._setState('canplay', true);
|
|
370
|
-
this._setState('canpause', this.config.canPause);
|
|
371
|
-
this._setState('canseek', this.config.canSeek);
|
|
372
|
-
this._setState('loading', false);
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Sets the start position inside the media
|
|
377
|
-
* @param {Number} time - The start position in seconds
|
|
378
|
-
* @param {*} [internal] - Internal use
|
|
379
|
-
* @returns {mediaplayer}
|
|
380
|
-
*/
|
|
381
|
-
seek(time, internal) {
|
|
382
|
-
if (this._canPlay()) {
|
|
383
|
-
this._updatePosition(time, internal);
|
|
384
|
-
|
|
385
|
-
this.execute('seek', this.position);
|
|
386
|
-
|
|
387
|
-
if (!this.is('ready')) {
|
|
388
|
-
this.autoStartAt = this.position;
|
|
389
|
-
}
|
|
390
|
-
this.loop = !!this.config.loop;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return this;
|
|
394
|
-
},
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Plays the media
|
|
398
|
-
* @param {Number} [time] - An optional start position in seconds
|
|
399
|
-
* @returns {mediaplayer}
|
|
400
|
-
*/
|
|
401
|
-
play(time) {
|
|
402
|
-
if (this._canPlay()) {
|
|
403
|
-
if (typeof time !== 'undefined') {
|
|
404
|
-
this.seek(time);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
this.execute('play');
|
|
408
|
-
|
|
409
|
-
if (!this.is('ready')) {
|
|
410
|
-
this.autoStart = true;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
this.loop = !!this.config.loop;
|
|
414
|
-
|
|
415
|
-
if (this.timerId) {
|
|
416
|
-
cancelAnimationFrame(this.timerId);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return this;
|
|
421
|
-
},
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Pauses the media
|
|
425
|
-
* @param {Number} [time] - An optional time position in seconds
|
|
426
|
-
* @returns {mediaplayer}
|
|
427
|
-
*/
|
|
428
|
-
pause(time) {
|
|
429
|
-
if (this._canPause()) {
|
|
430
|
-
if (typeof time !== 'undefined') {
|
|
431
|
-
this.seek(time);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
this.execute('pause');
|
|
435
|
-
|
|
436
|
-
if (!this.is('ready')) {
|
|
437
|
-
this.autoStart = false;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return this;
|
|
442
|
-
},
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Resumes the media
|
|
446
|
-
* @returns {mediaplayer}
|
|
447
|
-
*/
|
|
448
|
-
resume() {
|
|
449
|
-
if (this._canResume()) {
|
|
450
|
-
this.play();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return this;
|
|
454
|
-
},
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Stops the playback
|
|
458
|
-
* @returns {mediaplayer}
|
|
459
|
-
*/
|
|
460
|
-
stop() {
|
|
461
|
-
this.loop = false;
|
|
462
|
-
this.execute('stop');
|
|
463
|
-
|
|
464
|
-
if (!this.is('ready')) {
|
|
465
|
-
this.autoStart = false;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
return this;
|
|
469
|
-
},
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* Starts the media
|
|
473
|
-
* @returns {mediaplayer}
|
|
474
|
-
*/
|
|
475
|
-
start() {
|
|
476
|
-
this._setState('preview', true);
|
|
477
|
-
this._setState('loading', true);
|
|
478
|
-
this.play();
|
|
479
|
-
},
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Restarts the media from the beginning
|
|
483
|
-
* @returns {mediaplayer}
|
|
484
|
-
*/
|
|
485
|
-
restart() {
|
|
486
|
-
this.play(0);
|
|
487
|
-
|
|
488
|
-
return this;
|
|
489
|
-
},
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Rewind the media to the beginning
|
|
493
|
-
* @returns {mediaplayer}
|
|
494
|
-
*/
|
|
495
|
-
rewind() {
|
|
496
|
-
this.seek(0);
|
|
497
|
-
|
|
498
|
-
return this;
|
|
499
|
-
},
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Mutes the media
|
|
503
|
-
* @param {Boolean} [state] - A flag to set the mute state (default: true)
|
|
504
|
-
* @returns {mediaplayer}
|
|
505
|
-
*/
|
|
506
|
-
mute(state) {
|
|
507
|
-
if (typeof state === 'undefined') {
|
|
508
|
-
state = true;
|
|
509
|
-
}
|
|
510
|
-
this.execute('mute', state);
|
|
511
|
-
this._setState('muted', state);
|
|
512
|
-
|
|
513
|
-
if (!this.is('ready')) {
|
|
514
|
-
this.startMuted = state;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return this;
|
|
518
|
-
},
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Restore the sound of the media after a mute
|
|
522
|
-
* @returns {mediaplayer}
|
|
523
|
-
*/
|
|
524
|
-
unmute() {
|
|
525
|
-
this.mute(false);
|
|
526
|
-
|
|
527
|
-
return this;
|
|
528
|
-
},
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* Sets the sound volume of the media being played
|
|
532
|
-
* @param {Number} value - A value between 0 and 100
|
|
533
|
-
* @param {*} [internal] - Internal use
|
|
534
|
-
* @returns {mediaplayer}
|
|
535
|
-
*/
|
|
536
|
-
setVolume(value, internal) {
|
|
537
|
-
this._updateVolume(value, internal);
|
|
538
|
-
|
|
539
|
-
this.execute('setVolume', this.volume);
|
|
540
|
-
|
|
541
|
-
return this;
|
|
542
|
-
},
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Gets the sound volume applied to the media being played
|
|
546
|
-
* @returns {Number} Returns a value between 0 and 100
|
|
547
|
-
*/
|
|
548
|
-
getVolume() {
|
|
549
|
-
return this.volume;
|
|
550
|
-
},
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Gets the current displayed position inside the media
|
|
554
|
-
* @returns {Number}
|
|
555
|
-
*/
|
|
556
|
-
getPosition() {
|
|
557
|
-
return this.position;
|
|
558
|
-
},
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Gets the duration of the media
|
|
562
|
-
* @returns {Number}
|
|
563
|
-
*/
|
|
564
|
-
getDuration() {
|
|
565
|
-
return this.duration;
|
|
566
|
-
},
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Gets the number of times the media has been played
|
|
570
|
-
* @returns {Number}
|
|
571
|
-
*/
|
|
572
|
-
getTimesPlayed() {
|
|
573
|
-
return this.timesPlayed;
|
|
574
|
-
},
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Gets the type of player
|
|
578
|
-
* @returns {String}
|
|
579
|
-
*/
|
|
580
|
-
getType() {
|
|
581
|
-
return this.type;
|
|
582
|
-
},
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Gets the DOM container
|
|
586
|
-
* @returns {jQuery}
|
|
587
|
-
*/
|
|
588
|
-
getContainer() {
|
|
589
|
-
if (!this.$container && this.$component) {
|
|
590
|
-
let $container = this.$component.parent();
|
|
591
|
-
if ($container.length) {
|
|
592
|
-
this.$container = $container;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return this.$container;
|
|
596
|
-
},
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Gets the underlying DOM element
|
|
600
|
-
* @returns {jQuery}
|
|
601
|
-
*/
|
|
602
|
-
getElement() {
|
|
603
|
-
return this.$component;
|
|
604
|
-
},
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Gets the list of media
|
|
608
|
-
* @returns {Array}
|
|
609
|
-
*/
|
|
610
|
-
getSources() {
|
|
611
|
-
return this.config.sources.slice();
|
|
612
|
-
},
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Sets the media source. If a source has been already set, it will be replaced.
|
|
616
|
-
* @param {String|Object} src - The media URL, or an object containing the source and the type
|
|
617
|
-
* @param {Function} [callback] - A function called to provide the added media source object
|
|
618
|
-
* @returns {mediaplayer}
|
|
619
|
-
*/
|
|
620
|
-
setSource(src, callback) {
|
|
621
|
-
this._getSource(src, source => {
|
|
622
|
-
this.config.sources = [source];
|
|
623
|
-
|
|
624
|
-
if (this.is('rendered')) {
|
|
625
|
-
this.player.setMedia(source.src, source.type);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
if (callback) {
|
|
629
|
-
callback.call(this, source);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
return this;
|
|
634
|
-
},
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* Adds a media source.
|
|
638
|
-
* @param {String|Object} src - The media URL, or an object containing the source and the type
|
|
639
|
-
* @param {Function} [callback] - A function called to provide the added media source object
|
|
640
|
-
* @returns {mediaplayer}
|
|
641
|
-
*/
|
|
642
|
-
addSource(src, callback) {
|
|
643
|
-
this._getSource(src, source => {
|
|
644
|
-
this.config.sources.push(source);
|
|
645
|
-
|
|
646
|
-
if (this.is('rendered')) {
|
|
647
|
-
this.player.addMedia(source.src, source.type);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
if (callback) {
|
|
651
|
-
callback.call(this, source);
|
|
652
|
-
}
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
return this;
|
|
656
|
-
},
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* Tells if the media is in a particular state
|
|
660
|
-
* @param {String} state
|
|
661
|
-
* @returns {Boolean}
|
|
662
|
-
*/
|
|
663
|
-
is(state) {
|
|
664
|
-
return !!this.config.is[state];
|
|
665
|
-
},
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Changes the size of the player
|
|
669
|
-
* @param {Number} width
|
|
670
|
-
* @param {Number} height
|
|
671
|
-
* @returns {mediaplayer}
|
|
672
|
-
*/
|
|
673
|
-
resize(width, height) {
|
|
674
|
-
if ((isResponsiveSize(width) && !isResponsiveSize(height)) || this.is('youtube')) {
|
|
675
|
-
// responsive width height should be auto
|
|
676
|
-
// for youtube iframe height is limited by ration
|
|
677
|
-
height = 'auto';
|
|
678
|
-
}
|
|
679
|
-
this.execute('setSize', width, height);
|
|
680
|
-
|
|
681
|
-
return this;
|
|
682
|
-
},
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Enables the media player
|
|
686
|
-
* @returns {mediaplayer}
|
|
687
|
-
*/
|
|
688
|
-
enable() {
|
|
689
|
-
this._fromState('disabled');
|
|
690
|
-
|
|
691
|
-
return this;
|
|
692
|
-
},
|
|
693
|
-
|
|
694
|
-
/**
|
|
695
|
-
* Disables the media player
|
|
696
|
-
* @returns {mediaplayer}
|
|
697
|
-
*/
|
|
698
|
-
disable() {
|
|
699
|
-
this._toState('disabled');
|
|
700
|
-
this.trigger('disabled');
|
|
701
|
-
|
|
702
|
-
return this;
|
|
703
|
-
},
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Shows the media player
|
|
707
|
-
* @returns {mediaplayer}
|
|
708
|
-
*/
|
|
709
|
-
show() {
|
|
710
|
-
this._fromState('hidden');
|
|
711
|
-
|
|
712
|
-
return this;
|
|
713
|
-
},
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* hides the media player
|
|
717
|
-
* @returns {mediaplayer}
|
|
718
|
-
*/
|
|
719
|
-
hide() {
|
|
720
|
-
this._toState('hidden');
|
|
721
|
-
|
|
722
|
-
return this;
|
|
723
|
-
},
|
|
724
|
-
/**
|
|
725
|
-
* get media original size
|
|
726
|
-
* @returns {Object}
|
|
727
|
-
*/
|
|
728
|
-
getMediaOriginalSize() {
|
|
729
|
-
if (this.is('youtube')) {
|
|
730
|
-
return defaults.youtube;
|
|
731
|
-
}
|
|
732
|
-
if (this.is('video') && this.player) {
|
|
733
|
-
return this.player.getMediaSize();
|
|
734
|
-
}
|
|
735
|
-
return {};
|
|
736
|
-
},
|
|
737
|
-
/**
|
|
738
|
-
* Ensures the right media type is set
|
|
739
|
-
* @param {String} type
|
|
740
|
-
* @private
|
|
741
|
-
*/
|
|
742
|
-
_setType(type) {
|
|
743
|
-
if (type.indexOf('youtube') !== -1) {
|
|
744
|
-
this.type = 'youtube';
|
|
745
|
-
} else if (type.indexOf('audio') === 0) {
|
|
746
|
-
this.type = 'audio';
|
|
747
|
-
} else {
|
|
748
|
-
this.type = 'video';
|
|
749
|
-
}
|
|
750
|
-
},
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Ensures the type is correctly applied
|
|
754
|
-
* @private
|
|
755
|
-
*/
|
|
756
|
-
_initType() {
|
|
757
|
-
const is = this.config.is;
|
|
758
|
-
is.youtube = 'youtube' === this.type;
|
|
759
|
-
is.video = 'video' === this.type || 'youtube' === this.type;
|
|
760
|
-
is.audio = 'audio' === this.type;
|
|
761
|
-
},
|
|
762
|
-
|
|
763
|
-
/**
|
|
764
|
-
* Gets a source descriptor.
|
|
765
|
-
* @param {String|Object} src - The media URL, or an object containing the source and the type
|
|
766
|
-
* @param {Function} callback - A function called to provide the media source object
|
|
767
|
-
*/
|
|
768
|
-
_getSource(src, callback) {
|
|
769
|
-
let source;
|
|
770
|
-
const done = () => {
|
|
771
|
-
if (needTypeAdjust(source.type)) {
|
|
772
|
-
source.type = getAdjustedType(source);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
callback.call(this, source);
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
if (_.isString(src)) {
|
|
779
|
-
source = {
|
|
780
|
-
src: src
|
|
781
|
-
};
|
|
782
|
-
} else {
|
|
783
|
-
source = _.clone(src);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
if (!source.type) {
|
|
787
|
-
if (this.is('youtube')) {
|
|
788
|
-
source.type = defaults.type;
|
|
789
|
-
} else if (this.config.mimeType) {
|
|
790
|
-
source.type = this.config.mimeType;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
if (!source.type) {
|
|
795
|
-
mimetype.getResourceType(source.src, (err, type) => {
|
|
796
|
-
if (err) {
|
|
797
|
-
type = defaults.type;
|
|
798
|
-
}
|
|
799
|
-
source.type = type;
|
|
800
|
-
done();
|
|
801
|
-
});
|
|
802
|
-
} else {
|
|
803
|
-
done();
|
|
804
|
-
}
|
|
805
|
-
},
|
|
806
|
-
|
|
807
|
-
/**
|
|
808
|
-
* Ensures the sources are correctly set
|
|
809
|
-
* @param {Function} callback - A function called once all sources have been initialized
|
|
810
|
-
* @private
|
|
811
|
-
*/
|
|
812
|
-
_initSources(callback) {
|
|
813
|
-
const sources = configToSources(this.config);
|
|
814
|
-
|
|
815
|
-
this.config.sources = [];
|
|
816
|
-
|
|
817
|
-
async.each(
|
|
818
|
-
sources,
|
|
819
|
-
(source, cb) => {
|
|
820
|
-
this.addSource(source, src => cb(null, src));
|
|
821
|
-
},
|
|
822
|
-
callback
|
|
823
|
-
);
|
|
824
|
-
},
|
|
825
|
-
|
|
826
|
-
/**
|
|
827
|
-
* Installs the events manager onto the instance
|
|
828
|
-
* @private
|
|
829
|
-
*/
|
|
830
|
-
_initEvents() {
|
|
831
|
-
eventifier(this);
|
|
832
|
-
|
|
833
|
-
const triggerEvent = this.trigger;
|
|
834
|
-
this.trigger = function trigger(eventName, ...args) {
|
|
835
|
-
if (this.$component) {
|
|
836
|
-
this.$component.trigger(eventName + ns, ...args);
|
|
837
|
-
}
|
|
838
|
-
return triggerEvent.call(this, eventName, ...args);
|
|
839
|
-
};
|
|
840
|
-
},
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Ensures the right size is set according to the media type
|
|
844
|
-
* @private
|
|
845
|
-
*/
|
|
846
|
-
_initSize() {
|
|
847
|
-
const type = this.is('video') ? 'video' : 'audio';
|
|
848
|
-
const mediaConfig = defaults[type] || defaults.video;
|
|
849
|
-
|
|
850
|
-
this.config.width = this.config.width || mediaConfig.width;
|
|
851
|
-
this.config.height = this.config.height || mediaConfig.height;
|
|
852
|
-
|
|
853
|
-
if ((isResponsiveSize(this.config.width) && !isResponsiveSize(this.config.height)) || this.is('youtube')) {
|
|
854
|
-
// responsive width height should be auto
|
|
855
|
-
// for youtube iframe height is limited by ration
|
|
856
|
-
this.config.height = 'auto';
|
|
857
|
-
}
|
|
858
|
-
},
|
|
859
|
-
|
|
860
|
-
/**
|
|
861
|
-
* Initializes the right player instance
|
|
862
|
-
* @private
|
|
863
|
-
*/
|
|
864
|
-
_initPlayer() {
|
|
865
|
-
const playerFactory = players[this.type];
|
|
866
|
-
let error;
|
|
867
|
-
|
|
868
|
-
if (support.canPlay(this.type)) {
|
|
869
|
-
if (_.isFunction(playerFactory)) {
|
|
870
|
-
const playerConfig = {
|
|
871
|
-
type: this.getType(),
|
|
872
|
-
sources: this.getSources(),
|
|
873
|
-
preview: this.config.preview,
|
|
874
|
-
debug: this.config.debug,
|
|
875
|
-
stalledDetectionDelay: this.config.stalledDetectionDelay
|
|
876
|
-
};
|
|
877
|
-
this.player = playerFactory(this.$player, playerConfig)
|
|
878
|
-
.on('resize', (width, height) => {
|
|
879
|
-
if (this.$component) {
|
|
880
|
-
this.$component.width(width).height(height);
|
|
881
|
-
}
|
|
882
|
-
})
|
|
883
|
-
.on('ready', () => this._onReady())
|
|
884
|
-
.on('play', () => this._onPlay())
|
|
885
|
-
.on('pause', () => this._onPause())
|
|
886
|
-
.on('timeupdate', () => this._onTimeUpdate())
|
|
887
|
-
.on('stalled', () => this._onStalled())
|
|
888
|
-
.on('playing', () => this._onPlaying())
|
|
889
|
-
.on('end', () => this._onEnd())
|
|
890
|
-
.on('error', () => this._onError());
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
if (this.player) {
|
|
894
|
-
error = !this.player.init();
|
|
895
|
-
} else {
|
|
896
|
-
error = true;
|
|
897
|
-
}
|
|
898
|
-
} else {
|
|
899
|
-
error = true;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
this._setState('error', error);
|
|
903
|
-
this._setState('nogui', !support.canControl());
|
|
904
|
-
this._setState('preview', this.config.preview);
|
|
905
|
-
this._setState('loading', !error);
|
|
906
|
-
if (error) {
|
|
907
|
-
this._setState('ready', true);
|
|
908
|
-
this.trigger('ready');
|
|
909
|
-
}
|
|
910
|
-
},
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* Initializes the player state
|
|
914
|
-
* @private
|
|
915
|
-
*/
|
|
916
|
-
_initState() {
|
|
917
|
-
let isCORS = false;
|
|
918
|
-
let page;
|
|
919
|
-
|
|
920
|
-
if (!this.is('youtube')) {
|
|
921
|
-
page = new UrlParser(window.location);
|
|
922
|
-
isCORS = _.some(this.config.sources, source => !page.sameDomain(source.src));
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
this._setState('cors', isCORS);
|
|
926
|
-
this._setState('ready', false);
|
|
927
|
-
},
|
|
928
|
-
|
|
929
|
-
/**
|
|
930
|
-
* Resets the internals attributes
|
|
931
|
-
* @private
|
|
932
|
-
*/
|
|
933
|
-
_reset() {
|
|
934
|
-
this.config.is = {};
|
|
935
|
-
this._initType();
|
|
936
|
-
|
|
937
|
-
this.$component = null;
|
|
938
|
-
this.$container = null;
|
|
939
|
-
this.$player = null;
|
|
940
|
-
this.$controls = null;
|
|
941
|
-
this.$seek = null;
|
|
942
|
-
this.$seekSlider = null;
|
|
943
|
-
this.$sound = null;
|
|
944
|
-
this.$volume = null;
|
|
945
|
-
this.$volumeControl = null;
|
|
946
|
-
this.$volumeSlider = null;
|
|
947
|
-
this.$position = null;
|
|
948
|
-
this.$duration = null;
|
|
949
|
-
this.player = null;
|
|
950
|
-
|
|
951
|
-
this.duration = 0;
|
|
952
|
-
this.position = 0;
|
|
953
|
-
this.timesPlayed = 0;
|
|
954
|
-
|
|
955
|
-
this.volume = this.config.volume;
|
|
956
|
-
this.autoStart = this.config.autoStart;
|
|
957
|
-
this.autoStartAt = this.config.autoStartAt;
|
|
958
|
-
this.startMuted = this.config.startMuted;
|
|
959
|
-
},
|
|
960
|
-
|
|
961
|
-
/**
|
|
962
|
-
* Builds the DOM content
|
|
963
|
-
* @private
|
|
964
|
-
*/
|
|
965
|
-
_buildDom() {
|
|
966
|
-
const configForTemplate = _.clone(this.config);
|
|
967
|
-
configForTemplate.type = this.type;
|
|
968
|
-
this.$component = $(playerTpl(configForTemplate));
|
|
969
|
-
this.$player = this.$component.find('.player');
|
|
970
|
-
this.$controls = this.$component.find('.controls');
|
|
971
|
-
|
|
972
|
-
this.$seek = this.$controls.find('.seek .slider');
|
|
973
|
-
this.$sound = this.$controls.find('.sound');
|
|
974
|
-
this.$volumeControl = this.$controls.find('.volume');
|
|
975
|
-
this.$volume = this.$controls.find('.volume .slider');
|
|
976
|
-
this.$position = this.$controls.find('[data-control="time-cur"]');
|
|
977
|
-
this.$duration = this.$controls.find('[data-control="time-end"]');
|
|
978
|
-
|
|
979
|
-
this.$volumeSlider = this._renderSlider(this.$volume, this.volume, volumeMin, volumeMax, true);
|
|
980
|
-
},
|
|
981
|
-
|
|
982
|
-
/**
|
|
983
|
-
* Renders a slider onto an element
|
|
984
|
-
* @param {jQuery} $elt - The element on which renders the slider
|
|
985
|
-
* @param {Number} [value] - The current value of the slider
|
|
986
|
-
* @param {Number} [min] - The min value of the slider
|
|
987
|
-
* @param {Number} [max] - The max value of the slider
|
|
988
|
-
* @param {Boolean} [vertical] - Tells if the slider must be vertical
|
|
989
|
-
* @returns {jQuery} - Returns the element
|
|
990
|
-
* @private
|
|
991
|
-
*/
|
|
992
|
-
_renderSlider($elt, value, min, max, vertical) {
|
|
993
|
-
let orientation, direction;
|
|
994
|
-
|
|
995
|
-
if (vertical) {
|
|
996
|
-
orientation = 'vertical';
|
|
997
|
-
direction = 'rtl';
|
|
998
|
-
} else {
|
|
999
|
-
orientation = 'horizontal';
|
|
1000
|
-
direction = 'ltr';
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
return $elt.noUiSlider({
|
|
1004
|
-
start: ensureNumber(value) || 0,
|
|
1005
|
-
step: 1,
|
|
1006
|
-
connect: 'lower',
|
|
1007
|
-
orientation: orientation,
|
|
1008
|
-
direction: direction,
|
|
1009
|
-
animate: true,
|
|
1010
|
-
range: {
|
|
1011
|
-
min: ensureNumber(min) || 0,
|
|
1012
|
-
max: ensureNumber(max) || 0
|
|
1013
|
-
}
|
|
1014
|
-
});
|
|
1015
|
-
},
|
|
1016
|
-
|
|
1017
|
-
/**
|
|
1018
|
-
* Destroys a slider bound to an element
|
|
1019
|
-
* @param {jQuery} $elt
|
|
1020
|
-
* @private
|
|
1021
|
-
*/
|
|
1022
|
-
_destroySlider($elt) {
|
|
1023
|
-
if ($elt) {
|
|
1024
|
-
$elt.get(0).destroy();
|
|
1025
|
-
}
|
|
1026
|
-
},
|
|
1027
|
-
|
|
1028
|
-
/**
|
|
1029
|
-
* Binds events onto the rendered player
|
|
1030
|
-
* @private
|
|
1031
|
-
*/
|
|
1032
|
-
_bindEvents() {
|
|
1033
|
-
let overing = false;
|
|
1034
|
-
|
|
1035
|
-
this.$component.on(`contextmenu${ns}`, event => event.preventDefault());
|
|
1036
|
-
|
|
1037
|
-
this.$controls.on(`click${ns}`, '.action', event => {
|
|
1038
|
-
const $target = $(event.target);
|
|
1039
|
-
const $action = $target.closest('.action');
|
|
1040
|
-
const id = $action.data('control');
|
|
1041
|
-
|
|
1042
|
-
if (_.isFunction(this[id])) {
|
|
1043
|
-
this[id]();
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
this.$player.on(`click${ns}`, event => {
|
|
1048
|
-
const $target = $(event.target);
|
|
1049
|
-
const $action = $target.closest('.action');
|
|
1050
|
-
|
|
1051
|
-
// if action was clicked
|
|
1052
|
-
if ($action.length) {
|
|
1053
|
-
const id = $action.data('control');
|
|
1054
|
-
if (_.isFunction(this[id])) {
|
|
1055
|
-
this[id]();
|
|
1056
|
-
}
|
|
1057
|
-
} else {
|
|
1058
|
-
// default action is toggle play
|
|
1059
|
-
if (this.is('playing')) {
|
|
1060
|
-
this.pause();
|
|
1061
|
-
} else {
|
|
1062
|
-
this.play();
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
});
|
|
1066
|
-
|
|
1067
|
-
this.$seek.on(`change${ns}`, (event, value) => {
|
|
1068
|
-
this.seek(value, true);
|
|
1069
|
-
});
|
|
1070
|
-
|
|
1071
|
-
$(document).on(`updateVolume${ns}`, (event, value) => {
|
|
1072
|
-
this.setVolume(value);
|
|
1073
|
-
});
|
|
1074
|
-
|
|
1075
|
-
this.$volume.on(`change${ns}`, (event, value) => {
|
|
1076
|
-
this.unmute();
|
|
1077
|
-
$(document).trigger(`updateVolume${ns}`, value);
|
|
1078
|
-
this.setVolume(value, true);
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
this.$sound.on(`mouseover${ns}`, 'a', () => {
|
|
1082
|
-
let position;
|
|
1083
|
-
|
|
1084
|
-
if (!overing && !this.$volumeControl.hasClass('up') && !this.$volumeControl.hasClass('down')) {
|
|
1085
|
-
overing = true;
|
|
1086
|
-
position = this.$controls[0].getBoundingClientRect();
|
|
1087
|
-
if (position && position.top && position.top < volumePositionThreshold) {
|
|
1088
|
-
this.$volumeControl.addClass('down');
|
|
1089
|
-
} else {
|
|
1090
|
-
this.$volumeControl.addClass('up');
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
//close the volume control after 15s
|
|
1094
|
-
this.overingTimer = _.delay(() => {
|
|
1095
|
-
if (this.$volumeControl) {
|
|
1096
|
-
this.$volumeControl.removeClass('up down');
|
|
1097
|
-
}
|
|
1098
|
-
overing = false;
|
|
1099
|
-
}, 15000);
|
|
1100
|
-
this.$volumeControl.one(`mouseleave${ns}`, () => {
|
|
1101
|
-
this.$volumeControl.removeClass('up down');
|
|
1102
|
-
overing = false;
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
});
|
|
1106
|
-
},
|
|
1107
|
-
|
|
1108
|
-
/**
|
|
1109
|
-
* Unbinds events from the rendered player
|
|
1110
|
-
* @private
|
|
1111
|
-
*/
|
|
1112
|
-
_unbindEvents() {
|
|
1113
|
-
this.$component.off(ns);
|
|
1114
|
-
this.$player.off(ns);
|
|
1115
|
-
this.$controls.off(ns);
|
|
1116
|
-
this.$seek.off(ns);
|
|
1117
|
-
this.$volume.off(ns);
|
|
1118
|
-
|
|
1119
|
-
//if the volume is opened and the player destroyed,
|
|
1120
|
-
//prevent the callback to run
|
|
1121
|
-
if (this.overingTimer) {
|
|
1122
|
-
clearTimeout(this.overingTimer);
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
$(document).off(ns);
|
|
1126
|
-
},
|
|
1127
|
-
|
|
1128
|
-
/**
|
|
1129
|
-
* Updates the volume slider
|
|
1130
|
-
* @param {Number} value
|
|
1131
|
-
* @private
|
|
1132
|
-
*/
|
|
1133
|
-
_updateVolumeSlider(value) {
|
|
1134
|
-
if (this.$volumeSlider) {
|
|
1135
|
-
this.$volumeSlider.val(value);
|
|
1136
|
-
}
|
|
1137
|
-
},
|
|
1138
|
-
|
|
1139
|
-
/**
|
|
1140
|
-
* Updates the displayed volume
|
|
1141
|
-
* @param {Number} value
|
|
1142
|
-
* @param {*} [internal]
|
|
1143
|
-
* @private
|
|
1144
|
-
*/
|
|
1145
|
-
_updateVolume(value, internal) {
|
|
1146
|
-
this.volume = Math.max(volumeMin, Math.min(volumeMax, parseFloat(value)));
|
|
1147
|
-
this._storeVolume(this.volume);
|
|
1148
|
-
if (!internal) {
|
|
1149
|
-
this._updateVolumeSlider(value);
|
|
1150
|
-
}
|
|
1151
|
-
},
|
|
1152
|
-
|
|
1153
|
-
/**
|
|
1154
|
-
* Updates the time slider
|
|
1155
|
-
* @param {Number} value
|
|
1156
|
-
* @private
|
|
1157
|
-
*/
|
|
1158
|
-
_updatePositionSlider(value) {
|
|
1159
|
-
if (this.$seekSlider) {
|
|
1160
|
-
this.$seekSlider.val(value);
|
|
1161
|
-
}
|
|
1162
|
-
},
|
|
1163
|
-
|
|
1164
|
-
/**
|
|
1165
|
-
* Updates the time label
|
|
1166
|
-
* @param {Number} value
|
|
1167
|
-
* @private
|
|
1168
|
-
*/
|
|
1169
|
-
_updatePositionLabel(value) {
|
|
1170
|
-
if (this.$position) {
|
|
1171
|
-
this.$position.text(timerFormat(value));
|
|
1172
|
-
}
|
|
1173
|
-
},
|
|
1174
|
-
|
|
1175
|
-
/**
|
|
1176
|
-
* Updates the displayed time position
|
|
1177
|
-
* @param {Number} value
|
|
1178
|
-
* @param {*} [internal]
|
|
1179
|
-
* @private
|
|
1180
|
-
*/
|
|
1181
|
-
_updatePosition(value, internal) {
|
|
1182
|
-
this.position = Math.max(0, Math.min(this.duration || +Infinity, parseFloat(value)));
|
|
1183
|
-
|
|
1184
|
-
if (!internal && this.duration) {
|
|
1185
|
-
this._updatePositionSlider(this.position);
|
|
1186
|
-
}
|
|
1187
|
-
this._updatePositionLabel(this.position);
|
|
1188
|
-
},
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
* Updates the duration slider
|
|
1192
|
-
* @param {Number} value
|
|
1193
|
-
* @private
|
|
1194
|
-
*/
|
|
1195
|
-
_updateDurationSlider(value) {
|
|
1196
|
-
if (this.$seekSlider) {
|
|
1197
|
-
this._destroySlider(this.$seekSlider);
|
|
1198
|
-
this.$seekSlider = null;
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
if (value && isFinite(value)) {
|
|
1202
|
-
this.$seekSlider = this._renderSlider(this.$seek, 0, 0, value);
|
|
1203
|
-
this.$seekSlider.attr('disabled', !this.config.canSeek);
|
|
1204
|
-
}
|
|
1205
|
-
},
|
|
1206
|
-
|
|
1207
|
-
/**
|
|
1208
|
-
* Updates the duration label
|
|
1209
|
-
* @param {Number} value
|
|
1210
|
-
* @private
|
|
1211
|
-
*/
|
|
1212
|
-
_updateDurationLabel(value) {
|
|
1213
|
-
if (this.$duration) {
|
|
1214
|
-
if (value && isFinite(value)) {
|
|
1215
|
-
this.$duration.text(timerFormat(value)).show();
|
|
1216
|
-
} else {
|
|
1217
|
-
this.$duration.hide();
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
},
|
|
1221
|
-
|
|
1222
|
-
/**
|
|
1223
|
-
* Updates the displayed duration
|
|
1224
|
-
* @param {Number|String} value
|
|
1225
|
-
* @private
|
|
1226
|
-
*/
|
|
1227
|
-
_updateDuration(value) {
|
|
1228
|
-
const duration = Math.abs(parseFloat(value));
|
|
1229
|
-
if (duration !== this.duration) {
|
|
1230
|
-
this.duration = duration;
|
|
1231
|
-
this._updateDurationSlider(this.duration);
|
|
1232
|
-
this._updateDurationLabel(this.duration);
|
|
1233
|
-
}
|
|
1234
|
-
},
|
|
1235
|
-
|
|
1236
|
-
/**
|
|
1237
|
-
* Event called when the media is ready
|
|
1238
|
-
* @private
|
|
1239
|
-
*/
|
|
1240
|
-
_onReady() {
|
|
1241
|
-
if (this.is('error')) {
|
|
1242
|
-
this._setState('error', false);
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
const duration = this.player.getDuration();
|
|
1246
|
-
const timePreview = this.config.preview || duration;
|
|
1247
|
-
if (timePreview) {
|
|
1248
|
-
this._updateDuration(duration);
|
|
1249
|
-
}
|
|
1250
|
-
this.setInitialStates();
|
|
1251
|
-
|
|
1252
|
-
/**
|
|
1253
|
-
* Triggers a media ready event
|
|
1254
|
-
* @event mediaplayer#ready
|
|
1255
|
-
*/
|
|
1256
|
-
this.trigger('ready');
|
|
1257
|
-
|
|
1258
|
-
// set the initial state
|
|
1259
|
-
this.setVolume(this.volume);
|
|
1260
|
-
this.mute(!!this.startMuted);
|
|
1261
|
-
if (this.autoStartAt) {
|
|
1262
|
-
this.seek(this.autoStartAt);
|
|
1263
|
-
} else if (this.autoStart) {
|
|
1264
|
-
this.play();
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
if (this.config.preview && this.$container && this.config.height && this.config.height !== 'auto') {
|
|
1268
|
-
this._setMaxHeight();
|
|
1269
|
-
}
|
|
1270
|
-
},
|
|
1271
|
-
|
|
1272
|
-
/**
|
|
1273
|
-
* Set max height limit for container
|
|
1274
|
-
* using by old media items with defined height.
|
|
1275
|
-
* @private
|
|
1276
|
-
*/
|
|
1277
|
-
_setMaxHeight() {
|
|
1278
|
-
const $video = this.$container.find('video.video');
|
|
1279
|
-
const controlsHeight = parseInt(window.getComputedStyle(this.$controls[0]).height);
|
|
1280
|
-
const scale = $video.height() / this.config.height;
|
|
1281
|
-
const playerWidth = this.$container.find('.player').width();
|
|
1282
|
-
const videoWidth = $video.width() / scale;
|
|
1283
|
-
|
|
1284
|
-
if (videoWidth > playerWidth) {
|
|
1285
|
-
this.execute('setSize', '100%', 'auto');
|
|
1286
|
-
} else {
|
|
1287
|
-
this.$component.css({ maxHeight: `${this.config.height + controlsHeight}px` });
|
|
1288
|
-
this.execute('setSize', Math.floor(videoWidth), 'auto');
|
|
1289
|
-
}
|
|
1290
|
-
},
|
|
1291
|
-
|
|
1292
|
-
/**
|
|
1293
|
-
* Update volume in DBIndex store
|
|
1294
|
-
* @param {Number} volume
|
|
1295
|
-
* @returns {Promise}
|
|
1296
|
-
* @private
|
|
1297
|
-
*/
|
|
1298
|
-
_storeVolume(volume) {
|
|
1299
|
-
return store('mediaVolume').then(volumeStore => volumeStore.setItem('volume', volume));
|
|
1300
|
-
},
|
|
1301
|
-
|
|
1302
|
-
/**
|
|
1303
|
-
* Get volume from DBIndex store
|
|
1304
|
-
* @returns {Promise}
|
|
1305
|
-
* @private
|
|
1306
|
-
*/
|
|
1307
|
-
_updateVolumeFromStore() {
|
|
1308
|
-
return store('mediaVolume')
|
|
1309
|
-
.then(volumeStore => volumeStore.getItem('volume'))
|
|
1310
|
-
.then(volume => {
|
|
1311
|
-
if (_.isNumber(volume)) {
|
|
1312
|
-
this.volume = Math.max(volumeMin, Math.min(volumeMax, parseFloat(volume)));
|
|
1313
|
-
this.setVolume(this.volume);
|
|
1314
|
-
}
|
|
1315
|
-
});
|
|
1316
|
-
},
|
|
1317
|
-
|
|
1318
|
-
/**
|
|
1319
|
-
* Event called when the media throws unrecoverable error
|
|
1320
|
-
* @private
|
|
1321
|
-
*/
|
|
1322
|
-
_onError() {
|
|
1323
|
-
this._setState('error', true);
|
|
1324
|
-
this._setState('loading', false);
|
|
1325
|
-
|
|
1326
|
-
/**
|
|
1327
|
-
* Triggers an unrecoverable media error event
|
|
1328
|
-
* @event mediaplayer#error
|
|
1329
|
-
*/
|
|
1330
|
-
this.trigger('error');
|
|
1331
|
-
},
|
|
1332
|
-
|
|
1333
|
-
/**
|
|
1334
|
-
* Event called when the media is played
|
|
1335
|
-
* @private
|
|
1336
|
-
*/
|
|
1337
|
-
_onPlay() {
|
|
1338
|
-
this._playingState(true);
|
|
1339
|
-
this._setState('preview', true);
|
|
1340
|
-
|
|
1341
|
-
/**
|
|
1342
|
-
* Triggers a media playback event
|
|
1343
|
-
* @event mediaplayer#play
|
|
1344
|
-
*/
|
|
1345
|
-
this.trigger('play', this.player && this.player.getMedia());
|
|
1346
|
-
},
|
|
1347
|
-
|
|
1348
|
-
/**
|
|
1349
|
-
* Event called when the media is paused
|
|
1350
|
-
* @private
|
|
1351
|
-
*/
|
|
1352
|
-
_onPause() {
|
|
1353
|
-
this._playingState(false);
|
|
1354
|
-
|
|
1355
|
-
/**
|
|
1356
|
-
* Triggers a media paused event
|
|
1357
|
-
* @event mediaplayer#pause
|
|
1358
|
-
*/
|
|
1359
|
-
this.trigger('pause');
|
|
1360
|
-
},
|
|
1361
|
-
|
|
1362
|
-
/**
|
|
1363
|
-
* Event called when the media is ended
|
|
1364
|
-
* @private
|
|
1365
|
-
*/
|
|
1366
|
-
_onEnd() {
|
|
1367
|
-
this.timesPlayed++;
|
|
1368
|
-
this._playingState(false, true);
|
|
1369
|
-
this._updatePosition(0);
|
|
1370
|
-
|
|
1371
|
-
// disable when the play limit is reached
|
|
1372
|
-
if (this._playLimitReached()) {
|
|
1373
|
-
if (!this.is('disabled')) {
|
|
1374
|
-
this.disable();
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Triggers a play limit reached event
|
|
1378
|
-
* @event mediaplayer#limitreached
|
|
1379
|
-
*/
|
|
1380
|
-
this.trigger('limitreached');
|
|
1381
|
-
} else if (this.loop) {
|
|
1382
|
-
this.restart();
|
|
1383
|
-
} else if (parseInt(this.config.replayTimeout, 10) > 0) {
|
|
1384
|
-
this.replayTimeoutStartMs = new window.Date().getTime();
|
|
1385
|
-
this._replayTimeout();
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
/**
|
|
1389
|
-
* Triggers a media ended event
|
|
1390
|
-
* @event mediaplayer#ended
|
|
1391
|
-
*/
|
|
1392
|
-
this.trigger('ended');
|
|
1393
|
-
},
|
|
1394
|
-
|
|
1395
|
-
/**
|
|
1396
|
-
* Event called when the playback is playing
|
|
1397
|
-
* @private
|
|
1398
|
-
*/
|
|
1399
|
-
_onPlaying() {
|
|
1400
|
-
this._setState('preview', true);
|
|
1401
|
-
this._setState('stalled', false);
|
|
1402
|
-
this._setState('ready', true);
|
|
1403
|
-
},
|
|
1404
|
-
|
|
1405
|
-
/**
|
|
1406
|
-
* Event called when the playback is stalled
|
|
1407
|
-
* @private
|
|
1408
|
-
*/
|
|
1409
|
-
_onStalled() {
|
|
1410
|
-
this._setState('stalled', true);
|
|
1411
|
-
this._setState('ready', false);
|
|
1412
|
-
},
|
|
1413
|
-
|
|
1414
|
-
/**
|
|
1415
|
-
* Event called when the time position has changed
|
|
1416
|
-
* @private
|
|
1417
|
-
*/
|
|
1418
|
-
_onTimeUpdate() {
|
|
1419
|
-
this._updatePosition(this.player.getPosition());
|
|
1420
|
-
|
|
1421
|
-
/**
|
|
1422
|
-
* Triggers a media time update event
|
|
1423
|
-
* @event mediaplayer#update
|
|
1424
|
-
*/
|
|
1425
|
-
this.trigger('update');
|
|
1426
|
-
},
|
|
1427
|
-
|
|
1428
|
-
/**
|
|
1429
|
-
* Run a timer to disable the possibility of replaying a media
|
|
1430
|
-
* @private
|
|
1431
|
-
*/
|
|
1432
|
-
_replayTimeout() {
|
|
1433
|
-
const nowMs = new window.Date().getTime(),
|
|
1434
|
-
elapsedSeconds = Math.floor((nowMs - this.replayTimeoutStartMs) / 1000);
|
|
1435
|
-
|
|
1436
|
-
this.timerId = requestAnimationFrame(this._replayTimeout.bind(this));
|
|
1437
|
-
|
|
1438
|
-
if (elapsedSeconds >= parseInt(this.config.replayTimeout, 10)) {
|
|
1439
|
-
this.disable();
|
|
1440
|
-
cancelAnimationFrame(this.timerId);
|
|
1441
|
-
}
|
|
1442
|
-
},
|
|
1443
|
-
|
|
1444
|
-
/**
|
|
1445
|
-
* Checks if the play limit has been reached
|
|
1446
|
-
* @returns {Boolean}
|
|
1447
|
-
* @private
|
|
1448
|
-
*/
|
|
1449
|
-
_playLimitReached() {
|
|
1450
|
-
return this.config.maxPlays && this.timesPlayed >= this.config.maxPlays;
|
|
1451
|
-
},
|
|
1452
|
-
|
|
1453
|
-
/**
|
|
1454
|
-
* Checks if the media can be played
|
|
1455
|
-
* @returns {Boolean}
|
|
1456
|
-
* @private
|
|
1457
|
-
*/
|
|
1458
|
-
_canPlay() {
|
|
1459
|
-
return (
|
|
1460
|
-
(this.is('ready') || this.is('stalled')) &&
|
|
1461
|
-
!this.is('disabled') &&
|
|
1462
|
-
!this.is('hidden') &&
|
|
1463
|
-
!this._playLimitReached()
|
|
1464
|
-
);
|
|
1465
|
-
},
|
|
1466
|
-
|
|
1467
|
-
/**
|
|
1468
|
-
* Checks if the media can be paused
|
|
1469
|
-
* @returns {Boolean}
|
|
1470
|
-
* @private
|
|
1471
|
-
*/
|
|
1472
|
-
_canPause() {
|
|
1473
|
-
return !!this.config.canPause;
|
|
1474
|
-
},
|
|
1475
|
-
|
|
1476
|
-
/**
|
|
1477
|
-
* Checks if the media can be sought
|
|
1478
|
-
* @returns {Boolean}
|
|
1479
|
-
* @private
|
|
1480
|
-
*/
|
|
1481
|
-
_canSeek() {
|
|
1482
|
-
return !!this.config.canSeek;
|
|
1483
|
-
},
|
|
1484
|
-
|
|
1485
|
-
/**
|
|
1486
|
-
* Checks if the playback can be resumed
|
|
1487
|
-
* @returns {Boolean}
|
|
1488
|
-
* @private
|
|
1489
|
-
*/
|
|
1490
|
-
_canResume() {
|
|
1491
|
-
return this.is('paused') && this._canPlay();
|
|
1492
|
-
},
|
|
1493
|
-
|
|
1494
|
-
/**
|
|
1495
|
-
* Sets the media is in a particular state
|
|
1496
|
-
* @param {String} name
|
|
1497
|
-
* @param {Boolean} value
|
|
1498
|
-
* @returns {mediaplayer}
|
|
1499
|
-
*/
|
|
1500
|
-
_setState(name, value) {
|
|
1501
|
-
value = !!value;
|
|
1502
|
-
|
|
1503
|
-
this.config.is[name] = value;
|
|
1504
|
-
|
|
1505
|
-
if (this.$component) {
|
|
1506
|
-
this.$component.toggleClass(name, value);
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
return this;
|
|
1510
|
-
},
|
|
1511
|
-
|
|
1512
|
-
/**
|
|
1513
|
-
* Restores the media player from a particular state and resumes the playback
|
|
1514
|
-
* @param {String} stateName
|
|
1515
|
-
* @returns {mediaplayer}
|
|
1516
|
-
* @private
|
|
1517
|
-
*/
|
|
1518
|
-
_fromState(stateName) {
|
|
1519
|
-
this._setState(stateName, false);
|
|
1520
|
-
this.resume();
|
|
1521
|
-
|
|
1522
|
-
return this;
|
|
1523
|
-
},
|
|
1524
|
-
|
|
1525
|
-
/**
|
|
1526
|
-
* Sets the media player to a particular state and pauses the playback
|
|
1527
|
-
* @param {String} stateName
|
|
1528
|
-
* @returns {mediaplayer}
|
|
1529
|
-
* @private
|
|
1530
|
-
*/
|
|
1531
|
-
_toState(stateName) {
|
|
1532
|
-
this.pause();
|
|
1533
|
-
this._setState(stateName, true);
|
|
1534
|
-
|
|
1535
|
-
return this;
|
|
1536
|
-
},
|
|
1537
|
-
|
|
1538
|
-
/**
|
|
1539
|
-
* Sets the playing state
|
|
1540
|
-
* @param {Boolean} state
|
|
1541
|
-
* @param {Boolean} [ended]
|
|
1542
|
-
* @returns {mediaplayer}
|
|
1543
|
-
* @private
|
|
1544
|
-
*/
|
|
1545
|
-
_playingState(state, ended) {
|
|
1546
|
-
this._setState('playing', !!state);
|
|
1547
|
-
this._setState('paused', !state);
|
|
1548
|
-
this._setState('ended', !!ended);
|
|
1549
|
-
|
|
1550
|
-
return this;
|
|
1551
|
-
},
|
|
1552
|
-
|
|
1553
|
-
/**
|
|
1554
|
-
* Executes a command onto the media
|
|
1555
|
-
* @param {String} command - The name of the command to execute
|
|
1556
|
-
* @param {*} args - additional arguments
|
|
1557
|
-
* @returns {*}
|
|
1558
|
-
* @private
|
|
1559
|
-
*/
|
|
1560
|
-
execute(command, ...args) {
|
|
1561
|
-
if (this.player && 'function' === typeof this.player[command]) {
|
|
1562
|
-
return this.player[command](...args);
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
};
|
|
1566
|
-
|
|
1567
|
-
return mediaplayer.init(config);
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
/**
|
|
1571
|
-
* Tells if the browser can play audio and video
|
|
1572
|
-
* @param {String} [type] The type of media (audio or video)
|
|
1573
|
-
* @param {String} [mime] A media MIME type to check
|
|
1574
|
-
* @type {Boolean}
|
|
1575
|
-
*/
|
|
1576
|
-
mediaplayerFactory.canPlay = function canPlay(type, mime) {
|
|
1577
|
-
return support.canPlay(type, mime);
|
|
1578
|
-
};
|
|
1579
|
-
|
|
1580
|
-
/**
|
|
1581
|
-
* Tells if the browser can play audio
|
|
1582
|
-
* @param {String} [mime] A media MIME type to check
|
|
1583
|
-
* @type {Boolean}
|
|
1584
|
-
*/
|
|
1585
|
-
mediaplayerFactory.canPlayAudio = function canPlayAudio(mime) {
|
|
1586
|
-
return support.canPlayAudio(mime);
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
|
-
/**
|
|
1590
|
-
* Tells if the browser can play video
|
|
1591
|
-
* @param {String} [mime] A media MIME type to check
|
|
1592
|
-
* @type {Boolean}
|
|
1593
|
-
*/
|
|
1594
|
-
mediaplayerFactory.canPlayVideo = function canPlayVideo(mime) {
|
|
1595
|
-
return support.canPlayVideo(mime);
|
|
1596
|
-
};
|
|
1597
|
-
|
|
1598
|
-
/**
|
|
1599
|
-
* Checks if the browser allows to control the media playback
|
|
1600
|
-
* @returns {Boolean}
|
|
1601
|
-
*/
|
|
1602
|
-
mediaplayerFactory.canControl = function canControl() {
|
|
1603
|
-
return support.canControl();
|
|
1604
|
-
};
|
|
1605
|
-
|
|
1606
|
-
export default mediaplayerFactory;
|
|
1
|
+
/**
|
|
2
|
+
* This program is free software; you can redistribute it and/or
|
|
3
|
+
* modify it under the terms of the GNU General Public License
|
|
4
|
+
* as published by the Free Software Foundation; under version 2
|
|
5
|
+
* of the License (non-upgradable).
|
|
6
|
+
*
|
|
7
|
+
* This program is distributed in the hope that it will be useful,
|
|
8
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
* GNU General Public License for more details.
|
|
11
|
+
*
|
|
12
|
+
* You should have received a copy of the GNU General Public License
|
|
13
|
+
* along with this program; if not, write to the Free Software
|
|
14
|
+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
+
*
|
|
16
|
+
* Copyright (c) 2015-2021 (original work) Open Assessment Technologies SA ;
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import $ from 'jquery';
|
|
20
|
+
import _ from 'lodash';
|
|
21
|
+
import async from 'async';
|
|
22
|
+
import UrlParser from 'util/urlParser';
|
|
23
|
+
import eventifier from 'core/eventifier';
|
|
24
|
+
import mimetype from 'core/mimetype';
|
|
25
|
+
import store from 'core/store';
|
|
26
|
+
import support from 'ui/mediaplayer/support';
|
|
27
|
+
import players from 'ui/mediaplayer/players';
|
|
28
|
+
import playerTpl from 'ui/mediaplayer/tpl/player';
|
|
29
|
+
import 'ui/mediaplayer/css/player.css';
|
|
30
|
+
import 'nouislider';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* CSS namespace
|
|
34
|
+
* @type {String}
|
|
35
|
+
*/
|
|
36
|
+
const ns = '.mediaplayer';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Minimum value of the volume
|
|
40
|
+
* @type {Number}
|
|
41
|
+
*/
|
|
42
|
+
const volumeMin = 0;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Maximum value of the volume
|
|
46
|
+
* @type {Number}
|
|
47
|
+
*/
|
|
48
|
+
const volumeMax = 100;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Threshold (minimum required space above the player) to display the volume
|
|
52
|
+
* above the bar.
|
|
53
|
+
* @type {Number}
|
|
54
|
+
*/
|
|
55
|
+
const volumePositionThreshold = 200;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Some default values
|
|
59
|
+
* @type {Object}
|
|
60
|
+
*/
|
|
61
|
+
const defaults = {
|
|
62
|
+
type: 'video/mp4',
|
|
63
|
+
video: {
|
|
64
|
+
width: '100%',
|
|
65
|
+
height: 'auto'
|
|
66
|
+
},
|
|
67
|
+
audio: {
|
|
68
|
+
width: '100%',
|
|
69
|
+
height: 'auto'
|
|
70
|
+
},
|
|
71
|
+
youtube: {
|
|
72
|
+
width: 640,
|
|
73
|
+
height: 360
|
|
74
|
+
},
|
|
75
|
+
options: {
|
|
76
|
+
volume: 80,
|
|
77
|
+
startMuted: false,
|
|
78
|
+
maxPlays: 0,
|
|
79
|
+
replayTimeout: 0,
|
|
80
|
+
canPause: true,
|
|
81
|
+
canSeek: true,
|
|
82
|
+
loop: false,
|
|
83
|
+
autoStart: false,
|
|
84
|
+
preview: true,
|
|
85
|
+
debug: false
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Ensures a value is a number
|
|
91
|
+
* @param {Number|String} value
|
|
92
|
+
* @returns {Number}
|
|
93
|
+
*/
|
|
94
|
+
const ensureNumber = value => {
|
|
95
|
+
const floatValue = parseFloat(value);
|
|
96
|
+
return isFinite(floatValue) ? floatValue : 0;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Format a number to string with leading zeros
|
|
101
|
+
* @param {Number} n
|
|
102
|
+
* @param {Number} len
|
|
103
|
+
* @returns {String}
|
|
104
|
+
*/
|
|
105
|
+
const leadingZero = (n, len) => {
|
|
106
|
+
let value = n.toString();
|
|
107
|
+
while (value.length < len) {
|
|
108
|
+
value = `0${value}`;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Formats a time value to string
|
|
115
|
+
* @param {Number} time
|
|
116
|
+
* @returns {String}
|
|
117
|
+
*/
|
|
118
|
+
const timerFormat = time => {
|
|
119
|
+
const seconds = Math.floor(time % 60);
|
|
120
|
+
const minutes = Math.floor(time / 60) % 60;
|
|
121
|
+
const hours = Math.floor(time / 3600);
|
|
122
|
+
const parts = [];
|
|
123
|
+
|
|
124
|
+
if (hours) {
|
|
125
|
+
parts.push(hours);
|
|
126
|
+
}
|
|
127
|
+
parts.push(leadingZero(minutes, 2));
|
|
128
|
+
parts.push(leadingZero(seconds, 2));
|
|
129
|
+
|
|
130
|
+
return parts.join(':');
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Checks if a type needs to be adjusted
|
|
135
|
+
* @param {String} type
|
|
136
|
+
* @returns {Boolean}
|
|
137
|
+
*/
|
|
138
|
+
const needTypeAdjust = type => {
|
|
139
|
+
return 'string' === typeof type && type.indexOf('application') === 0;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Adjust bad type by apllying heuristic on URI
|
|
144
|
+
* @param {Object|String} source
|
|
145
|
+
* @returns {String}
|
|
146
|
+
*/
|
|
147
|
+
const getAdjustedType = source => {
|
|
148
|
+
let type = 'video/ogg';
|
|
149
|
+
const url = (source && source.src) || source;
|
|
150
|
+
const ext = url && url.substr(-4);
|
|
151
|
+
if (ext === '.ogg' || ext === '.oga') {
|
|
152
|
+
type = 'audio/ogg';
|
|
153
|
+
}
|
|
154
|
+
return type;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Extract a list of media sources from a config object
|
|
159
|
+
* @param {Object} config
|
|
160
|
+
* @returns {Array}
|
|
161
|
+
*/
|
|
162
|
+
const configToSources = config => {
|
|
163
|
+
let sources = config.sources || [];
|
|
164
|
+
let url = config.url;
|
|
165
|
+
|
|
166
|
+
if (!_.isArray(sources)) {
|
|
167
|
+
sources = [sources];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (url) {
|
|
171
|
+
if (!_.isArray(config.url)) {
|
|
172
|
+
url = [url];
|
|
173
|
+
}
|
|
174
|
+
sources = sources.concat(url);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return sources;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Checks if the browser can play media
|
|
182
|
+
* @param {String} sizeProps Width or Height
|
|
183
|
+
* @returns {Boolean}
|
|
184
|
+
*/
|
|
185
|
+
const isResponsiveSize = sizeProps => {
|
|
186
|
+
return /%/.test(sizeProps) || sizeProps === 'auto';
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Builds a media player instance
|
|
191
|
+
* @param {Object} config
|
|
192
|
+
* @param {String} config.type - The type of media to play, say `audio`, `video`, or `youtube`. The default is `video`.
|
|
193
|
+
* It might also contain the MIME type of the media as a shorthand.
|
|
194
|
+
* @param {String|Array} [config.url] - The URL to the media. If several media are proposed as alternatives,
|
|
195
|
+
* please look at the `sources` option instead.
|
|
196
|
+
* @param {String} [config.mimeType] - The MIME type of the media. If omitted, the player will try to extract it
|
|
197
|
+
* from the `type` property, otherwise it will request the server to get the content-type.
|
|
198
|
+
* @param {Array} [config.sources] - A list of URL if several media can be proposed. Each entry may be either a
|
|
199
|
+
* string (single URL), or an object containing both the URL and the MIME type ({src: string, type: string}).
|
|
200
|
+
* @param {String|jQuery|HTMLElement} [config.renderTo] - An optional container in which renders the player
|
|
201
|
+
* @param {Boolean} [config.canSeek] - The player allows to reach an arbitrary position within the media using the duration bar
|
|
202
|
+
* @param {Boolean} [config.loop] - The media will be played continuously
|
|
203
|
+
* @param {Boolean} [config.canPause] - The player can be paused
|
|
204
|
+
* @param {Boolean} [config.startMuted] - The player should be initially muted
|
|
205
|
+
* @param {Boolean} [config.autoStart] - The player starts as soon as it is displayed
|
|
206
|
+
* @param {Number} [config.autoStartAt] - The time position at which the player should start
|
|
207
|
+
* @param {Number} [config.maxPlays] - Sets a few number of plays (default: infinite)
|
|
208
|
+
* @param {Number} [config.replayTimeout] - disable the possibility to replay a media after this timeout, in seconds (default: 0)
|
|
209
|
+
* @param {Number} [config.volume] - Sets the sound volume (default: 80)
|
|
210
|
+
* @param {Number} [config.width] - Sets the width of the player (default: depends on media type)
|
|
211
|
+
* @param {Number} [config.height] - Sets the height of the player (default: depends on media type)
|
|
212
|
+
* @param {Boolean} [config.preview] - Enables the media preview (load media metadata)
|
|
213
|
+
* @param {Boolean} [config.debug] - Enables the debug mode
|
|
214
|
+
* @param {number} [config.config.stalledDetectionDelay] - The delay before considering a media is stalled
|
|
215
|
+
* @event render - Event triggered when the player is rendering
|
|
216
|
+
* @event error - Event triggered when the player throws an unrecoverable error
|
|
217
|
+
* @event ready - Event triggered when the player is fully ready
|
|
218
|
+
* @event play - Event triggered when the playback is starting
|
|
219
|
+
* @event update - Event triggered while the player is playing
|
|
220
|
+
* @event pause - Event triggered when the playback is paused
|
|
221
|
+
* @event ended - Event triggered when the playback is ended
|
|
222
|
+
* @event limitreached - Event triggered when the play limit has been reached
|
|
223
|
+
* @event destroy - Event triggered when the player is destroying
|
|
224
|
+
* @returns {mediaplayer}
|
|
225
|
+
*/
|
|
226
|
+
function mediaplayerFactory(config) {
|
|
227
|
+
/**
|
|
228
|
+
* Defines a media player object
|
|
229
|
+
* @type {Object}
|
|
230
|
+
*/
|
|
231
|
+
const mediaplayer = {
|
|
232
|
+
/**
|
|
233
|
+
* Initializes the media player
|
|
234
|
+
* @param {Object} mediaPlayerConfig
|
|
235
|
+
* @returns {mediaplayer}
|
|
236
|
+
*/
|
|
237
|
+
init(mediaPlayerConfig) {
|
|
238
|
+
// load the config set, discard null values in order to allow defaults to be set
|
|
239
|
+
this.config = _.omit(mediaPlayerConfig || {}, value => typeof value === 'undefined' || value === null);
|
|
240
|
+
_.defaults(this.config, defaults.options);
|
|
241
|
+
if (!this.config.mimeType && 'string' === typeof this.config.type && this.config.type.indexOf('/') > 0) {
|
|
242
|
+
this.config.mimeType = this.config.type;
|
|
243
|
+
}
|
|
244
|
+
this._setType(this.config.type || defaults.type);
|
|
245
|
+
|
|
246
|
+
this._reset();
|
|
247
|
+
this._updateVolumeFromStore();
|
|
248
|
+
this._initEvents();
|
|
249
|
+
this._initSources(() => {
|
|
250
|
+
if (!this.is('youtube')) {
|
|
251
|
+
_.forEach(this.config.sources, source => {
|
|
252
|
+
if (source && source.type && source.type.indexOf('audio') === 0) {
|
|
253
|
+
this._setType(source.type);
|
|
254
|
+
this._initType();
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (this.config.renderTo) {
|
|
260
|
+
_.defer(() => this.render());
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return this;
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Uninstalls the media player
|
|
269
|
+
* @returns {mediaplayer}
|
|
270
|
+
*/
|
|
271
|
+
destroy() {
|
|
272
|
+
/**
|
|
273
|
+
* Triggers a destroy event
|
|
274
|
+
* @event mediaplayer#destroy
|
|
275
|
+
*/
|
|
276
|
+
this.trigger('destroy');
|
|
277
|
+
|
|
278
|
+
if (this.player) {
|
|
279
|
+
this.player.destroy();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (this.$component) {
|
|
283
|
+
this._unbindEvents();
|
|
284
|
+
this._destroySlider(this.$seekSlider);
|
|
285
|
+
this._destroySlider(this.$volumeSlider);
|
|
286
|
+
|
|
287
|
+
this.$component.remove();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this._reset();
|
|
291
|
+
|
|
292
|
+
return this;
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Renders the media player according to the media type
|
|
297
|
+
* @param {String|jQuery|HTMLElement} [to]
|
|
298
|
+
* @returns {mediaplayer}
|
|
299
|
+
*/
|
|
300
|
+
render(to) {
|
|
301
|
+
const renderTo = to || this.config.renderTo || this.$container;
|
|
302
|
+
|
|
303
|
+
if (this.$component) {
|
|
304
|
+
this.destroy();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this._initState();
|
|
308
|
+
this._buildDom();
|
|
309
|
+
if (this.config.preview) {
|
|
310
|
+
this._updateDuration(0);
|
|
311
|
+
this._updatePosition(0);
|
|
312
|
+
}
|
|
313
|
+
this._bindEvents();
|
|
314
|
+
this._playingState(false, true);
|
|
315
|
+
this._initPlayer();
|
|
316
|
+
this._initSize();
|
|
317
|
+
|
|
318
|
+
// Resize for old items with defined height to avoid big jump
|
|
319
|
+
if (this.config.height && this.config.height !== 'auto') {
|
|
320
|
+
this.resize('100%', 'auto');
|
|
321
|
+
} else {
|
|
322
|
+
this.resize(this.config.width, this.config.height);
|
|
323
|
+
}
|
|
324
|
+
this.config.is.rendered = true;
|
|
325
|
+
|
|
326
|
+
if (renderTo) {
|
|
327
|
+
this.$container = $(renderTo).append(this.$component);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// add class if it is stalled
|
|
331
|
+
if (this.is('stalled')) {
|
|
332
|
+
this._setState('stalled', true);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Triggers a render event
|
|
337
|
+
* @event mediaplayer#render
|
|
338
|
+
* @param {jQuery} $component
|
|
339
|
+
*/
|
|
340
|
+
this.trigger('render', this.$component);
|
|
341
|
+
|
|
342
|
+
return this;
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Reloads media player after it was stalled
|
|
347
|
+
*/
|
|
348
|
+
reload() {
|
|
349
|
+
/**
|
|
350
|
+
* Triggers a reload event
|
|
351
|
+
* @event mediaplayer#reload
|
|
352
|
+
*/
|
|
353
|
+
this.trigger('reload');
|
|
354
|
+
|
|
355
|
+
if (this.player) {
|
|
356
|
+
this.player.recover();
|
|
357
|
+
}
|
|
358
|
+
this._setState('stalled', false);
|
|
359
|
+
this.setInitialStates();
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Set initial states
|
|
364
|
+
*/
|
|
365
|
+
setInitialStates() {
|
|
366
|
+
if (!this.is('stalled')) {
|
|
367
|
+
this._setState('ready', true);
|
|
368
|
+
}
|
|
369
|
+
this._setState('canplay', true);
|
|
370
|
+
this._setState('canpause', this.config.canPause);
|
|
371
|
+
this._setState('canseek', this.config.canSeek);
|
|
372
|
+
this._setState('loading', false);
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Sets the start position inside the media
|
|
377
|
+
* @param {Number} time - The start position in seconds
|
|
378
|
+
* @param {*} [internal] - Internal use
|
|
379
|
+
* @returns {mediaplayer}
|
|
380
|
+
*/
|
|
381
|
+
seek(time, internal) {
|
|
382
|
+
if (this._canPlay()) {
|
|
383
|
+
this._updatePosition(time, internal);
|
|
384
|
+
|
|
385
|
+
this.execute('seek', this.position);
|
|
386
|
+
|
|
387
|
+
if (!this.is('ready')) {
|
|
388
|
+
this.autoStartAt = this.position;
|
|
389
|
+
}
|
|
390
|
+
this.loop = !!this.config.loop;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return this;
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Plays the media
|
|
398
|
+
* @param {Number} [time] - An optional start position in seconds
|
|
399
|
+
* @returns {mediaplayer}
|
|
400
|
+
*/
|
|
401
|
+
play(time) {
|
|
402
|
+
if (this._canPlay()) {
|
|
403
|
+
if (typeof time !== 'undefined') {
|
|
404
|
+
this.seek(time);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.execute('play');
|
|
408
|
+
|
|
409
|
+
if (!this.is('ready')) {
|
|
410
|
+
this.autoStart = true;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
this.loop = !!this.config.loop;
|
|
414
|
+
|
|
415
|
+
if (this.timerId) {
|
|
416
|
+
cancelAnimationFrame(this.timerId);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return this;
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Pauses the media
|
|
425
|
+
* @param {Number} [time] - An optional time position in seconds
|
|
426
|
+
* @returns {mediaplayer}
|
|
427
|
+
*/
|
|
428
|
+
pause(time) {
|
|
429
|
+
if (this._canPause()) {
|
|
430
|
+
if (typeof time !== 'undefined') {
|
|
431
|
+
this.seek(time);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
this.execute('pause');
|
|
435
|
+
|
|
436
|
+
if (!this.is('ready')) {
|
|
437
|
+
this.autoStart = false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return this;
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Resumes the media
|
|
446
|
+
* @returns {mediaplayer}
|
|
447
|
+
*/
|
|
448
|
+
resume() {
|
|
449
|
+
if (this._canResume()) {
|
|
450
|
+
this.play();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return this;
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Stops the playback
|
|
458
|
+
* @returns {mediaplayer}
|
|
459
|
+
*/
|
|
460
|
+
stop() {
|
|
461
|
+
this.loop = false;
|
|
462
|
+
this.execute('stop');
|
|
463
|
+
|
|
464
|
+
if (!this.is('ready')) {
|
|
465
|
+
this.autoStart = false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return this;
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Starts the media
|
|
473
|
+
* @returns {mediaplayer}
|
|
474
|
+
*/
|
|
475
|
+
start() {
|
|
476
|
+
this._setState('preview', true);
|
|
477
|
+
this._setState('loading', true);
|
|
478
|
+
this.play();
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Restarts the media from the beginning
|
|
483
|
+
* @returns {mediaplayer}
|
|
484
|
+
*/
|
|
485
|
+
restart() {
|
|
486
|
+
this.play(0);
|
|
487
|
+
|
|
488
|
+
return this;
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Rewind the media to the beginning
|
|
493
|
+
* @returns {mediaplayer}
|
|
494
|
+
*/
|
|
495
|
+
rewind() {
|
|
496
|
+
this.seek(0);
|
|
497
|
+
|
|
498
|
+
return this;
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Mutes the media
|
|
503
|
+
* @param {Boolean} [state] - A flag to set the mute state (default: true)
|
|
504
|
+
* @returns {mediaplayer}
|
|
505
|
+
*/
|
|
506
|
+
mute(state) {
|
|
507
|
+
if (typeof state === 'undefined') {
|
|
508
|
+
state = true;
|
|
509
|
+
}
|
|
510
|
+
this.execute('mute', state);
|
|
511
|
+
this._setState('muted', state);
|
|
512
|
+
|
|
513
|
+
if (!this.is('ready')) {
|
|
514
|
+
this.startMuted = state;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return this;
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Restore the sound of the media after a mute
|
|
522
|
+
* @returns {mediaplayer}
|
|
523
|
+
*/
|
|
524
|
+
unmute() {
|
|
525
|
+
this.mute(false);
|
|
526
|
+
|
|
527
|
+
return this;
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Sets the sound volume of the media being played
|
|
532
|
+
* @param {Number} value - A value between 0 and 100
|
|
533
|
+
* @param {*} [internal] - Internal use
|
|
534
|
+
* @returns {mediaplayer}
|
|
535
|
+
*/
|
|
536
|
+
setVolume(value, internal) {
|
|
537
|
+
this._updateVolume(value, internal);
|
|
538
|
+
|
|
539
|
+
this.execute('setVolume', this.volume);
|
|
540
|
+
|
|
541
|
+
return this;
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Gets the sound volume applied to the media being played
|
|
546
|
+
* @returns {Number} Returns a value between 0 and 100
|
|
547
|
+
*/
|
|
548
|
+
getVolume() {
|
|
549
|
+
return this.volume;
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Gets the current displayed position inside the media
|
|
554
|
+
* @returns {Number}
|
|
555
|
+
*/
|
|
556
|
+
getPosition() {
|
|
557
|
+
return this.position;
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Gets the duration of the media
|
|
562
|
+
* @returns {Number}
|
|
563
|
+
*/
|
|
564
|
+
getDuration() {
|
|
565
|
+
return this.duration;
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Gets the number of times the media has been played
|
|
570
|
+
* @returns {Number}
|
|
571
|
+
*/
|
|
572
|
+
getTimesPlayed() {
|
|
573
|
+
return this.timesPlayed;
|
|
574
|
+
},
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Gets the type of player
|
|
578
|
+
* @returns {String}
|
|
579
|
+
*/
|
|
580
|
+
getType() {
|
|
581
|
+
return this.type;
|
|
582
|
+
},
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Gets the DOM container
|
|
586
|
+
* @returns {jQuery}
|
|
587
|
+
*/
|
|
588
|
+
getContainer() {
|
|
589
|
+
if (!this.$container && this.$component) {
|
|
590
|
+
let $container = this.$component.parent();
|
|
591
|
+
if ($container.length) {
|
|
592
|
+
this.$container = $container;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return this.$container;
|
|
596
|
+
},
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Gets the underlying DOM element
|
|
600
|
+
* @returns {jQuery}
|
|
601
|
+
*/
|
|
602
|
+
getElement() {
|
|
603
|
+
return this.$component;
|
|
604
|
+
},
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Gets the list of media
|
|
608
|
+
* @returns {Array}
|
|
609
|
+
*/
|
|
610
|
+
getSources() {
|
|
611
|
+
return this.config.sources.slice();
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Sets the media source. If a source has been already set, it will be replaced.
|
|
616
|
+
* @param {String|Object} src - The media URL, or an object containing the source and the type
|
|
617
|
+
* @param {Function} [callback] - A function called to provide the added media source object
|
|
618
|
+
* @returns {mediaplayer}
|
|
619
|
+
*/
|
|
620
|
+
setSource(src, callback) {
|
|
621
|
+
this._getSource(src, source => {
|
|
622
|
+
this.config.sources = [source];
|
|
623
|
+
|
|
624
|
+
if (this.is('rendered')) {
|
|
625
|
+
this.player.setMedia(source.src, source.type);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
if (callback) {
|
|
629
|
+
callback.call(this, source);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
return this;
|
|
634
|
+
},
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Adds a media source.
|
|
638
|
+
* @param {String|Object} src - The media URL, or an object containing the source and the type
|
|
639
|
+
* @param {Function} [callback] - A function called to provide the added media source object
|
|
640
|
+
* @returns {mediaplayer}
|
|
641
|
+
*/
|
|
642
|
+
addSource(src, callback) {
|
|
643
|
+
this._getSource(src, source => {
|
|
644
|
+
this.config.sources.push(source);
|
|
645
|
+
|
|
646
|
+
if (this.is('rendered')) {
|
|
647
|
+
this.player.addMedia(source.src, source.type);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (callback) {
|
|
651
|
+
callback.call(this, source);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
return this;
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Tells if the media is in a particular state
|
|
660
|
+
* @param {String} state
|
|
661
|
+
* @returns {Boolean}
|
|
662
|
+
*/
|
|
663
|
+
is(state) {
|
|
664
|
+
return !!this.config.is[state];
|
|
665
|
+
},
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Changes the size of the player
|
|
669
|
+
* @param {Number} width
|
|
670
|
+
* @param {Number} height
|
|
671
|
+
* @returns {mediaplayer}
|
|
672
|
+
*/
|
|
673
|
+
resize(width, height) {
|
|
674
|
+
if ((isResponsiveSize(width) && !isResponsiveSize(height)) || this.is('youtube')) {
|
|
675
|
+
// responsive width height should be auto
|
|
676
|
+
// for youtube iframe height is limited by ration
|
|
677
|
+
height = 'auto';
|
|
678
|
+
}
|
|
679
|
+
this.execute('setSize', width, height);
|
|
680
|
+
|
|
681
|
+
return this;
|
|
682
|
+
},
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Enables the media player
|
|
686
|
+
* @returns {mediaplayer}
|
|
687
|
+
*/
|
|
688
|
+
enable() {
|
|
689
|
+
this._fromState('disabled');
|
|
690
|
+
|
|
691
|
+
return this;
|
|
692
|
+
},
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Disables the media player
|
|
696
|
+
* @returns {mediaplayer}
|
|
697
|
+
*/
|
|
698
|
+
disable() {
|
|
699
|
+
this._toState('disabled');
|
|
700
|
+
this.trigger('disabled');
|
|
701
|
+
|
|
702
|
+
return this;
|
|
703
|
+
},
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Shows the media player
|
|
707
|
+
* @returns {mediaplayer}
|
|
708
|
+
*/
|
|
709
|
+
show() {
|
|
710
|
+
this._fromState('hidden');
|
|
711
|
+
|
|
712
|
+
return this;
|
|
713
|
+
},
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* hides the media player
|
|
717
|
+
* @returns {mediaplayer}
|
|
718
|
+
*/
|
|
719
|
+
hide() {
|
|
720
|
+
this._toState('hidden');
|
|
721
|
+
|
|
722
|
+
return this;
|
|
723
|
+
},
|
|
724
|
+
/**
|
|
725
|
+
* get media original size
|
|
726
|
+
* @returns {Object}
|
|
727
|
+
*/
|
|
728
|
+
getMediaOriginalSize() {
|
|
729
|
+
if (this.is('youtube')) {
|
|
730
|
+
return defaults.youtube;
|
|
731
|
+
}
|
|
732
|
+
if (this.is('video') && this.player) {
|
|
733
|
+
return this.player.getMediaSize();
|
|
734
|
+
}
|
|
735
|
+
return {};
|
|
736
|
+
},
|
|
737
|
+
/**
|
|
738
|
+
* Ensures the right media type is set
|
|
739
|
+
* @param {String} type
|
|
740
|
+
* @private
|
|
741
|
+
*/
|
|
742
|
+
_setType(type) {
|
|
743
|
+
if (type.indexOf('youtube') !== -1) {
|
|
744
|
+
this.type = 'youtube';
|
|
745
|
+
} else if (type.indexOf('audio') === 0) {
|
|
746
|
+
this.type = 'audio';
|
|
747
|
+
} else {
|
|
748
|
+
this.type = 'video';
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Ensures the type is correctly applied
|
|
754
|
+
* @private
|
|
755
|
+
*/
|
|
756
|
+
_initType() {
|
|
757
|
+
const is = this.config.is;
|
|
758
|
+
is.youtube = 'youtube' === this.type;
|
|
759
|
+
is.video = 'video' === this.type || 'youtube' === this.type;
|
|
760
|
+
is.audio = 'audio' === this.type;
|
|
761
|
+
},
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Gets a source descriptor.
|
|
765
|
+
* @param {String|Object} src - The media URL, or an object containing the source and the type
|
|
766
|
+
* @param {Function} callback - A function called to provide the media source object
|
|
767
|
+
*/
|
|
768
|
+
_getSource(src, callback) {
|
|
769
|
+
let source;
|
|
770
|
+
const done = () => {
|
|
771
|
+
if (needTypeAdjust(source.type)) {
|
|
772
|
+
source.type = getAdjustedType(source);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
callback.call(this, source);
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
if (_.isString(src)) {
|
|
779
|
+
source = {
|
|
780
|
+
src: src
|
|
781
|
+
};
|
|
782
|
+
} else {
|
|
783
|
+
source = _.clone(src);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (!source.type) {
|
|
787
|
+
if (this.is('youtube')) {
|
|
788
|
+
source.type = defaults.type;
|
|
789
|
+
} else if (this.config.mimeType) {
|
|
790
|
+
source.type = this.config.mimeType;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (!source.type) {
|
|
795
|
+
mimetype.getResourceType(source.src, (err, type) => {
|
|
796
|
+
if (err) {
|
|
797
|
+
type = defaults.type;
|
|
798
|
+
}
|
|
799
|
+
source.type = type;
|
|
800
|
+
done();
|
|
801
|
+
});
|
|
802
|
+
} else {
|
|
803
|
+
done();
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Ensures the sources are correctly set
|
|
809
|
+
* @param {Function} callback - A function called once all sources have been initialized
|
|
810
|
+
* @private
|
|
811
|
+
*/
|
|
812
|
+
_initSources(callback) {
|
|
813
|
+
const sources = configToSources(this.config);
|
|
814
|
+
|
|
815
|
+
this.config.sources = [];
|
|
816
|
+
|
|
817
|
+
async.each(
|
|
818
|
+
sources,
|
|
819
|
+
(source, cb) => {
|
|
820
|
+
this.addSource(source, src => cb(null, src));
|
|
821
|
+
},
|
|
822
|
+
callback
|
|
823
|
+
);
|
|
824
|
+
},
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Installs the events manager onto the instance
|
|
828
|
+
* @private
|
|
829
|
+
*/
|
|
830
|
+
_initEvents() {
|
|
831
|
+
eventifier(this);
|
|
832
|
+
|
|
833
|
+
const triggerEvent = this.trigger;
|
|
834
|
+
this.trigger = function trigger(eventName, ...args) {
|
|
835
|
+
if (this.$component) {
|
|
836
|
+
this.$component.trigger(eventName + ns, ...args);
|
|
837
|
+
}
|
|
838
|
+
return triggerEvent.call(this, eventName, ...args);
|
|
839
|
+
};
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Ensures the right size is set according to the media type
|
|
844
|
+
* @private
|
|
845
|
+
*/
|
|
846
|
+
_initSize() {
|
|
847
|
+
const type = this.is('video') ? 'video' : 'audio';
|
|
848
|
+
const mediaConfig = defaults[type] || defaults.video;
|
|
849
|
+
|
|
850
|
+
this.config.width = this.config.width || mediaConfig.width;
|
|
851
|
+
this.config.height = this.config.height || mediaConfig.height;
|
|
852
|
+
|
|
853
|
+
if ((isResponsiveSize(this.config.width) && !isResponsiveSize(this.config.height)) || this.is('youtube')) {
|
|
854
|
+
// responsive width height should be auto
|
|
855
|
+
// for youtube iframe height is limited by ration
|
|
856
|
+
this.config.height = 'auto';
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Initializes the right player instance
|
|
862
|
+
* @private
|
|
863
|
+
*/
|
|
864
|
+
_initPlayer() {
|
|
865
|
+
const playerFactory = players[this.type];
|
|
866
|
+
let error;
|
|
867
|
+
|
|
868
|
+
if (support.canPlay(this.type)) {
|
|
869
|
+
if (_.isFunction(playerFactory)) {
|
|
870
|
+
const playerConfig = {
|
|
871
|
+
type: this.getType(),
|
|
872
|
+
sources: this.getSources(),
|
|
873
|
+
preview: this.config.preview,
|
|
874
|
+
debug: this.config.debug,
|
|
875
|
+
stalledDetectionDelay: this.config.stalledDetectionDelay
|
|
876
|
+
};
|
|
877
|
+
this.player = playerFactory(this.$player, playerConfig)
|
|
878
|
+
.on('resize', (width, height) => {
|
|
879
|
+
if (this.$component) {
|
|
880
|
+
this.$component.width(width).height(height);
|
|
881
|
+
}
|
|
882
|
+
})
|
|
883
|
+
.on('ready', () => this._onReady())
|
|
884
|
+
.on('play', () => this._onPlay())
|
|
885
|
+
.on('pause', () => this._onPause())
|
|
886
|
+
.on('timeupdate', () => this._onTimeUpdate())
|
|
887
|
+
.on('stalled', () => this._onStalled())
|
|
888
|
+
.on('playing', () => this._onPlaying())
|
|
889
|
+
.on('end', () => this._onEnd())
|
|
890
|
+
.on('error', () => this._onError());
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (this.player) {
|
|
894
|
+
error = !this.player.init();
|
|
895
|
+
} else {
|
|
896
|
+
error = true;
|
|
897
|
+
}
|
|
898
|
+
} else {
|
|
899
|
+
error = true;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
this._setState('error', error);
|
|
903
|
+
this._setState('nogui', !support.canControl());
|
|
904
|
+
this._setState('preview', this.config.preview);
|
|
905
|
+
this._setState('loading', !error);
|
|
906
|
+
if (error) {
|
|
907
|
+
this._setState('ready', true);
|
|
908
|
+
this.trigger('ready');
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Initializes the player state
|
|
914
|
+
* @private
|
|
915
|
+
*/
|
|
916
|
+
_initState() {
|
|
917
|
+
let isCORS = false;
|
|
918
|
+
let page;
|
|
919
|
+
|
|
920
|
+
if (!this.is('youtube')) {
|
|
921
|
+
page = new UrlParser(window.location);
|
|
922
|
+
isCORS = _.some(this.config.sources, source => !page.sameDomain(source.src));
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
this._setState('cors', isCORS);
|
|
926
|
+
this._setState('ready', false);
|
|
927
|
+
},
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Resets the internals attributes
|
|
931
|
+
* @private
|
|
932
|
+
*/
|
|
933
|
+
_reset() {
|
|
934
|
+
this.config.is = {};
|
|
935
|
+
this._initType();
|
|
936
|
+
|
|
937
|
+
this.$component = null;
|
|
938
|
+
this.$container = null;
|
|
939
|
+
this.$player = null;
|
|
940
|
+
this.$controls = null;
|
|
941
|
+
this.$seek = null;
|
|
942
|
+
this.$seekSlider = null;
|
|
943
|
+
this.$sound = null;
|
|
944
|
+
this.$volume = null;
|
|
945
|
+
this.$volumeControl = null;
|
|
946
|
+
this.$volumeSlider = null;
|
|
947
|
+
this.$position = null;
|
|
948
|
+
this.$duration = null;
|
|
949
|
+
this.player = null;
|
|
950
|
+
|
|
951
|
+
this.duration = 0;
|
|
952
|
+
this.position = 0;
|
|
953
|
+
this.timesPlayed = 0;
|
|
954
|
+
|
|
955
|
+
this.volume = this.config.volume;
|
|
956
|
+
this.autoStart = this.config.autoStart;
|
|
957
|
+
this.autoStartAt = this.config.autoStartAt;
|
|
958
|
+
this.startMuted = this.config.startMuted;
|
|
959
|
+
},
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Builds the DOM content
|
|
963
|
+
* @private
|
|
964
|
+
*/
|
|
965
|
+
_buildDom() {
|
|
966
|
+
const configForTemplate = _.clone(this.config);
|
|
967
|
+
configForTemplate.type = this.type;
|
|
968
|
+
this.$component = $(playerTpl(configForTemplate));
|
|
969
|
+
this.$player = this.$component.find('.player');
|
|
970
|
+
this.$controls = this.$component.find('.controls');
|
|
971
|
+
|
|
972
|
+
this.$seek = this.$controls.find('.seek .slider');
|
|
973
|
+
this.$sound = this.$controls.find('.sound');
|
|
974
|
+
this.$volumeControl = this.$controls.find('.volume');
|
|
975
|
+
this.$volume = this.$controls.find('.volume .slider');
|
|
976
|
+
this.$position = this.$controls.find('[data-control="time-cur"]');
|
|
977
|
+
this.$duration = this.$controls.find('[data-control="time-end"]');
|
|
978
|
+
|
|
979
|
+
this.$volumeSlider = this._renderSlider(this.$volume, this.volume, volumeMin, volumeMax, true);
|
|
980
|
+
},
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Renders a slider onto an element
|
|
984
|
+
* @param {jQuery} $elt - The element on which renders the slider
|
|
985
|
+
* @param {Number} [value] - The current value of the slider
|
|
986
|
+
* @param {Number} [min] - The min value of the slider
|
|
987
|
+
* @param {Number} [max] - The max value of the slider
|
|
988
|
+
* @param {Boolean} [vertical] - Tells if the slider must be vertical
|
|
989
|
+
* @returns {jQuery} - Returns the element
|
|
990
|
+
* @private
|
|
991
|
+
*/
|
|
992
|
+
_renderSlider($elt, value, min, max, vertical) {
|
|
993
|
+
let orientation, direction;
|
|
994
|
+
|
|
995
|
+
if (vertical) {
|
|
996
|
+
orientation = 'vertical';
|
|
997
|
+
direction = 'rtl';
|
|
998
|
+
} else {
|
|
999
|
+
orientation = 'horizontal';
|
|
1000
|
+
direction = 'ltr';
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return $elt.noUiSlider({
|
|
1004
|
+
start: ensureNumber(value) || 0,
|
|
1005
|
+
step: 1,
|
|
1006
|
+
connect: 'lower',
|
|
1007
|
+
orientation: orientation,
|
|
1008
|
+
direction: direction,
|
|
1009
|
+
animate: true,
|
|
1010
|
+
range: {
|
|
1011
|
+
min: ensureNumber(min) || 0,
|
|
1012
|
+
max: ensureNumber(max) || 0
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
},
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Destroys a slider bound to an element
|
|
1019
|
+
* @param {jQuery} $elt
|
|
1020
|
+
* @private
|
|
1021
|
+
*/
|
|
1022
|
+
_destroySlider($elt) {
|
|
1023
|
+
if ($elt) {
|
|
1024
|
+
$elt.get(0).destroy();
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Binds events onto the rendered player
|
|
1030
|
+
* @private
|
|
1031
|
+
*/
|
|
1032
|
+
_bindEvents() {
|
|
1033
|
+
let overing = false;
|
|
1034
|
+
|
|
1035
|
+
this.$component.on(`contextmenu${ns}`, event => event.preventDefault());
|
|
1036
|
+
|
|
1037
|
+
this.$controls.on(`click${ns}`, '.action', event => {
|
|
1038
|
+
const $target = $(event.target);
|
|
1039
|
+
const $action = $target.closest('.action');
|
|
1040
|
+
const id = $action.data('control');
|
|
1041
|
+
|
|
1042
|
+
if (_.isFunction(this[id])) {
|
|
1043
|
+
this[id]();
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
this.$player.on(`click${ns}`, event => {
|
|
1048
|
+
const $target = $(event.target);
|
|
1049
|
+
const $action = $target.closest('.action');
|
|
1050
|
+
|
|
1051
|
+
// if action was clicked
|
|
1052
|
+
if ($action.length) {
|
|
1053
|
+
const id = $action.data('control');
|
|
1054
|
+
if (_.isFunction(this[id])) {
|
|
1055
|
+
this[id]();
|
|
1056
|
+
}
|
|
1057
|
+
} else {
|
|
1058
|
+
// default action is toggle play
|
|
1059
|
+
if (this.is('playing')) {
|
|
1060
|
+
this.pause();
|
|
1061
|
+
} else {
|
|
1062
|
+
this.play();
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
this.$seek.on(`change${ns}`, (event, value) => {
|
|
1068
|
+
this.seek(value, true);
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
$(document).on(`updateVolume${ns}`, (event, value) => {
|
|
1072
|
+
this.setVolume(value);
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
this.$volume.on(`change${ns}`, (event, value) => {
|
|
1076
|
+
this.unmute();
|
|
1077
|
+
$(document).trigger(`updateVolume${ns}`, value);
|
|
1078
|
+
this.setVolume(value, true);
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
this.$sound.on(`mouseover${ns}`, 'a', () => {
|
|
1082
|
+
let position;
|
|
1083
|
+
|
|
1084
|
+
if (!overing && !this.$volumeControl.hasClass('up') && !this.$volumeControl.hasClass('down')) {
|
|
1085
|
+
overing = true;
|
|
1086
|
+
position = this.$controls[0].getBoundingClientRect();
|
|
1087
|
+
if (position && position.top && position.top < volumePositionThreshold) {
|
|
1088
|
+
this.$volumeControl.addClass('down');
|
|
1089
|
+
} else {
|
|
1090
|
+
this.$volumeControl.addClass('up');
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
//close the volume control after 15s
|
|
1094
|
+
this.overingTimer = _.delay(() => {
|
|
1095
|
+
if (this.$volumeControl) {
|
|
1096
|
+
this.$volumeControl.removeClass('up down');
|
|
1097
|
+
}
|
|
1098
|
+
overing = false;
|
|
1099
|
+
}, 15000);
|
|
1100
|
+
this.$volumeControl.one(`mouseleave${ns}`, () => {
|
|
1101
|
+
this.$volumeControl.removeClass('up down');
|
|
1102
|
+
overing = false;
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
},
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* Unbinds events from the rendered player
|
|
1110
|
+
* @private
|
|
1111
|
+
*/
|
|
1112
|
+
_unbindEvents() {
|
|
1113
|
+
this.$component.off(ns);
|
|
1114
|
+
this.$player.off(ns);
|
|
1115
|
+
this.$controls.off(ns);
|
|
1116
|
+
this.$seek.off(ns);
|
|
1117
|
+
this.$volume.off(ns);
|
|
1118
|
+
|
|
1119
|
+
//if the volume is opened and the player destroyed,
|
|
1120
|
+
//prevent the callback to run
|
|
1121
|
+
if (this.overingTimer) {
|
|
1122
|
+
clearTimeout(this.overingTimer);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
$(document).off(ns);
|
|
1126
|
+
},
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Updates the volume slider
|
|
1130
|
+
* @param {Number} value
|
|
1131
|
+
* @private
|
|
1132
|
+
*/
|
|
1133
|
+
_updateVolumeSlider(value) {
|
|
1134
|
+
if (this.$volumeSlider) {
|
|
1135
|
+
this.$volumeSlider.val(value);
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Updates the displayed volume
|
|
1141
|
+
* @param {Number} value
|
|
1142
|
+
* @param {*} [internal]
|
|
1143
|
+
* @private
|
|
1144
|
+
*/
|
|
1145
|
+
_updateVolume(value, internal) {
|
|
1146
|
+
this.volume = Math.max(volumeMin, Math.min(volumeMax, parseFloat(value)));
|
|
1147
|
+
this._storeVolume(this.volume);
|
|
1148
|
+
if (!internal) {
|
|
1149
|
+
this._updateVolumeSlider(value);
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Updates the time slider
|
|
1155
|
+
* @param {Number} value
|
|
1156
|
+
* @private
|
|
1157
|
+
*/
|
|
1158
|
+
_updatePositionSlider(value) {
|
|
1159
|
+
if (this.$seekSlider) {
|
|
1160
|
+
this.$seekSlider.val(value);
|
|
1161
|
+
}
|
|
1162
|
+
},
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Updates the time label
|
|
1166
|
+
* @param {Number} value
|
|
1167
|
+
* @private
|
|
1168
|
+
*/
|
|
1169
|
+
_updatePositionLabel(value) {
|
|
1170
|
+
if (this.$position) {
|
|
1171
|
+
this.$position.text(timerFormat(value));
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* Updates the displayed time position
|
|
1177
|
+
* @param {Number} value
|
|
1178
|
+
* @param {*} [internal]
|
|
1179
|
+
* @private
|
|
1180
|
+
*/
|
|
1181
|
+
_updatePosition(value, internal) {
|
|
1182
|
+
this.position = Math.max(0, Math.min(this.duration || +Infinity, parseFloat(value)));
|
|
1183
|
+
|
|
1184
|
+
if (!internal && this.duration) {
|
|
1185
|
+
this._updatePositionSlider(this.position);
|
|
1186
|
+
}
|
|
1187
|
+
this._updatePositionLabel(this.position);
|
|
1188
|
+
},
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Updates the duration slider
|
|
1192
|
+
* @param {Number} value
|
|
1193
|
+
* @private
|
|
1194
|
+
*/
|
|
1195
|
+
_updateDurationSlider(value) {
|
|
1196
|
+
if (this.$seekSlider) {
|
|
1197
|
+
this._destroySlider(this.$seekSlider);
|
|
1198
|
+
this.$seekSlider = null;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (value && isFinite(value)) {
|
|
1202
|
+
this.$seekSlider = this._renderSlider(this.$seek, 0, 0, value);
|
|
1203
|
+
this.$seekSlider.attr('disabled', !this.config.canSeek);
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* Updates the duration label
|
|
1209
|
+
* @param {Number} value
|
|
1210
|
+
* @private
|
|
1211
|
+
*/
|
|
1212
|
+
_updateDurationLabel(value) {
|
|
1213
|
+
if (this.$duration) {
|
|
1214
|
+
if (value && isFinite(value)) {
|
|
1215
|
+
this.$duration.text(timerFormat(value)).show();
|
|
1216
|
+
} else {
|
|
1217
|
+
this.$duration.hide();
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
},
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* Updates the displayed duration
|
|
1224
|
+
* @param {Number|String} value
|
|
1225
|
+
* @private
|
|
1226
|
+
*/
|
|
1227
|
+
_updateDuration(value) {
|
|
1228
|
+
const duration = Math.abs(parseFloat(value));
|
|
1229
|
+
if (duration !== this.duration) {
|
|
1230
|
+
this.duration = duration;
|
|
1231
|
+
this._updateDurationSlider(this.duration);
|
|
1232
|
+
this._updateDurationLabel(this.duration);
|
|
1233
|
+
}
|
|
1234
|
+
},
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Event called when the media is ready
|
|
1238
|
+
* @private
|
|
1239
|
+
*/
|
|
1240
|
+
_onReady() {
|
|
1241
|
+
if (this.is('error')) {
|
|
1242
|
+
this._setState('error', false);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
const duration = this.player.getDuration();
|
|
1246
|
+
const timePreview = this.config.preview || duration;
|
|
1247
|
+
if (timePreview) {
|
|
1248
|
+
this._updateDuration(duration);
|
|
1249
|
+
}
|
|
1250
|
+
this.setInitialStates();
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Triggers a media ready event
|
|
1254
|
+
* @event mediaplayer#ready
|
|
1255
|
+
*/
|
|
1256
|
+
this.trigger('ready');
|
|
1257
|
+
|
|
1258
|
+
// set the initial state
|
|
1259
|
+
this.setVolume(this.volume);
|
|
1260
|
+
this.mute(!!this.startMuted);
|
|
1261
|
+
if (this.autoStartAt) {
|
|
1262
|
+
this.seek(this.autoStartAt);
|
|
1263
|
+
} else if (this.autoStart) {
|
|
1264
|
+
this.play();
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
if (this.config.preview && this.$container && this.config.height && this.config.height !== 'auto') {
|
|
1268
|
+
this._setMaxHeight();
|
|
1269
|
+
}
|
|
1270
|
+
},
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Set max height limit for container
|
|
1274
|
+
* using by old media items with defined height.
|
|
1275
|
+
* @private
|
|
1276
|
+
*/
|
|
1277
|
+
_setMaxHeight() {
|
|
1278
|
+
const $video = this.$container.find('video.video');
|
|
1279
|
+
const controlsHeight = parseInt(window.getComputedStyle(this.$controls[0]).height);
|
|
1280
|
+
const scale = $video.height() / this.config.height;
|
|
1281
|
+
const playerWidth = this.$container.find('.player').width();
|
|
1282
|
+
const videoWidth = $video.width() / scale;
|
|
1283
|
+
|
|
1284
|
+
if (videoWidth > playerWidth) {
|
|
1285
|
+
this.execute('setSize', '100%', 'auto');
|
|
1286
|
+
} else {
|
|
1287
|
+
this.$component.css({ maxHeight: `${this.config.height + controlsHeight}px` });
|
|
1288
|
+
this.execute('setSize', Math.floor(videoWidth), 'auto');
|
|
1289
|
+
}
|
|
1290
|
+
},
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Update volume in DBIndex store
|
|
1294
|
+
* @param {Number} volume
|
|
1295
|
+
* @returns {Promise}
|
|
1296
|
+
* @private
|
|
1297
|
+
*/
|
|
1298
|
+
_storeVolume(volume) {
|
|
1299
|
+
return store('mediaVolume').then(volumeStore => volumeStore.setItem('volume', volume));
|
|
1300
|
+
},
|
|
1301
|
+
|
|
1302
|
+
/**
|
|
1303
|
+
* Get volume from DBIndex store
|
|
1304
|
+
* @returns {Promise}
|
|
1305
|
+
* @private
|
|
1306
|
+
*/
|
|
1307
|
+
_updateVolumeFromStore() {
|
|
1308
|
+
return store('mediaVolume')
|
|
1309
|
+
.then(volumeStore => volumeStore.getItem('volume'))
|
|
1310
|
+
.then(volume => {
|
|
1311
|
+
if (_.isNumber(volume)) {
|
|
1312
|
+
this.volume = Math.max(volumeMin, Math.min(volumeMax, parseFloat(volume)));
|
|
1313
|
+
this.setVolume(this.volume);
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
},
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Event called when the media throws unrecoverable error
|
|
1320
|
+
* @private
|
|
1321
|
+
*/
|
|
1322
|
+
_onError() {
|
|
1323
|
+
this._setState('error', true);
|
|
1324
|
+
this._setState('loading', false);
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Triggers an unrecoverable media error event
|
|
1328
|
+
* @event mediaplayer#error
|
|
1329
|
+
*/
|
|
1330
|
+
this.trigger('error');
|
|
1331
|
+
},
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Event called when the media is played
|
|
1335
|
+
* @private
|
|
1336
|
+
*/
|
|
1337
|
+
_onPlay() {
|
|
1338
|
+
this._playingState(true);
|
|
1339
|
+
this._setState('preview', true);
|
|
1340
|
+
|
|
1341
|
+
/**
|
|
1342
|
+
* Triggers a media playback event
|
|
1343
|
+
* @event mediaplayer#play
|
|
1344
|
+
*/
|
|
1345
|
+
this.trigger('play', this.player && this.player.getMedia());
|
|
1346
|
+
},
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Event called when the media is paused
|
|
1350
|
+
* @private
|
|
1351
|
+
*/
|
|
1352
|
+
_onPause() {
|
|
1353
|
+
this._playingState(false);
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* Triggers a media paused event
|
|
1357
|
+
* @event mediaplayer#pause
|
|
1358
|
+
*/
|
|
1359
|
+
this.trigger('pause');
|
|
1360
|
+
},
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* Event called when the media is ended
|
|
1364
|
+
* @private
|
|
1365
|
+
*/
|
|
1366
|
+
_onEnd() {
|
|
1367
|
+
this.timesPlayed++;
|
|
1368
|
+
this._playingState(false, true);
|
|
1369
|
+
this._updatePosition(0);
|
|
1370
|
+
|
|
1371
|
+
// disable when the play limit is reached
|
|
1372
|
+
if (this._playLimitReached()) {
|
|
1373
|
+
if (!this.is('disabled')) {
|
|
1374
|
+
this.disable();
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Triggers a play limit reached event
|
|
1378
|
+
* @event mediaplayer#limitreached
|
|
1379
|
+
*/
|
|
1380
|
+
this.trigger('limitreached');
|
|
1381
|
+
} else if (this.loop) {
|
|
1382
|
+
this.restart();
|
|
1383
|
+
} else if (parseInt(this.config.replayTimeout, 10) > 0) {
|
|
1384
|
+
this.replayTimeoutStartMs = new window.Date().getTime();
|
|
1385
|
+
this._replayTimeout();
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* Triggers a media ended event
|
|
1390
|
+
* @event mediaplayer#ended
|
|
1391
|
+
*/
|
|
1392
|
+
this.trigger('ended');
|
|
1393
|
+
},
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* Event called when the playback is playing
|
|
1397
|
+
* @private
|
|
1398
|
+
*/
|
|
1399
|
+
_onPlaying() {
|
|
1400
|
+
this._setState('preview', true);
|
|
1401
|
+
this._setState('stalled', false);
|
|
1402
|
+
this._setState('ready', true);
|
|
1403
|
+
},
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* Event called when the playback is stalled
|
|
1407
|
+
* @private
|
|
1408
|
+
*/
|
|
1409
|
+
_onStalled() {
|
|
1410
|
+
this._setState('stalled', true);
|
|
1411
|
+
this._setState('ready', false);
|
|
1412
|
+
},
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Event called when the time position has changed
|
|
1416
|
+
* @private
|
|
1417
|
+
*/
|
|
1418
|
+
_onTimeUpdate() {
|
|
1419
|
+
this._updatePosition(this.player.getPosition());
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Triggers a media time update event
|
|
1423
|
+
* @event mediaplayer#update
|
|
1424
|
+
*/
|
|
1425
|
+
this.trigger('update');
|
|
1426
|
+
},
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* Run a timer to disable the possibility of replaying a media
|
|
1430
|
+
* @private
|
|
1431
|
+
*/
|
|
1432
|
+
_replayTimeout() {
|
|
1433
|
+
const nowMs = new window.Date().getTime(),
|
|
1434
|
+
elapsedSeconds = Math.floor((nowMs - this.replayTimeoutStartMs) / 1000);
|
|
1435
|
+
|
|
1436
|
+
this.timerId = requestAnimationFrame(this._replayTimeout.bind(this));
|
|
1437
|
+
|
|
1438
|
+
if (elapsedSeconds >= parseInt(this.config.replayTimeout, 10)) {
|
|
1439
|
+
this.disable();
|
|
1440
|
+
cancelAnimationFrame(this.timerId);
|
|
1441
|
+
}
|
|
1442
|
+
},
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* Checks if the play limit has been reached
|
|
1446
|
+
* @returns {Boolean}
|
|
1447
|
+
* @private
|
|
1448
|
+
*/
|
|
1449
|
+
_playLimitReached() {
|
|
1450
|
+
return this.config.maxPlays && this.timesPlayed >= this.config.maxPlays;
|
|
1451
|
+
},
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* Checks if the media can be played
|
|
1455
|
+
* @returns {Boolean}
|
|
1456
|
+
* @private
|
|
1457
|
+
*/
|
|
1458
|
+
_canPlay() {
|
|
1459
|
+
return (
|
|
1460
|
+
(this.is('ready') || this.is('stalled')) &&
|
|
1461
|
+
!this.is('disabled') &&
|
|
1462
|
+
!this.is('hidden') &&
|
|
1463
|
+
!this._playLimitReached()
|
|
1464
|
+
);
|
|
1465
|
+
},
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Checks if the media can be paused
|
|
1469
|
+
* @returns {Boolean}
|
|
1470
|
+
* @private
|
|
1471
|
+
*/
|
|
1472
|
+
_canPause() {
|
|
1473
|
+
return !!this.config.canPause;
|
|
1474
|
+
},
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* Checks if the media can be sought
|
|
1478
|
+
* @returns {Boolean}
|
|
1479
|
+
* @private
|
|
1480
|
+
*/
|
|
1481
|
+
_canSeek() {
|
|
1482
|
+
return !!this.config.canSeek;
|
|
1483
|
+
},
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Checks if the playback can be resumed
|
|
1487
|
+
* @returns {Boolean}
|
|
1488
|
+
* @private
|
|
1489
|
+
*/
|
|
1490
|
+
_canResume() {
|
|
1491
|
+
return this.is('paused') && this._canPlay();
|
|
1492
|
+
},
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* Sets the media is in a particular state
|
|
1496
|
+
* @param {String} name
|
|
1497
|
+
* @param {Boolean} value
|
|
1498
|
+
* @returns {mediaplayer}
|
|
1499
|
+
*/
|
|
1500
|
+
_setState(name, value) {
|
|
1501
|
+
value = !!value;
|
|
1502
|
+
|
|
1503
|
+
this.config.is[name] = value;
|
|
1504
|
+
|
|
1505
|
+
if (this.$component) {
|
|
1506
|
+
this.$component.toggleClass(name, value);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
return this;
|
|
1510
|
+
},
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Restores the media player from a particular state and resumes the playback
|
|
1514
|
+
* @param {String} stateName
|
|
1515
|
+
* @returns {mediaplayer}
|
|
1516
|
+
* @private
|
|
1517
|
+
*/
|
|
1518
|
+
_fromState(stateName) {
|
|
1519
|
+
this._setState(stateName, false);
|
|
1520
|
+
this.resume();
|
|
1521
|
+
|
|
1522
|
+
return this;
|
|
1523
|
+
},
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* Sets the media player to a particular state and pauses the playback
|
|
1527
|
+
* @param {String} stateName
|
|
1528
|
+
* @returns {mediaplayer}
|
|
1529
|
+
* @private
|
|
1530
|
+
*/
|
|
1531
|
+
_toState(stateName) {
|
|
1532
|
+
this.pause();
|
|
1533
|
+
this._setState(stateName, true);
|
|
1534
|
+
|
|
1535
|
+
return this;
|
|
1536
|
+
},
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Sets the playing state
|
|
1540
|
+
* @param {Boolean} state
|
|
1541
|
+
* @param {Boolean} [ended]
|
|
1542
|
+
* @returns {mediaplayer}
|
|
1543
|
+
* @private
|
|
1544
|
+
*/
|
|
1545
|
+
_playingState(state, ended) {
|
|
1546
|
+
this._setState('playing', !!state);
|
|
1547
|
+
this._setState('paused', !state);
|
|
1548
|
+
this._setState('ended', !!ended);
|
|
1549
|
+
|
|
1550
|
+
return this;
|
|
1551
|
+
},
|
|
1552
|
+
|
|
1553
|
+
/**
|
|
1554
|
+
* Executes a command onto the media
|
|
1555
|
+
* @param {String} command - The name of the command to execute
|
|
1556
|
+
* @param {*} args - additional arguments
|
|
1557
|
+
* @returns {*}
|
|
1558
|
+
* @private
|
|
1559
|
+
*/
|
|
1560
|
+
execute(command, ...args) {
|
|
1561
|
+
if (this.player && 'function' === typeof this.player[command]) {
|
|
1562
|
+
return this.player[command](...args);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1567
|
+
return mediaplayer.init(config);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* Tells if the browser can play audio and video
|
|
1572
|
+
* @param {String} [type] The type of media (audio or video)
|
|
1573
|
+
* @param {String} [mime] A media MIME type to check
|
|
1574
|
+
* @type {Boolean}
|
|
1575
|
+
*/
|
|
1576
|
+
mediaplayerFactory.canPlay = function canPlay(type, mime) {
|
|
1577
|
+
return support.canPlay(type, mime);
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1580
|
+
/**
|
|
1581
|
+
* Tells if the browser can play audio
|
|
1582
|
+
* @param {String} [mime] A media MIME type to check
|
|
1583
|
+
* @type {Boolean}
|
|
1584
|
+
*/
|
|
1585
|
+
mediaplayerFactory.canPlayAudio = function canPlayAudio(mime) {
|
|
1586
|
+
return support.canPlayAudio(mime);
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* Tells if the browser can play video
|
|
1591
|
+
* @param {String} [mime] A media MIME type to check
|
|
1592
|
+
* @type {Boolean}
|
|
1593
|
+
*/
|
|
1594
|
+
mediaplayerFactory.canPlayVideo = function canPlayVideo(mime) {
|
|
1595
|
+
return support.canPlayVideo(mime);
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
* Checks if the browser allows to control the media playback
|
|
1600
|
+
* @returns {Boolean}
|
|
1601
|
+
*/
|
|
1602
|
+
mediaplayerFactory.canControl = function canControl() {
|
|
1603
|
+
return support.canControl();
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1606
|
+
export default mediaplayerFactory;
|