Skip to content

Commit 101dc0d

Browse files
authored
Merge pull request #250 from mediaelement/feature/add-svg-icons-to-button
The pull request will be merged because the feature is needed immediately.
2 parents 9acdae4 + eedaa59 commit 101dc0d

File tree

11 files changed

+81
-36
lines changed

11 files changed

+81
-36
lines changed

changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Version History
22

3+
## *3.0.0 (2024/03/06)*
4+
### QUALITY PLUGIN
5+
#### New Feature: Inline SVG icons for quality button
6+
* Using `iconPath`: By setting the `iconPath` option, you can specify the path to your SVG icon. Once set, an inline SVG icon will be dynamically generated and displayed on the quality button. Further information can be found [here](docs/quality.md).
7+
* Default Behavior: If you choose not to utilize the `iconPath` option, the quality button will continue to operate as before, displaying the default quality value as its text.
8+
9+
### A11Y PLUGIN
10+
#### New Feature: Inline SVG icons for audio and video description buttons
11+
* Setting `iconSpritePath`: This option enables you to define the path to your SVG icon sprite. When specified, it allows for dynamic generation of inline SVG icons, which will be displayed on the audio and/or video description buttons. Further information can be found [here](docs/a11y.md).
12+
* Important CSS Adjustment: If you're planning to use `iconSpritePath` and your current setup includes background icons defined in CSS, you'll need to make a small but crucial update to your CSS file. Please remove any CSS rules that apply background icons to these buttons. This step is necessary to prevent the display of both SVG and background icons simultaneously.
13+
314
## *2.6.7 (2023/08/02)*
415
### QUALITY PLUGIN
516
* The `cleanMediaSource` function was updated to avoid captions being

demo/a11y.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<link rel="icon" href="favicon.ico" type="image/x-icon">
99

1010
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
11-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaelementplayer.css">
11+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mediaelement/7.0.3/mediaelementplayer.css">
1212
<link rel="stylesheet" href="../dist/a11y/a11y.css">
1313
<link rel="stylesheet" href="demo.css">
1414
</head>

demo/mejs-a11y-icons.svg

Lines changed: 9 additions & 0 deletions
Loading

demo/mejs-quality.svg

Lines changed: 20 additions & 0 deletions
Loading

docs/a11y.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Parameter | Type | Default | Description
1414
`data-video-description` | array | null | An array of video source objects like `{ src: "description.mp4", type: "video/mp4" }`. This plugin will evaluate the best matching type out of the array.
1515
`data-audio-description` | array | null | An array of audio description source objects like `{ src: "description.mp3", type: "audio/mp3" }`. This plugin will evaluate the best matching type out of the array.
1616
`data-audio-description-voiceover` | boolean | false | If set as data attribute only or with value `true` audio description will be started in voice-over mode.
17+
`iconSpritePath` | string | `mejs-a11y-icons.svg` | Path for the SVG icon sprite file.
1718

1819
#### Audio-description node
1920
The Audio description node is bound to the MediaElement.js object at `mejs.audioDescription.node`, like the original node is bound under `mejs.node`.

docs/quality.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ Parameter | Type | Default | Description
1616
------ | --------- | ------- | --------
1717
defaultQuality | string | `null` | Initial media quality; if `null`, it will take the first available source
1818
qualityText | string | `null` | Title for Quality button for WARIA purposes
19-
qualityChangeCallback | callback | | Action that will be executed as soon as the user change video quality; passes 3 arguments: media (the wrapper that mimics all the native events/properties/methods for all renderers), node (the original HTML video, audio or iframe tag where the media was loaded originally and string newQuality
19+
qualityChangeCallback | callback | | Action that will be executed as soon as the user change video quality; passes 3 arguments: media (the wrapper that mimics all the native events/properties/methods for all renderers), node (the original HTML video, audio or iframe tag where the media was loaded originally and string newQuality
20+
iconPath | string | `mejs-quality-icon.svg` | Path for the SVG icon file

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mediaelement-plugins",
3-
"version": "2.6.7",
3+
"version": "3.0.0",
44
"repository": {
55
"type": "git",
66
"url": "https://github.com/mediaelement/mediaelement-plugins.git"

src/a11y/a11y.css

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,3 @@
1-
.mejs-video-description-button > button,
2-
.mejs__video-description-button > button,
3-
.mejs-audio-description-button > button,
4-
.mejs__audio-description-button > button {
5-
background-repeat: no-repeat;
6-
background-size: contain;
7-
opacity: 0.7;
8-
}
9-
10-
.mejs-video-description-button.video-description-on > button,
11-
.mejs__video-description-button.video-description-on > button,
12-
.mejs-audio-description-button.audio-description-on > button,
13-
.mejs__audio-description-button.audio-description-on > button {
14-
opacity: 1;
15-
}
16-
17-
.mejs-video-description-button > button,
18-
.mejs__video-description-button > button {
19-
background-image: url('video-description-icon.svg');
20-
}
21-
22-
.mejs-audio-description-button > button,
23-
.mejs__audio-description-button > button {
24-
background-image: url('audio-description-icon.svg');
25-
}
26-
271
.mejs-volume-button.hidden,
282
.mejs__volume-button.hidden {
293
display: none;

src/a11y/a11y.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ Object.assign(mejs.MepDefaults, {
5151
* @type {Boolean}
5252
*/
5353
audioDescriptionCanPlay: false,
54+
55+
/**
56+
* The path where the icon sprite is located
57+
* @type {String}
58+
*/
59+
iconSpritePath: 'mejs-a11y-icons.svg',
5460
});
5561

5662

@@ -86,18 +92,32 @@ Object.assign(MediaElementPlayer.prototype, {
8692
return [...parentNode.childNodes].find(node => node.className.indexOf(className) > -1);
8793
},
8894

95+
/**
96+
* Generates an HTML for an SVG icon.
97+
* @private
98+
* @param {String} id - ID of the MediaElement player
99+
* @param {String} classPrefix - Prefix for the class attribute
100+
* @param {String} iconSpritePath - Path to the SVG sprite containing icons
101+
* @param {String} iconId - Specific ID of the icon within the SVG sprite
102+
* @returns {String} The complete HTML string for the SVG element
103+
*/
104+
_generateIconHtml(id, classPrefix, iconSpritePath, iconId) {
105+
return `<svg xmlns="http://www.w3.org/2000/svg" id="${id}" class="${classPrefix}${iconId}" aria-hidden="true" focusable="false">
106+
<use xlink:href="${iconSpritePath}#${iconId}"></use></svg>`;
107+
},
108+
89109
/**
90110
* Create audio description button and bind events
91111
* @private
92112
* @returns {Undefined}
93113
*/
94114
_createAudioDescription() {
95115
const t = this;
96-
116+
const iconHtml = t._generateIconHtml(t.id, t.options.classPrefix, t.options.iconSpritePath, 'icon-audio');
97117
const audioDescriptionTitle = mejs.i18n.t('mejs.a11y-audio-description');
98118
const audioDescriptionButton = document.createElement('div');
99119
audioDescriptionButton.className = `${t.options.classPrefix}button ${t.options.classPrefix}audio-description-button`;
100-
audioDescriptionButton.innerHTML = `<button type="button" aria-controls="${t.id}" title="${audioDescriptionTitle}" aria-label="${audioDescriptionTitle}" tabindex="0"></button>`;
120+
audioDescriptionButton.innerHTML = `<button type="button" aria-controls="${t.id}" title="${audioDescriptionTitle}" aria-label="${audioDescriptionTitle}" tabindex="0">${iconHtml}</button>`;
101121

102122
t.addControlElement(audioDescriptionButton, 'audio-description');
103123

@@ -116,10 +136,11 @@ Object.assign(MediaElementPlayer.prototype, {
116136
*/
117137
_createVideoDescription() {
118138
const t = this;
139+
const iconHtml = t._generateIconHtml(t.id, t.options.classPrefix, t.options.iconSpritePath, 'icon-video');
119140
const videoDescriptionTitle = mejs.i18n.t('mejs.a11y-video-description');
120141
const videoDescriptionButton = document.createElement('div');
121142
videoDescriptionButton.className = `${t.options.classPrefix}button ${t.options.classPrefix}video-description-button`;
122-
videoDescriptionButton.innerHTML = `<button type="button" aria-controls="${t.id}" title="${videoDescriptionTitle}" aria-label="${videoDescriptionTitle}" tabindex="0"></button>`;
143+
videoDescriptionButton.innerHTML = `<button type="button" aria-controls="${t.id}" title="${videoDescriptionTitle}" aria-label="${videoDescriptionTitle}" tabindex="0">${iconHtml}</button>`;
123144
t.addControlElement(videoDescriptionButton, 'video-description');
124145

125146
videoDescriptionButton.addEventListener('click', () => {

src/quality/quality.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ Object.assign(mejs.MepDefaults, {
3535
/**
3636
* @type Function
3737
*/
38-
qualityChangeCallback: null
38+
qualityChangeCallback: null,
39+
/**
40+
* The path where the icon file is located
41+
* @type {String}
42+
*/
43+
iconPath: 'mejs-quality.svg',
3944
});
4045

4146
Object.assign(MediaElementPlayer.prototype, {
@@ -151,9 +156,12 @@ Object.assign(MediaElementPlayer.prototype, {
151156

152157
// Get initial quality
153158
const generateId = Math.floor(Math.random() * 100);
159+
const iconHtml = `<svg xmlns="http://www.w3.org/2000/svg" id="${generateId}" class="${t.options.classPrefix}" aria-hidden="true" focusable="false">
160+
<use xlink:href="${t.options.iconPath}#default-icon"></use></svg>`;
154161
player.qualitiesContainer = document.createElement('div');
155162
player.qualitiesContainer.className = `${t.options.classPrefix}button ${t.options.classPrefix}qualities-button`;
156-
player.qualitiesContainer.innerHTML = `<button type="button" title="${qualityTitle}" aria-label="${qualityTitle}" aria-controls="qualitieslist-${generateId}" aria-expanded="false">${defaultValue}</button>` +
163+
player.qualitiesContainer.innerHTML = `<button type="button" title="${qualityTitle}" aria-label="${qualityTitle}" aria-controls="qualitieslist-${generateId}" aria-expanded="false">
164+
${t.options.iconPath ? iconHtml : defaultValue}</button>` +
157165
`<div class="${t.options.classPrefix}qualities-selector ${t.options.classPrefix}offscreen">` +
158166
`<ul class="${t.options.classPrefix}qualities-selector-list" id="qualitieslist-${generateId}" tabindex="-1"></ul></div>`;
159167

0 commit comments

Comments
 (0)