This commit is contained in:
Xes
2025-08-14 22:39:38 +02:00
parent 3641e93527
commit 5403f346e3
3370 changed files with 327179 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
/* Main Container */
.joubel-help-text-dialog-box {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
color: #555;
z-index: 2;
}
/* Background for help dialog */
.joubel-help-text-dialog-background {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: #555555;
opacity: 0.75;
}
/* ------------------------------------ Main content container ------------------------ */
.joubel-help-text-dialog-container {
display: block;
position: absolute;
top: 3.5%;
left: 20%;
width: 60%;
background: #fff;
opacity: 1;
max-height: 93%;
overflow: auto;
}
/* Header */
.joubel-help-text-header {
font-size: 1.1em;
border-bottom: 1px solid #E0E0E0;
padding: 0.75em 3em 0.75em 1.5em;
}
/* Body */
.joubel-help-text-body {
padding: 0.5em 1.5em;
margin: 1em 0 2em;
}
/* Close help dialog container */
.joubel-help-text-remove {
position: absolute;
right: 1.5em;
top: 0.8em;
cursor: pointer;
}
.joubel-help-text-remove:hover {
color: #333;
}
.joubel-help-text-remove:before {
font-family: 'H5PFontAwesome4';
content: '\f00d';
font-size: 1.3em;
right: 0em;
top: 0em;
}

View File

@@ -0,0 +1,152 @@
[class^="joubel-icon-"], [class*=" joubel-icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'H5PFontIcons' !important;
speak: none;
font-size: 1.5em;
line-height: 1.5em;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
word-wrap: normal;
cursor: pointer;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Comment icon */
.joubel-icon-comment-normal .h5p-icon-shadow:before {
content: "\e90a";
color: rgb(221, 221, 221);
}
.joubel-icon-comment-normal .h5p-icon-speech-bubble:before {
content: "\e908";
margin-left: -1em;
color: rgb(206, 114, 193);
}
.joubel-icon-comment-normal .h5p-icon-question:before {
content: "\e906";
margin-left: -1em;
color: rgb(255, 255, 255);
}
.joubel-icon-comment-normal:hover .h5p-icon-speech-bubble:before {
color: rgb(188, 92, 163);
}
.joubel-icon-comment-normal:active .h5p-icon-speech-bubble:before {
color: rgb(206, 114, 193);
position: relative;
top: 0.05em;
left: 0.05em;
}
.joubel-icon-comment-normal:active .h5p-icon-question:before {
position: relative;
top: 0.05em;
left: 0.05em;
}
/* Tip icon */
.joubel-icon-tip-normal .h5p-icon-shadow:before {
content: "\e90a";
color: rgb(221, 221, 221);
}
.joubel-icon-tip-normal .h5p-icon-speech-bubble:before {
content: "\e908";
margin-left: -1em;
color: rgb(53, 128, 195);
}
.joubel-icon-tip-normal .h5p-icon-info:before {
content: "\e905";
margin-left: -1em;
color: rgb(255, 255, 255);
}
.joubel-icon-tip-normal:hover .h5p-icon-speech-bubble:before {
color: rgb(72, 155, 213);
}
.joubel-icon-tip-normal:active .h5p-icon-speech-bubble:before {
color: rgb(72, 155, 213);
position: relative;
top: 0.05em;
left: 0.05em;
}
.joubel-icon-tip-normal:active .h5p-icon-info:before {
position: relative;
top: 0.05em;
left: 0.05em;
}
/* Edit icon */
.joubel-icon-edit .h5p-icon-circle:before {
content: "\e90d";
color: #1d74c8;
font-size: 1.5em;
}
.joubel-icon-edit .h5p-icon-pencil:before {
content: "\e90c";
color: #1d74c8;
font-size: 0.8em;
text-align: center;
position: absolute;
margin-left: -1.42em;
}
.joubel-icon-edit:hover .h5p-icon-circle:before {
content: "\e90e";
color: #1d74c8;
}
.joubel-icon-edit:hover .h5p-icon-pencil:before {
color: #ffffff;
}
/* Approve icon */
.joubel-icon-approve .h5p-icon-circle:before {
content: "\e90d";
color: #3ea35f;
font-size: 1.5em;
}
.joubel-icon-approve .h5p-icon-check:before {
content: "\e601";
color: #3ea35f;
font-size: 0.8em;
text-align: center;
position: absolute;
margin-left: -1.42em;
}
.joubel-icon-approve:hover .h5p-icon-circle:before {
content: "\e90e";
}
.joubel-icon-approve:hover .h5p-icon-check:before {
color: #ffffff;
}
/* Cancel icon */
.joubel-icon-cancel .h5p-icon-circle:before {
content: "\e90d";
color: #de3354;
font-size: 1.5em;
}
.joubel-icon-cancel .h5p-icon-cross:before {
content: "\e600";
color: #de3354;
font-size: 0.85em;
text-align: center;
position: absolute;
margin-left: -1.38em;
}
.joubel-icon-cancel:hover .h5p-icon-circle:before {
content: "\e90e";
}
.joubel-icon-cancel:hover .h5p-icon-cross:before {
color: #ffffff;
}
/* Custom override CSS */
.joubel-icon-tip-normal.help-icon:before {
font-family: 'H5PFontAwesome4';
content: "\f059";
}
/* Hide icon layers if using font awesome */
.joubel-icon-tip-normal.help-icon > span {
display: none;
}

View File

@@ -0,0 +1,25 @@
@CHARSET "UTF-8";
.joubel-message-dialog {
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 100%;
padding: .3em;
z-index: 11;
box-sizing: border-box;
-moz-box-sizing: border-box;
border-top: 1px solid #ffcd0d;
background-color: #fcffcc;
text-align: center;
font-size: 0.8em;
}
.joubel-message-dialog:before {
font-family: 'H5PFontAwesome4';
content: '\f05a';
padding-right: .5em;
font-size: 1.3em;
position: relative;
top: 0.1em;
}

View File

@@ -0,0 +1,56 @@
.joubel-progress-circle-wrapper{
display: inline-block;
padding: 0em 1em;
}
.joubel-progress-circle-percentage{
position: relative;
font-size: 1em;
}
.joubel-progress-circle-circle{
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-justify-content: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: relative;
top: 0.151em;
left: 0.151em;
text-align: center;
width: 2.7em;
height: 2.7em;
-webkit-border-radius: 100%; /* Safari 3-4, iOS 1-3.2, Android 1.6- */
-moz-border-radius: 100%; /* Firefox 1-3.6 */
border-radius: 100%; /* Opera 10.5, IE 9, Safari 5, Chrome, Firefox 4, iOS 4, Android 2.1+ */
background-color: #fff;
}
.joubel-progress-circle-active-border{
position: relative;
text-align: center;
width: 3em;
height: 3em;
-webkit-border-radius: 100%; /* Safari 3-4, iOS 1-3.2, Android 1.6- */
-moz-border-radius: 100%; /* Firefox 1-3.6 */
border-radius: 100%; /* Opera 10.5, IE 9, Safari 5, Chrome, Firefox 4, iOS 4, Android 2.1+ */
background-color: #1a73d9;
}

View File

@@ -0,0 +1,38 @@
.h5p-joubelui-progressbar {
width: 100%;
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: .25em;
background: #fff;
-webkit-box-shadow: 0px -1px 3px 0px rgba(1,72,118,0.75);
-moz-box-shadow: 0px -1px 3px 0px rgba(1,72,118,0.75);
box-shadow: 0px -1px 3px 0px rgba(1,72,118,0.75);
}
.h5p-joubelui-progressbar-slide-status-text {
font-size: 0;
width: 0;
height: 0;
}
.h5p-joubelui-progressbar-background {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0;
background-color: #014876;
-webkit-transition: width .3s ease-in-out;
-moz-transition: width .3s ease-in-out;
-o-transition: width .3s ease-in-out;
transition: width .3s ease-in-out;
}
.h5p-joubelui-drop {
z-index: 1;
}
.drop-element.h5p-joubelui-drop .drop-content {
padding: .2em .8em;
font-weight: bold;
}

View File

@@ -0,0 +1,159 @@
.h5p-joubelui-score-bar {
display: inline-flex;
display: -webkit-inline-flexbox;
display: -ms-inline-flexbox;
display: -webkit-inline-flex;
width: 15em;
max-width: 100%;
background: #fff;
border-radius: 1.5em;
padding: 0.625em;
border: 1px solid rgba(0, 0, 0, 0.08);
box-sizing: border-box;
position: relative;
}
.h5p-joubelui-score-bar-visuals {
flex: 1;
-webkit-box-flex: 1;
-webkit-flex: 1;
position: relative;
overflow: visible;
}
.h5p-joubelui-score-bar-progress-wrapper {
position: relative;
margin-right: 1.7em;
height: 0.917em;
border-top-left-radius: 1.5em;
border-bottom-left-radius: 1.5em;
background: #ddd;
}
.h5p-joubelui-score-bar-progress {
overflow: hidden;
white-space: nowrap;
color: transparent;
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0;
border-top-left-radius: .5em;
border-bottom-left-radius: .5em;
background: #48b57e; /* For browsers not supporting linear-gradient */
background: linear-gradient(to right, #52ca8d, #48b57e);
-webkit-transition: width 0.4s ease-in-out;
-moz-transition: width 0.4s ease-in-out;
-o-transition: width 0.4s ease-in-out;
transition: width 0.4s ease-in-out;
-webkit-backface-visibility: hidden;
}
/* The star */
.h5p-joubelui-score-bar-star {
height: 1.8em;
width: 2.1em;
position: absolute;
right: 0;
top: -0.4em;
overflow: visible;
}
.h5p-joubelui-score-bar-full-score.h5p-joubelui-score-bar-animation-active .h5p-joubelui-score-bar-star {
-webkit-animation: pound 0.8s 1;
animation: pound 0.8s 1;
}
@keyframes pound {
from {
transform: scale(0);
}
20% {
transform: scale(1.4);
}
60% {
transform: scale(0.8);
}
80% {
transform: scale(1.2);
}
to {
transform: scale(1);
}
}
@-webkit-keyframes pound {
from {
-webkit-transform: scale(0);
}
20% {
-webkit-transform: scale(1.4);
}
60% {
-webkit-transform: scale(0.8);
}
80% {
-webkit-transform: scale(1.2);
}
to {
-webkit-transform: scale(1);
}
}
/* Styling the star */
.h5p-joubelui-score-bar-star svg {
overflow: visible;
}
.h5p-joubelui-score-bar-star-shadow {
fill: #fff;
}
.h5p-joubelui-score-bar-star-border {
fill: none;
stroke: #ddd;
stroke-miterlimit: 10;
stroke-width: 3px;
}
.h5p-joubelui-score-bar-star-fill {
fill: #ddd;
}
.h5p-joubelui-score-bar-full-score .h5p-joubelui-score-bar-star-border {
stroke: #ab741a;
stroke-width: 6;
}
.h5p-joubelui-score-bar-star-fill-full-score {
visibility: hidden;
fill: #ffc80b;
}
.h5p-joubelui-score-bar-full-score .h5p-joubelui-score-bar-star-fill {
/* This will be a fallback for browsers not supporting the filter,
i.e. Safari */
fill: #ffc80b;
}
.h5p-joubelui-score-bar-full-score .h5p-joubelui-score-bar-star-fill-full-score {
visibility: visible;
}
/* The numeric part on the end (score / maxScore) */
.h5p-joubelui-score-numeric {
margin: 0em 0.4em;
font-size: 1.333em;
line-height: 0.7;
font-weight: bold;
}
.h5p-score-bar-has-help .h5p-joubelui-score-numeric {
/* Need more space when icon is displayed */
margin-right: 0.625em;
}
.h5p-joubelui-score-number {
color: #333;
}
.h5p-joubelui-score-number-separator {
color: #999;
padding: 0 0.1em;
}
.h5p-joubelui-score-bar .joubel-tip-container {
position: absolute;
top: 0.1em;
right: 0.3em;
font-size: 1em;
line-height: 1;
color: #1a73d9;
margin-right: 0.2em;
}
.h5p-joubelui-score-bar .joubel-tip-container > .help-icon {
font-size: 1em;
line-height: 1;
}

View File

@@ -0,0 +1,28 @@
.joubel-simple-rounded-button {
cursor: pointer;
display: inline-block;
margin: 0.25em 0;
padding: 0.3em 1.2em;
border-radius: 2em;
background-color: #1a73d9;
color: #FFFFFF;
}
.joubel-simple-rounded-button:hover,
.joubel-simple-rounded-button:focus {
background-color: #1356a3;
}
.joubel-simple-rounded-button:active {
position: relative;
background-color: #104888;
-webkit-box-shadow: inset 0 4px 0px #0e407a;
-moz-box-shadow: inset 0 4px 0px #0e407a;
box-shadow: inset 0 4px 0px #0e407a;
}
.joubel-simple-rounded-button:active .joubel-simple-rounded-button-text {
position: relative;
top: 2px;
}

View File

@@ -0,0 +1,29 @@
.h5p-joubel-ui-slider {
-webkit-transition: -webkit-transform 0.3s ease-in-out;
-moz-transition: -moz-transform 0.3s ease-in-out;
-ms-transition: -ms-transform 0.3s ease-in-out;
transition: transform 0.3s ease-in-out;
/* Avoid flickering */
-webkit-transform: translateX(0);
-moz-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
height: 100%;
}
.h5p-joubel-ui-slide {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: none;
}
.h5p-joubel-ui-slide.current {
display: block;
}

View File

@@ -0,0 +1,50 @@
@CHARSET "UTF-8";
.joubel-speech-bubble {
position: absolute;
color: #333;
z-index: 301;
margin-top: 0.45em;
min-width: 1em;
opacity: 0;
transition: opacity 0.5s ease;
}
.joubel-speech-bubble p {
margin: 0.75em 0;
}
.joubel-speech-bubble p:first-child {
margin-top: 0.25em;
}
.joubel-speech-bubble p:last-child {
margin-bottom: 0.25em;
}
.joubel-speech-bubble.show {
opacity: 1;
}
.joubel-speech-bubble-inner {
background: #fbfbfb;
box-shadow: 0 0 0.5em #2c2c2c;
border-radius: 0.5em;
padding: 0.2em 0.5em;
text-align: left;
position: relative;
word-wrap: break-word; /* Long words have to break */
z-index: 1;
}
.joubel-speech-bubble-inner-tail,
.joubel-speech-bubble-tail {
position: absolute;
/* Setting these in pixels is intentional. We use it when calculating placement of bubble */
width: 12px;
height: 12px;
background: #fbfbfb;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
z-index: -1;
}
.joubel-speech-bubble-tail {
box-shadow: 0 0 0.5em #2c2c2c;
}

View File

@@ -0,0 +1,56 @@
.joubel-tip-container {
display: inline-block;
font-weight: normal;
color: #777;
cursor: pointer;
}
.joubel-tip-container:hover {
color: #333;
}
.joubel-tip-container:focus {
outline: 0;
box-shadow: 0px 0px 5px 2px rgba(140,185,240,1);
outline: rgba(140,185,240,1) solid 1px;
}
.using-mouse .joubel-tip-container:focus {
box-shadow: none;
}
.joubel-tip-container.be-quiet:focus {
outline: none;
}
.joubel-tip-container.be-quiet {
pointer-events: none;
}
.joubel-tip-icon {
text-align: center;
font-size: 1.5em;
line-height: 1.5em;
width: 1.5em;
height: 1.5em;
pointer-events: none;
}
.joubel-tip-icon:before {
font-family: Joubel;
content: "\e888";
/* Better Font Rendering */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.joubel-tip-icon.help-icon:before {
font-family: 'H5PFontAwesome4';
content: "\f059";
}
.joubel-tip-container * {
pointer-events: none;
}
.joubel-tip-container .hidden-but-read {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
overflow: hidden;
}

View File

@@ -0,0 +1,96 @@
@CHARSET "UTF-8";
/* Styling of button */
.h5peditor .ui-dialog .h5p-joubelui-button,
.h5peditor .h5p-joubelui-button,
.h5p-joubelui-button {
font-size: 1em;
line-height: 1.2;
margin: 0 0.5em 1em;
padding: 0.5em 1.25em;
border-radius: 2em;
background: #1a73d9;
color: #ffffff;
cursor: pointer;
border: none;
box-shadow: none;
-webkit-transform: translateZ(0);
transform: translateZ(0);
display: inline-block;
text-align: center;
text-shadow: none;
text-decoration: none;
vertical-align: baseline;
}
.h5p-joubelui-button:first-child {
margin: 0 0.5em 1em 0;
}
.h5p-joubelui-button:last-child {
margin: 0 0 1em 0.5em;
}
.h5p-joubelui-button:first-child:last-child {
margin: 0 0 1em;
}
/* Truncated buttons */
.h5p-joubelui-button.truncated {
width: 2.235em;
height: 2.235em;
border-radius: 50%;
padding: 0;
text-align: center;
}
.h5p-joubelui-button.truncated:before {
padding: 0;
}
.h5p-joubelui-button:hover,
.h5p-joubelui-button:focus {
background: #1356a3;
color: #fff;
text-decoration: none;
-webkit-transition: initial;
-moz-transition: initial;
-o-transition: initial;
transition: initial;
}
.h5p-joubelui-button:active {
position: relative;
background: #104888;
-webkit-box-shadow: inset 0 4px 0 #0e407a;
-moz-box-shadow: inset 0 4px 0 #0e407a;
box-shadow: inset 0 4px 0 #0e407a;
}
.h5p-joubelui-button:active .h5p-joubelui-button-text {
position: relative;
top: 2px;
}
.h5p-joubelui-button:before {
font-family: 'H5PFontAwesome4';
padding-right: 0.5em;
}
.h5p-question-try-again:before {
content: "\F01E";
}
.h5p-question-show-solution:before {
content: "\F06E";
}
.h5p-question-check-answer:before {
content: "\F058";
}
/* Remove outline for all elements having tabIndex="-1"*/
.h5p-content [tabIndex="-1"] {
outline: none;
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="joubel" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe888;" horiz-adv-x="1091" d="M1015.937 644.199c10.751-33.596 16.126-69.207 16.126-105.491 0-232.483-231.139-421.291-516.031-421.291-38.971 0-188.136-26.205-296.315-46.362l-59.801-135.727c0 0 351.412 68.535 415.244 68.535 284.892 0 516.031 188.808 516.031 421.291-0.672 80.63-27.549 155.213-75.255 219.045z" />
<glyph unicode="&#xe889;" horiz-adv-x="1091" d="M1015.937 644.199c-57.113 181.417-259.36 315.801-499.906 315.801-284.892 0-516.031-188.808-516.031-421.291 0-117.585 59.129-223.748 154.541-300.346l-53.753-188.808c0 0 51.738 10.079 118.929 22.173 108.178 20.157 256.672 46.362 296.315 46.362 284.892 0 516.031 188.808 516.031 421.291 0 36.283-5.375 71.223-16.126 104.819z" />
<glyph unicode="&#xe900;" horiz-adv-x="1091" d="M1015.937 644.199c10.751-33.596 16.126-69.207 16.126-105.491 0-232.483-231.139-421.291-516.031-421.291-38.971 0-188.136-26.205-296.315-46.362l-59.801-135.727c0 0 351.412 68.535 415.244 68.535 284.892 0 516.031 188.808 516.031 421.291-0.672 80.63-27.549 155.213-75.255 219.045z" />
<glyph unicode="&#xe901;" horiz-adv-x="1091" d="M1015.937 644.199c-57.113 181.417-259.36 315.801-499.906 315.801-284.892 0-516.031-188.808-516.031-421.291 0-117.585 59.129-223.748 154.541-300.346l-53.753-188.808c0 0 51.738 10.079 118.929 22.173 108.178 20.157 256.672 46.362 296.315 46.362 284.892 0 516.031 188.808 516.031 421.291 0 36.283-5.375 71.223-16.126 104.819z" />
<glyph unicode="&#xe902;" horiz-adv-x="1091" d="M692.073 626.058c0-14.11-2.016-26.877-6.047-38.971s-8.735-22.173-13.438-29.564c-4.703-7.391-12.094-15.454-20.829-22.845-9.407-7.391-16.798-13.438-22.173-16.798s-13.438-8.063-23.517-13.438c-10.751-6.047-19.486-14.11-26.205-24.861s-10.751-19.486-10.751-25.533c0-4.031-1.344-8.735-4.703-12.766s-6.719-6.047-10.751-6.047h-92.052c-4.031 0-7.391 2.688-10.079 7.391s-4.031 9.407-4.031 14.11v17.47c0 21.501 8.063 41.659 24.861 60.472s34.94 32.924 55.097 41.659c15.454 6.719 26.205 14.11 32.252 21.501 6.719 7.391 9.407 17.47 9.407 28.892 0 10.751-6.047 20.157-18.142 28.22s-25.533 12.094-41.659 12.094c-16.798 0-30.236-4.031-41.659-11.423-8.735-6.719-22.845-20.829-40.987-44.346-3.36-4.031-7.391-6.047-12.094-6.047-3.36 0-6.047 1.344-9.407 3.36l-61.816 49.050c-3.36 2.688-5.375 6.047-6.047 9.407-0.672 4.031 0 7.391 2.016 10.751 40.987 67.864 100.787 102.131 178.058 102.131 20.829 0 40.987-4.031 61.816-12.094s39.643-18.814 56.441-31.58c16.798-13.438 30.236-29.564 40.987-49.050 10.079-19.486 15.454-39.643 15.454-61.144zM570.457 395.591v-92.052c0-4.031-1.344-7.391-4.703-10.751s-6.719-4.703-10.751-4.703h-92.052c-4.031 0-7.391 1.344-10.751 4.703s-4.703 6.719-4.703 10.751v92.052c0 4.031 1.344 7.391 4.703 10.751s6.719 4.703 10.751 4.703h92.052c4.031 0 7.391-1.344 10.751-4.703 3.36-2.688 4.703-6.719 4.703-10.751z" />
<glyph unicode="&#xe903;" horiz-adv-x="1091" d="M1015.937 644.199c10.751-33.596 16.126-69.207 16.126-105.491 0-232.483-231.139-421.291-516.031-421.291-38.971 0-188.136-26.205-296.315-46.362l-59.801-135.727c0 0 351.412 68.535 415.244 68.535 284.892 0 516.031 188.808 516.031 421.291-0.672 80.63-27.549 155.213-75.255 219.045z" />
<glyph unicode="&#xe904;" horiz-adv-x="1091" d="M1030.719 622.698c6.719-14.782 12.766-30.236 18.142-45.69 10.751-33.596 16.126-68.535 16.126-104.819 0-232.483-231.139-421.291-516.031-421.291-39.643 0-188.136-26.205-296.315-46.362-26.205-4.703-49.722-8.735-68.535-12.766l-24.861-55.769c0 0 351.412 68.535 415.244 68.535 284.892 0 516.031 188.808 516.031 421.291 0 71.223-21.501 138.415-59.801 196.871z" />
<glyph unicode="&#xe905;" horiz-adv-x="1091" d="M1048.861 577.008c-4.703 15.454-10.751 30.908-18.142 45.69-74.583 157.9-262.047 270.11-481.764 270.11-284.892 0-516.031-188.808-516.031-421.291 0-117.585 59.129-223.748 154.541-300.346l-53.753-188.808c0 0 19.486 4.031 50.394 9.407 19.486 3.36 43.003 8.063 68.535 12.766 108.178 20.157 256.672 46.362 296.315 46.362 284.892 0 516.031 188.808 516.031 421.291 0 36.283-5.375 71.223-16.126 104.819z" />
<glyph unicode="&#xe906;" horiz-adv-x="1091" d="M724.997 558.866c0-14.11-2.016-26.877-6.047-38.971s-8.735-22.173-13.438-29.564c-4.703-7.391-12.094-15.454-20.829-22.845-9.407-7.391-16.798-13.438-22.173-16.798s-13.438-8.063-23.517-13.438c-10.751-6.047-19.486-14.11-26.205-24.861s-10.751-19.486-10.751-25.533c0-4.031-1.344-8.735-4.703-12.766s-6.719-6.047-10.751-6.047h-92.052c-4.031 0-7.391 2.688-10.079 7.391s-4.031 9.407-4.031 14.11v17.47c0 21.501 8.063 41.659 24.861 60.472s34.94 32.924 55.097 41.659c15.454 6.719 26.205 14.11 32.252 21.501 6.719 7.391 9.407 17.47 9.407 28.892 0 10.751-6.047 20.157-18.142 28.22s-25.533 12.094-41.659 12.094c-16.798 0-30.236-4.031-41.659-11.423-8.735-6.719-22.845-20.829-40.987-44.346-3.36-4.031-7.391-6.047-12.094-6.047-3.36 0-6.047 1.344-9.407 3.36l-63.16 48.378c-3.36 2.688-5.375 6.047-6.047 9.407-0.672 4.031 0 7.391 2.016 10.751 40.987 67.864 100.787 102.131 178.058 102.131 20.829 0 40.987-4.031 61.816-12.094s39.643-18.814 56.441-31.58c16.798-13.438 30.236-29.564 40.987-49.050 11.423-18.814 16.798-38.971 16.798-60.472zM603.381 328.399v-92.052c0-4.031-1.344-7.391-4.703-10.751s-6.719-4.703-10.751-4.703h-92.052c-4.031 0-7.391 1.344-10.751 4.703s-4.703 6.719-4.703 10.751v92.052c0 4.031 1.344 7.391 4.703 10.751s6.719 4.703 10.751 4.703h92.052c4.031 0 7.391-1.344 10.751-4.703s4.703-6.719 4.703-10.751z" />
<glyph unicode="&#xe907;" glyph-name="examples" horiz-adv-x="1421" d="M952.805 828.213c-71.195 0-137.846-18.178-196.923-56.047v-615.006c60.592 30.296 125.728 45.444 196.923 45.444 121.183 0 204.497-33.325 315.077-78.769l-78.769 654.391c-72.71 33.325-157.538 49.988-236.308 49.988zM474.13 828.213c-78.769 0-163.598-16.663-236.308-49.988l-78.769-654.391c110.58 45.444 193.893 78.769 315.077 78.769 74.225 0 139.361-15.148 202.982-48.473v613.491c-59.077 42.414-130.272 60.592-202.982 60.592zM116.639 858.509l-116.639-901.302c119.669-39.385 230.249 93.917 451.408 90.888 180.26-3.030 260.544-109.065 260.544-109.065s103.006 119.669 289.325 115.124c127.243-3.030 333.254-169.657 419.598-99.976l-113.609 902.817c0 0-190.864 103.006-296.899 101.491-154.509-4.544-289.325-63.621-289.325-63.621s-165.112 71.195-271.148 65.136c-186.32-13.633-333.254-101.491-333.254-101.491zM1204.26 52.639c-80.284 36.355-163.598 59.077-251.456 59.077-90.888 0-174.201-30.296-239.337-95.432-65.136 65.136-148.45 95.432-239.337 95.432-87.858 0-171.172-21.207-251.456-57.562-51.503-22.722-104.521-36.355-162.083-36.355h-3.030l99.976 814.959c90.888 51.503 202.982 81.799 309.018 81.799 86.343 0 175.716-18.178 248.426-68.166 72.71 49.988 162.083 68.166 248.426 68.166 106.036 0 216.615-28.781 309.018-81.799l99.976-814.959c-62.107-1.515-113.609 10.604-168.142 34.84z" />
<glyph unicode="&#xe908;" horiz-adv-x="1091" d="M1015.937 644.199c10.751-33.596 16.126-69.207 16.126-105.491 0-232.483-231.139-421.291-516.031-421.291-38.971 0-188.136-26.205-296.315-46.362l-59.801-135.727c0 0 351.412 68.535 415.244 68.535 284.892 0 516.031 188.808 516.031 421.291-0.672 80.63-27.549 155.213-75.255 219.045z" />
<glyph unicode="&#xe909;" horiz-adv-x="1091" d="M633.617 361.995v-49.050c0-6.719-2.688-12.766-7.391-17.47s-10.751-7.391-17.47-7.391h-196.871c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v49.050c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h24.861v147.822h-24.861c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v49.050c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h147.822c6.719 0 12.094-2.688 17.47-7.391 4.703-4.703 7.391-10.751 7.391-17.47v-221.732h24.861c6.719 0 12.094-2.688 17.47-7.391 4.031-5.375 6.719-10.751 6.719-17.47zM583.895 804.787v-73.911c0-6.719-2.688-12.766-7.391-17.47s-10.751-7.391-17.47-7.391h-98.1c-6.719 0-12.766 2.688-17.47 7.391s-6.719 10.751-6.719 17.47v73.911c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h98.1c6.719 0 12.094-2.688 17.47-7.391 4.703-5.375 6.719-10.751 6.719-17.47z" />
<glyph unicode="&#xe90a;" horiz-adv-x="1091" d="M663.181 295.475v-49.050c0-6.719-2.688-12.766-7.391-17.47s-10.751-7.391-17.47-7.391h-196.871c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v49.050c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h24.861v147.822h-24.861c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v49.050c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h147.822c6.719 0 12.094-2.688 17.47-7.391s7.391-10.751 7.391-17.47v-221.732h24.189c6.719 0 12.094-2.688 17.47-7.391 4.703-5.375 7.391-10.751 7.391-17.47zM613.459 738.268v-73.911c0-6.719-2.688-12.766-7.391-17.47s-10.751-7.391-17.47-7.391h-98.1c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v73.911c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h98.1c6.719 0 12.094-2.688 17.47-7.391s7.391-10.751 7.391-17.47z" />
<glyph unicode="&#xe90b;" horiz-adv-x="1091" d="M1021.312 636.136c9.407-18.814 17.47-37.627 24.189-57.785 10.751-34.268 16.126-69.207 16.126-104.819 0-232.483-231.139-421.291-516.031-421.291-39.643 0-188.136-26.205-296.315-46.362-24.189-4.031-46.362-8.063-64.504-12.094l-25.533-57.785c0 0 351.412 68.535 415.244 68.535 284.892 0 516.031 188.808 516.031 421.291 0 77.27-24.861 148.493-69.207 210.31z" />
<glyph unicode="&#xe90c;" horiz-adv-x="1091" d="M1045.501 578.352c-6.047 20.157-14.11 38.971-24.189 57.785-77.942 151.181-262.047 258.016-475.717 258.016-284.892 0-516.031-188.808-516.031-421.291 0-117.585 59.129-224.42 154.541-300.346l-53.753-188.808c0 0 21.501 4.031 54.425 10.079 18.142 3.36 40.315 7.391 64.504 12.094 108.178 20.157 256.672 46.362 296.315 46.362 284.892 0 516.031 188.808 516.031 421.291 0 35.612-5.375 70.551-16.126 104.819z" />
<glyph unicode="&#xe90d;" glyph-name="tutorial" horiz-adv-x="1537" d="M1521.881 683.565l-747.565-234.821c-2.972 0-4.459 0-5.945 0s-4.459 0-5.945 0l-435.46 136.731c-38.642-29.724-65.393-104.035-69.852-193.208 25.266-14.862 41.614-41.614 41.614-72.824 0-29.724-14.862-56.476-38.642-71.338l38.642-288.325c0-5.945-1.486-11.89-5.945-16.348s-10.403-7.431-16.348-7.431h-127.814c-5.945 0-11.89 2.972-16.348 7.431s-5.945 10.403-5.945 16.348l38.642 288.325c-23.779 14.862-38.642 41.614-38.642 71.338 0 32.697 17.835 59.448 43.1 74.311 4.459 78.769 23.779 161.997 65.393 219.959l-219.959 69.852c-8.917 2.972-14.862 11.89-14.862 20.807s5.945 17.835 14.862 20.807l746.078 234.821c2.972 0 4.459 0 5.945 0s4.459 0 5.945 0l747.565-234.821c8.917-2.972 14.862-11.89 14.862-20.807s-4.459-17.835-13.376-20.807zM1194.914 276.343c5.945-93.631-191.721-170.914-426.543-170.914-234.821 1.486-432.488 77.283-426.543 170.914l11.89 211.042 383.443-120.383c10.403-2.972 20.807-4.459 32.697-4.459s20.807 1.486 32.697 4.459l383.443 120.383 8.917-211.042z" />
<glyph unicode="&#xe90e;" horiz-adv-x="1091" d="M1015.937 644.199c-57.113 181.417-259.36 315.801-499.906 315.801-284.892 0-516.031-188.808-516.031-421.291 0-117.585 59.129-223.748 154.541-300.346l-53.753-188.808c0 0 51.738 10.079 118.929 22.173 108.178 20.157 256.672 46.362 296.315 46.362 284.892 0 516.031 188.808 516.031 421.291 0 36.283-5.375 71.223-16.126 104.819z" />
<glyph unicode="&#xe90f;" horiz-adv-x="1091" d="M692.073 626.058c0-14.11-2.016-26.877-6.047-38.971s-8.735-22.173-13.438-29.564c-4.703-7.391-12.094-15.454-20.829-22.845-9.407-7.391-16.798-13.438-22.173-16.798s-13.438-8.063-23.517-13.438c-10.751-6.047-19.486-14.11-26.205-24.861s-10.751-19.486-10.751-25.533c0-4.031-1.344-8.735-4.703-12.766s-6.719-6.047-10.751-6.047h-92.052c-4.031 0-7.391 2.688-10.079 7.391s-4.031 9.407-4.031 14.11v17.47c0 21.501 8.063 41.659 24.861 60.472s34.94 32.924 55.097 41.659c15.454 6.719 26.205 14.11 32.252 21.501 6.719 7.391 9.407 17.47 9.407 28.892 0 10.751-6.047 20.157-18.142 28.22s-25.533 12.094-41.659 12.094c-16.798 0-30.236-4.031-41.659-11.423-8.735-6.719-22.845-20.829-40.987-44.346-3.36-4.031-7.391-6.047-12.094-6.047-3.36 0-6.047 1.344-9.407 3.36l-61.816 49.050c-3.36 2.688-5.375 6.047-6.047 9.407-0.672 4.031 0 7.391 2.016 10.751 40.987 67.864 100.787 102.131 178.058 102.131 20.829 0 40.987-4.031 61.816-12.094s39.643-18.814 56.441-31.58c16.798-13.438 30.236-29.564 40.987-49.050 10.079-19.486 15.454-39.643 15.454-61.144zM570.457 395.591v-92.052c0-4.031-1.344-7.391-4.703-10.751s-6.719-4.703-10.751-4.703h-92.052c-4.031 0-7.391 1.344-10.751 4.703s-4.703 6.719-4.703 10.751v92.052c0 4.031 1.344 7.391 4.703 10.751s6.719 4.703 10.751 4.703h92.052c4.031 0 7.391-1.344 10.751-4.703 3.36-2.688 4.703-6.719 4.703-10.751z" />
<glyph unicode="&#xe910;" horiz-adv-x="1091" d="M1015.937 644.199c-57.113 181.417-259.36 315.801-499.906 315.801-284.892 0-516.031-188.808-516.031-421.291 0-117.585 59.129-223.748 154.541-300.346l-53.753-188.808c0 0 51.738 10.079 118.929 22.173 108.178 20.157 256.672 46.362 296.315 46.362 284.892 0 516.031 188.808 516.031 421.291 0 36.283-5.375 71.223-16.126 104.819z" />
<glyph unicode="&#xe911;" horiz-adv-x="1091" d="M633.617 361.995v-49.050c0-6.719-2.688-12.766-7.391-17.47s-10.751-7.391-17.47-7.391h-196.871c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v49.050c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h24.861v147.822h-24.861c-6.719 0-12.766 2.688-17.47 7.391s-7.391 10.751-7.391 17.47v49.050c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h147.822c6.719 0 12.094-2.688 17.47-7.391 4.703-4.703 7.391-10.751 7.391-17.47v-221.732h24.861c6.719 0 12.094-2.688 17.47-7.391 4.031-5.375 6.719-10.751 6.719-17.47zM583.895 804.787v-73.911c0-6.719-2.688-12.766-7.391-17.47s-10.751-7.391-17.47-7.391h-98.1c-6.719 0-12.766 2.688-17.47 7.391s-6.719 10.751-6.719 17.47v73.911c0 6.719 2.688 12.094 7.391 17.47 4.703 4.703 10.751 7.391 17.47 7.391h98.1c6.719 0 12.094-2.688 17.47-7.391 4.703-5.375 6.719-10.751 6.719-17.47z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,93 @@
var H5P = H5P || {};
/**
* Class responsible for creating a help text dialog
*/
H5P.JoubelHelpTextDialog = (function ($) {
var numInstances = 0;
/**
* Display a pop-up containing a message.
*
* @param {H5P.jQuery} $container The container which message dialog will be appended to
* @param {string} message The message
* @param {string} closeButtonTitle The title for the close button
* @return {H5P.jQuery}
*/
function JoubelHelpTextDialog(header, message, closeButtonTitle) {
H5P.EventDispatcher.call(this);
var self = this;
numInstances++;
var headerId = 'joubel-help-text-header-' + numInstances;
var helpTextId = 'joubel-help-text-body-' + numInstances;
var $helpTextDialogBox = $('<div>', {
'class': 'joubel-help-text-dialog-box',
'role': 'dialog',
'aria-labelledby': headerId,
'aria-describedby': helpTextId
});
$('<div>', {
'class': 'joubel-help-text-dialog-background'
}).appendTo($helpTextDialogBox);
var $helpTextDialogContainer = $('<div>', {
'class': 'joubel-help-text-dialog-container'
}).appendTo($helpTextDialogBox);
$('<div>', {
'class': 'joubel-help-text-header',
'id': headerId,
'role': 'header',
'html': header
}).appendTo($helpTextDialogContainer);
$('<div>', {
'class': 'joubel-help-text-body',
'id': helpTextId,
'html': message,
'role': 'document',
'tabindex': 0
}).appendTo($helpTextDialogContainer);
var handleClose = function () {
$helpTextDialogBox.remove();
self.trigger('closed');
};
var $closeButton = $('<div>', {
'class': 'joubel-help-text-remove',
'role': 'button',
'title': closeButtonTitle,
'tabindex': 1,
'click': handleClose,
'keydown': function (event) {
// 32 - space, 13 - enter
if ([32, 13].indexOf(event.which) !== -1) {
event.preventDefault();
handleClose();
}
}
}).appendTo($helpTextDialogContainer);
/**
* Get the DOM element
* @return {HTMLElement}
*/
self.getElement = function () {
return $helpTextDialogBox;
};
self.focus = function () {
$closeButton.focus();
};
}
JoubelHelpTextDialog.prototype = Object.create(H5P.EventDispatcher.prototype);
JoubelHelpTextDialog.prototype.constructor = JoubelHelpTextDialog;
return JoubelHelpTextDialog;
}(H5P.jQuery));

View File

@@ -0,0 +1,38 @@
var H5P = H5P || {};
/**
* Class responsible for creating auto-disappearing dialogs
*/
H5P.JoubelMessageDialog = (function ($) {
/**
* Display a pop-up containing a message.
*
* @param {H5P.jQuery} $container The container which message dialog will be appended to
* @param {string} message The message
* @return {H5P.jQuery}
*/
function JoubelMessageDialog ($container, message) {
var timeout;
var removeDialog = function () {
$warning.remove();
clearTimeout(timeout);
$container.off('click.messageDialog');
};
// Create warning popup:
var $warning = $('<div/>', {
'class': 'joubel-message-dialog',
text: message
}).appendTo($container);
// Remove after 3 seconds or if user clicks anywhere in $container:
timeout = setTimeout(removeDialog, 3000);
$container.on('click.messageDialog', removeDialog);
return $warning;
}
return JoubelMessageDialog;
})(H5P.jQuery);

View File

@@ -0,0 +1,159 @@
var H5P = H5P || {};
/**
* Class responsible for creating a circular progress bar
*/
H5P.JoubelProgressCircle = (function ($) {
/**
* Constructor for the Progress Circle
*
* @param {Number} number The amount of progress to display
* @param {string} progressColor Color for the progress meter
* @param {string} backgroundColor Color behind the progress meter
*/
function ProgressCircle(number, progressColor, fillColor, backgroundColor) {
progressColor = progressColor || '#1a73d9';
fillColor = fillColor || '#f0f0f0';
backgroundColor = backgroundColor || '#ffffff';
var progressColorRGB = this.hexToRgb(progressColor);
//Verify number
try {
number = Number(number);
if (number === '') {
throw 'is empty';
}
if (isNaN(number)) {
throw 'is not a number';
}
} catch (e) {
number = 'err';
}
//Draw circle
if (number > 100) {
number = 100;
}
// We can not use rgba, since they will stack on top of each other.
// Instead we create the equivalent of the rgba color
// and applies this to the activeborder and background color.
var progressColorString = 'rgb(' + parseInt(progressColorRGB.r, 10) +
',' + parseInt(progressColorRGB.g, 10) +
',' + parseInt(progressColorRGB.b, 10) + ')';
// Circle wrapper
var $wrapper = $('<div/>', {
'class': "joubel-progress-circle-wrapper"
});
//Active border indicates progress
var $activeBorder = $('<div/>', {
'class': "joubel-progress-circle-active-border"
}).appendTo($wrapper);
//Background circle
var $backgroundCircle = $('<div/>', {
'class': "joubel-progress-circle-circle"
}).appendTo($activeBorder);
//Progress text/number
$('<span/>', {
'text': number + '%',
'class': "joubel-progress-circle-percentage"
}).appendTo($backgroundCircle);
var deg = number * 3.6;
if (deg <= 180) {
$activeBorder.css('background-image',
'linear-gradient(' + (90 + deg) + 'deg, transparent 50%, ' + fillColor + ' 50%),' +
'linear-gradient(90deg, ' + fillColor + ' 50%, transparent 50%)')
.css('border', '2px solid' + backgroundColor)
.css('background-color', progressColorString);
} else {
$activeBorder.css('background-image',
'linear-gradient(' + (deg - 90) + 'deg, transparent 50%, ' + progressColorString + ' 50%),' +
'linear-gradient(90deg, ' + fillColor + ' 50%, transparent 50%)')
.css('border', '2px solid' + backgroundColor)
.css('background-color', progressColorString);
}
this.$activeBorder = $activeBorder;
this.$backgroundCircle = $backgroundCircle;
this.$wrapper = $wrapper;
this.initResizeFunctionality();
return $wrapper;
}
/**
* Initializes resize functionality for the progress circle
*/
ProgressCircle.prototype.initResizeFunctionality = function () {
var self = this;
$(window).resize(function () {
// Queue resize
setTimeout(function () {
self.resize();
});
});
// First resize
setTimeout(function () {
self.resize();
}, 0);
};
/**
* Resize function makes progress circle grow or shrink relative to parent container
*/
ProgressCircle.prototype.resize = function () {
var $parent = this.$wrapper.parent();
if ($parent !== undefined && $parent) {
// Measurements
var fontSize = parseInt($parent.css('font-size'), 10);
// Static sizes
var fontSizeMultiplum = 3.75;
var progressCircleWidthPx = parseInt((fontSize / 4.5), 10) % 2 === 0 ? parseInt((fontSize / 4.5), 10) + 4 : parseInt((fontSize / 4.5), 10) + 5;
var progressCircleOffset = progressCircleWidthPx / 2;
var width = fontSize * fontSizeMultiplum;
var height = fontSize * fontSizeMultiplum;
this.$activeBorder.css({
'width': width,
'height': height
});
this.$backgroundCircle.css({
'width': width - progressCircleWidthPx,
'height': height - progressCircleWidthPx,
'top': progressCircleOffset,
'left': progressCircleOffset
});
}
};
/**
* Hex to RGB conversion
* @param hex
* @returns {{r: Number, g: Number, b: Number}}
*/
ProgressCircle.prototype.hexToRgb = function (hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
};
return ProgressCircle;
}(H5P.jQuery));

View File

@@ -0,0 +1,190 @@
var H5P = H5P || {};
H5P.JoubelProgressbar = (function ($) {
/**
* Joubel progressbar class
* @method JoubelProgressbar
* @constructor
* @param {number} steps Number of steps
* @param {Object} [options] Additional options
* @param {boolean} [options.disableAria] Disable readspeaker assistance
* @param {string} [options.progressText] A progress text for describing
* current progress out of total progress for readspeakers.
* e.g. "Slide :num of :total"
*/
function JoubelProgressbar(steps, options) {
H5P.EventDispatcher.call(this);
var self = this;
this.options = $.extend({
progressText: 'Slide :num of :total'
}, options);
this.currentStep = 0;
this.steps = steps;
this.$progressbar = $('<div>', {
'class': 'h5p-joubelui-progressbar',
on: {
click: function () {
self.toggleTooltip();
return false;
},
mouseenter: function () {
self.showTooltip();
},
mouseleave: function () {
setTimeout(function () {
self.hideTooltip();
}, 1500);
}
}
});
this.$background = $('<div>', {
'class': 'h5p-joubelui-progressbar-background'
}).appendTo(this.$progressbar);
$('body').click(function () {
self.toggleTooltip(true);
});
}
JoubelProgressbar.prototype = Object.create(H5P.EventDispatcher.prototype);
JoubelProgressbar.prototype.constructor = JoubelProgressbar;
/**
* Display tooltip
* @method showTooltip
*/
JoubelProgressbar.prototype.showTooltip = function () {
var self = this;
if (this.currentStep === 0 || this.tooltip !== undefined) {
return;
}
var parentWidth = self.$progressbar.offset().left + self.$progressbar.width();
this.tooltip = new H5P.Drop({
target: this.$background.get(0),
content: this.currentStep + '/' + this.steps,
classes: 'drop-theme-arrows-bounce h5p-joubelui-drop',
position: 'top right',
openOn: 'always',
tetherOptions: {
attachment: 'bottom center',
targetAttachment: 'top right'
}
});
this.tooltip.on('open', function () {
var $drop = $(self.tooltip.drop);
var left = $drop.position().left;
var dropWidth = $drop.width();
// Need to handle drops getting outside of the progressbar:
if (left < 0) {
$drop.css({marginLeft: (-left) + 'px'});
}
else if (left + dropWidth > parentWidth) {
$drop.css({marginLeft: (parentWidth - (left + dropWidth)) + 'px'});
}
});
};
JoubelProgressbar.prototype.updateAria = function () {
var self = this;
if (this.options.disableAria) {
return;
}
if (!this.$currentStatus) {
this.$currentStatus = $('<div>', {
'class': 'h5p-joubelui-progressbar-slide-status-text',
'aria-live': 'assertive'
}).appendTo(this.$progressbar);
}
var interpolatedProgressText = self.options.progressText
.replace(':num', self.currentStep)
.replace(':total', self.steps);
this.$currentStatus.html(interpolatedProgressText);
};
/**
* Hides tooltip
* @method hideTooltip
*/
JoubelProgressbar.prototype.hideTooltip = function () {
if (this.tooltip !== undefined) {
this.tooltip.remove();
this.tooltip.destroy();
this.tooltip = undefined;
}
};
/**
* Toggles tooltip-visibility
* @method toggleTooltip
* @param {boolean} [closeOnly] Don't show, only close if open
*/
JoubelProgressbar.prototype.toggleTooltip = function (closeOnly) {
if (this.tooltip === undefined && !closeOnly) {
this.showTooltip();
}
else if (this.tooltip !== undefined) {
this.hideTooltip();
}
};
/**
* Appends to a container
* @method appendTo
* @param {H5P.jquery} $container
*/
JoubelProgressbar.prototype.appendTo = function ($container) {
this.$progressbar.appendTo($container);
};
/**
* Update progress
* @method setProgress
* @param {number} step
*/
JoubelProgressbar.prototype.setProgress = function (step) {
// Check for valid value:
if (step > this.steps || step < 0) {
return;
}
this.currentStep = step;
this.$background.css({
width: ((this.currentStep/this.steps)*100) + '%'
});
this.updateAria();
};
/**
* Increment progress with 1
* @method next
*/
JoubelProgressbar.prototype.next = function () {
this.setProgress(this.currentStep+1);
};
/**
* Reset progressbar
* @method reset
*/
JoubelProgressbar.prototype.reset = function () {
this.setProgress(0);
};
/**
* Check if last step is reached
* @method isLastStep
* @return {Boolean}
*/
JoubelProgressbar.prototype.isLastStep = function () {
return this.steps === this.currentStep;
};
return JoubelProgressbar;
})(H5P.jQuery);

View File

@@ -0,0 +1,225 @@
var H5P = H5P || {};
/**
* @module
*/
H5P.JoubelScoreBar = (function ($) {
/* Need to use an id for the star SVG since that is the only way to reference
SVG filters */
var idCounter = 0;
/**
* Creates a score bar
* @class H5P.JoubelScoreBar
* @param {number} maxScore Maximum score
* @param {string} [label] Makes it easier for readspeakers to identify the scorebar
* @param {string} [helpText] Score explanation
* @param {string} [scoreExplanationButtonLabel] Label for score explanation button
*/
function JoubelScoreBar(maxScore, label, helpText, scoreExplanationButtonLabel) {
var self = this;
self.maxScore = maxScore;
self.score = 0;
idCounter++;
/**
* @const {string}
*/
self.STAR_MARKUP = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 63.77 53.87" aria-hidden="true" focusable="false">' +
'<title>star</title>' +
'<filter id="h5p-joubelui-score-bar-star-inner-shadow-' + idCounter + '" x0="-50%" y0="-50%" width="200%" height="200%">' +
'<feGaussianBlur in="SourceAlpha" stdDeviation="3" result="blur"></feGaussianBlur>' +
'<feOffset dy="2" dx="4"></feOffset>' +
'<feComposite in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowDiff"></feComposite>' +
'<feFlood flood-color="#ffe95c" flood-opacity="1"></feFlood>' +
'<feComposite in2="shadowDiff" operator="in"></feComposite>' +
'<feComposite in2="SourceGraphic" operator="over" result="firstfilter"></feComposite>' +
'<feGaussianBlur in="firstfilter" stdDeviation="3" result="blur2"></feGaussianBlur>' +
'<feOffset dy="-2" dx="-4"></feOffset>' +
'<feComposite in2="firstfilter" operator="arithmetic" k2="-1" k3="1" result="shadowDiff"></feComposite>' +
'<feFlood flood-color="#ffe95c" flood-opacity="1"></feFlood>' +
'<feComposite in2="shadowDiff" operator="in"></feComposite>' +
'<feComposite in2="firstfilter" operator="over"></feComposite>' +
'</filter>' +
'<path class="h5p-joubelui-score-bar-star-shadow" d="M35.08,43.41V9.16H20.91v0L9.51,10.85,9,10.93C2.8,12.18,0,17,0,21.25a11.22,11.22,0,0,0,3,7.48l8.73,8.53-1.07,6.16Z"/>' +
'<g>' +
'<path class="h5p-joubelui-score-bar-star-border" d="M61.36,22.8,49.72,34.11l2.78,16a2.6,2.6,0,0,1,.05.64c0,.85-.37,1.6-1.33,1.6A2.74,2.74,0,0,1,49.94,52L35.58,44.41,21.22,52a2.93,2.93,0,0,1-1.28.37c-.91,0-1.33-.75-1.33-1.6,0-.21.05-.43.05-.64l2.78-16L9.8,22.8A2.57,2.57,0,0,1,9,21.25c0-1,1-1.33,1.81-1.49l16.07-2.35L34.09,2.83c.27-.59.85-1.33,1.55-1.33s1.28.69,1.55,1.33l7.21,14.57,16.07,2.35c.75.11,1.81.53,1.81,1.49A3.07,3.07,0,0,1,61.36,22.8Z"/>' +
'<path class="h5p-joubelui-score-bar-star-fill" d="M61.36,22.8,49.72,34.11l2.78,16a2.6,2.6,0,0,1,.05.64c0,.85-.37,1.6-1.33,1.6A2.74,2.74,0,0,1,49.94,52L35.58,44.41,21.22,52a2.93,2.93,0,0,1-1.28.37c-.91,0-1.33-.75-1.33-1.6,0-.21.05-.43.05-.64l2.78-16L9.8,22.8A2.57,2.57,0,0,1,9,21.25c0-1,1-1.33,1.81-1.49l16.07-2.35L34.09,2.83c.27-.59.85-1.33,1.55-1.33s1.28.69,1.55,1.33l7.21,14.57,16.07,2.35c.75.11,1.81.53,1.81,1.49A3.07,3.07,0,0,1,61.36,22.8Z"/>' +
'<path filter="url(#h5p-joubelui-score-bar-star-inner-shadow-' + idCounter + ')" class="h5p-joubelui-score-bar-star-fill-full-score" d="M61.36,22.8,49.72,34.11l2.78,16a2.6,2.6,0,0,1,.05.64c0,.85-.37,1.6-1.33,1.6A2.74,2.74,0,0,1,49.94,52L35.58,44.41,21.22,52a2.93,2.93,0,0,1-1.28.37c-.91,0-1.33-.75-1.33-1.6,0-.21.05-.43.05-.64l2.78-16L9.8,22.8A2.57,2.57,0,0,1,9,21.25c0-1,1-1.33,1.81-1.49l16.07-2.35L34.09,2.83c.27-.59.85-1.33,1.55-1.33s1.28.69,1.55,1.33l7.21,14.57,16.07,2.35c.75.11,1.81.53,1.81,1.49A3.07,3.07,0,0,1,61.36,22.8Z"/>' +
'</g>' +
'</svg>';
/**
* @function appendTo
* @memberOf H5P.JoubelScoreBar#
* @param {H5P.jQuery} $wrapper Dom container
*/
self.appendTo = function ($wrapper) {
self.$scoreBar.appendTo($wrapper);
};
/**
* Create the text representation of the scorebar .
*
* @private
* @return {string}
*/
var createLabel = function (score) {
if (!label) {
return '';
}
return label.replace(':num', score).replace(':total', self.maxScore);
};
/**
* Creates the html for this widget
*
* @method createHtml
* @private
*/
var createHtml = function () {
// Container div
self.$scoreBar = $('<div>', {
'class': 'h5p-joubelui-score-bar',
});
var $visuals = $('<div>', {
'class': 'h5p-joubelui-score-bar-visuals',
appendTo: self.$scoreBar
});
// The progress bar wrapper
self.$progressWrapper = $('<div>', {
'class': 'h5p-joubelui-score-bar-progress-wrapper',
appendTo: $visuals
});
self.$progress = $('<div>', {
'class': 'h5p-joubelui-score-bar-progress',
'html': createLabel(self.score),
appendTo: self.$progressWrapper
});
// The star
$('<div>', {
'class': 'h5p-joubelui-score-bar-star',
html: self.STAR_MARKUP
}).appendTo($visuals);
// The score container
var $numerics = $('<div>', {
'class': 'h5p-joubelui-score-numeric',
appendTo: self.$scoreBar,
'aria-hidden': true
});
// The current score
self.$scoreCounter = $('<span>', {
'class': 'h5p-joubelui-score-number h5p-joubelui-score-number-counter',
text: 0,
appendTo: $numerics
});
// The separator
$('<span>', {
'class': 'h5p-joubelui-score-number-separator',
text: '/',
appendTo: $numerics
});
// Max score
self.$maxScore = $('<span>', {
'class': 'h5p-joubelui-score-number h5p-joubelui-score-max',
text: self.maxScore,
appendTo: $numerics
});
if (helpText) {
H5P.JoubelUI.createTip(helpText, {
tipLabel: scoreExplanationButtonLabel ? scoreExplanationButtonLabel : helpText,
helpIcon: true
}).appendTo(self.$scoreBar);
self.$scoreBar.addClass('h5p-score-bar-has-help');
}
};
/**
* Set the current score
* @method setScore
* @memberOf H5P.JoubelScoreBar#
* @param {number} score
*/
self.setScore = function (score) {
// Do nothing if score hasn't changed
if (score === self.score) {
return;
}
self.score = score > self.maxScore ? self.maxScore : score;
self.updateVisuals();
};
/**
* Increment score
* @method incrementScore
* @memberOf H5P.JoubelScoreBar#
* @param {number=} incrementBy Optional parameter, defaults to 1
*/
self.incrementScore = function (incrementBy) {
self.setScore(self.score + (incrementBy || 1));
};
/**
* Set the max score
* @method setMaxScore
* @memberOf H5P.JoubelScoreBar#
* @param {number} maxScore The max score
*/
self.setMaxScore = function (maxScore) {
self.maxScore = maxScore;
};
/**
* Updates the progressbar visuals
* @memberOf H5P.JoubelScoreBar#
* @method updateVisuals
*/
self.updateVisuals = function () {
self.$progress.html(createLabel(self.score));
self.$scoreCounter.text(self.score);
self.$maxScore.text(self.maxScore);
setTimeout(function () {
// Start the progressbar animation
self.$progress.css({
width: ((self.score / self.maxScore) * 100) + '%'
});
H5P.Transition.onTransitionEnd(self.$progress, function () {
// If fullscore fill the star and start the animation
self.$scoreBar.toggleClass('h5p-joubelui-score-bar-full-score', self.score === self.maxScore);
self.$scoreBar.toggleClass('h5p-joubelui-score-bar-animation-active', self.score === self.maxScore);
// Only allow the star animation to run once
self.$scoreBar.one("animationend", function() {
self.$scoreBar.removeClass("h5p-joubelui-score-bar-animation-active");
});
}, 600);
}, 300);
};
/**
* Removes all classes
* @method reset
*/
self.reset = function () {
self.$scoreBar.removeClass('h5p-joubelui-score-bar-full-score');
};
createHtml();
}
return JoubelScoreBar;
})(H5P.jQuery);

View File

@@ -0,0 +1,32 @@
var H5P = H5P || {};
H5P.SimpleRoundedButton = (function ($) {
/**
* Creates a new tip
*/
function SimpleRoundedButton(text) {
var $simpleRoundedButton = $('<div>', {
'class': 'joubel-simple-rounded-button',
'title': text,
'role': 'button',
'tabindex': '0'
}).keydown(function (e) {
// 32 - space, 13 - enter
if ([32, 13].indexOf(e.which) !== -1) {
$(this).click();
e.preventDefault();
}
});
$('<span>', {
'class': 'joubel-simple-rounded-button-text',
'html': text
}).appendTo($simpleRoundedButton);
return $simpleRoundedButton;
}
return SimpleRoundedButton;
}(H5P.jQuery));

View File

@@ -0,0 +1,96 @@
var H5P = H5P || {};
H5P.JoubelSlider = (function ($) {
/**
* Creates a new Slider
*
* @param {object} [params] Additional parameters
*/
function JoubelSlider(params) {
H5P.EventDispatcher.call(this);
this.$slider = $('<div>', $.extend({
'class': 'h5p-joubel-ui-slider'
}, params));
this.$slides = [];
this.currentIndex = 0;
this.numSlides = 0;
}
JoubelSlider.prototype = Object.create(H5P.EventDispatcher.prototype);
JoubelSlider.prototype.constructor = JoubelSlider;
JoubelSlider.prototype.addSlide = function ($content) {
$content.addClass('h5p-joubel-ui-slide').css({
'left': (this.numSlides*100) + '%'
});
this.$slider.append($content);
this.$slides.push($content);
this.numSlides++;
if(this.numSlides === 1) {
$content.addClass('current');
}
};
JoubelSlider.prototype.attach = function ($container) {
$container.append(this.$slider);
};
JoubelSlider.prototype.move = function (index) {
var self = this;
if(index === 0) {
self.trigger('first-slide');
}
if(index+1 === self.numSlides) {
self.trigger('last-slide');
}
self.trigger('move');
var $previousSlide = self.$slides[this.currentIndex];
H5P.Transition.onTransitionEnd(this.$slider, function () {
$previousSlide.removeClass('current');
self.trigger('moved');
});
this.$slides[index].addClass('current');
var translateX = 'translateX(' + (-index*100) + '%)';
this.$slider.css({
'-webkit-transform': translateX,
'-moz-transform': translateX,
'-ms-transform': translateX,
'transform': translateX
});
this.currentIndex = index;
};
JoubelSlider.prototype.remove = function () {
this.$slider.remove();
};
JoubelSlider.prototype.next = function () {
if(this.currentIndex+1 >= this.numSlides) {
return;
}
this.move(this.currentIndex+1);
};
JoubelSlider.prototype.previous = function () {
this.move(this.currentIndex-1);
};
JoubelSlider.prototype.first = function () {
this.move(0);
};
JoubelSlider.prototype.last = function () {
this.move(this.numSlides-1);
};
return JoubelSlider;
})(H5P.jQuery);

View File

@@ -0,0 +1,356 @@
var H5P = H5P || {};
/**
* Class responsible for creating speech bubbles
*/
H5P.JoubelSpeechBubble = (function ($) {
var $currentSpeechBubble;
var $currentContainer;
var $tail;
var $innerTail;
var removeSpeechBubbleTimeout;
var currentMaxWidth;
var DEFAULT_MAX_WIDTH = 400;
var iDevice = navigator.userAgent.match(/iPod|iPhone|iPad/g) ? true : false;
/**
* Creates a new speech bubble
*
* @param {H5P.jQuery} $container The speaking object
* @param {string} text The text to display
* @param {number} maxWidth The maximum width of the bubble
* @return {H5P.JoubelSpeechBubble}
*/
function JoubelSpeechBubble($container, text, maxWidth) {
maxWidth = maxWidth || DEFAULT_MAX_WIDTH;
currentMaxWidth = maxWidth;
$currentContainer = $container;
this.isCurrent = function ($tip) {
return $tip.is($currentContainer);
};
this.remove = function () {
remove();
};
var fadeOutSpeechBubble = function ($speechBubble) {
if (!$speechBubble) {
return;
}
// Stop removing bubble
clearTimeout(removeSpeechBubbleTimeout);
$speechBubble.removeClass('show');
setTimeout(function () {
if ($speechBubble) {
$speechBubble.remove();
$speechBubble = undefined;
}
}, 500);
};
if ($currentSpeechBubble !== undefined) {
remove();
}
var $h5pContainer = getH5PContainer($container);
// Make sure we fade out old speech bubble
fadeOutSpeechBubble($currentSpeechBubble);
// Create bubble
$tail = $('<div class="joubel-speech-bubble-tail"></div>');
$innerTail = $('<div class="joubel-speech-bubble-inner-tail"></div>');
var $innerBubble = $(
'<div class="joubel-speech-bubble-inner">' +
'<div class="joubel-speech-bubble-text">' + text + '</div>' +
'</div>'
).prepend($innerTail);
$currentSpeechBubble = $(
'<div class="joubel-speech-bubble" aria-live="assertive">'
).append([$tail, $innerBubble])
.appendTo($h5pContainer);
// Show speech bubble with transition
setTimeout(function () {
$currentSpeechBubble.addClass('show');
}, 0);
position($currentSpeechBubble, $currentContainer, maxWidth, $tail, $innerTail);
// Handle click to close
H5P.$body.on('mousedown.speechBubble', handleOutsideClick);
// Handle window resizing
H5P.$window.on('resize', '', handleResize);
// Handle clicks when inside IV which blocks bubbling.
$container.parents('.h5p-dialog')
.on('mousedown.speechBubble', handleOutsideClick);
if (iDevice) {
H5P.$body.css('cursor', 'pointer');
}
return this;
}
// Remove speechbubble if it belongs to a dom element that is about to be hidden
H5P.externalDispatcher.on('domHidden', function (event) {
if ($currentSpeechBubble !== undefined && event.data.$dom.find($currentContainer).length !== 0) {
remove();
}
});
/**
* Returns the closest h5p container for the given DOM element.
*
* @param {object} $container jquery element
* @return {object} the h5p container (jquery element)
*/
function getH5PContainer($container) {
var $h5pContainer = $container.closest('.h5p-frame');
// Check closest h5p frame first, then check for container in case there is no frame.
if (!$h5pContainer.length) {
$h5pContainer = $container.closest('.h5p-container');
}
return $h5pContainer;
}
/**
* Event handler that is called when the window is resized.
*/
function handleResize() {
position($currentSpeechBubble, $currentContainer, currentMaxWidth, $tail, $innerTail);
}
/**
* Repositions the speech bubble according to the position of the container.
*
* @param {object} $currentSpeechbubble the speech bubble that should be positioned
* @param {object} $container the container to which the speech bubble should point
* @param {number} maxWidth the maximum width of the speech bubble
* @param {object} $tail the tail (the triangle that points to the referenced container)
* @param {object} $innerTail the inner tail (the triangle that points to the referenced container)
*/
function position($currentSpeechBubble, $container, maxWidth, $tail, $innerTail) {
var $h5pContainer = getH5PContainer($container);
// Calculate offset between the button and the h5p frame
var offset = getOffsetBetween($h5pContainer, $container);
var direction = (offset.bottom > offset.top ? 'bottom' : 'top');
var tipWidth = offset.outerWidth * 0.9; // Var needs to be renamed to make sense
var bubbleWidth = tipWidth > maxWidth ? maxWidth : tipWidth;
var bubblePosition = getBubblePosition(bubbleWidth, offset);
var tailPosition = getTailPosition(bubbleWidth, bubblePosition, offset, $container.width());
// Need to set font-size, since element is appended to body.
// Using same font-size as parent. In that way it will grow accordingly
// when resizing
var fontSize = 16;//parseFloat($parent.css('font-size'));
// Set width and position of speech bubble
$currentSpeechBubble.css(bubbleCSS(
direction,
bubbleWidth,
bubblePosition,
fontSize
));
var preparedTailCSS = tailCSS(direction, tailPosition);
$tail.css(preparedTailCSS);
$innerTail.css(preparedTailCSS);
}
/**
* Static function for removing the speechbubble
*/
var remove = function () {
H5P.$body.off('mousedown.speechBubble');
H5P.$window.off('resize', '', handleResize);
$currentContainer.parents('.h5p-dialog').off('mousedown.speechBubble');
if (iDevice) {
H5P.$body.css('cursor', '');
}
if ($currentSpeechBubble !== undefined) {
// Apply transition, then remove speech bubble
$currentSpeechBubble.removeClass('show');
// Make sure we remove any old timeout before reassignment
clearTimeout(removeSpeechBubbleTimeout);
removeSpeechBubbleTimeout = setTimeout(function () {
$currentSpeechBubble.remove();
$currentSpeechBubble = undefined;
}, 500);
}
// Don't return false here. If the user e.g. clicks a button when the bubble is visible,
// we want the bubble to disapear AND the button to receive the event
};
/**
* Remove the speech bubble and container reference
*/
function handleOutsideClick(event) {
if (event.target === $currentContainer[0]) {
return; // Button clicks are not outside clicks
}
remove();
// There is no current container when a container isn't clicked
$currentContainer = undefined;
}
/**
* Calculate position for speech bubble
*
* @param {number} bubbleWidth The width of the speech bubble
* @param {object} offset
* @return {object} Return position for the speech bubble
*/
function getBubblePosition(bubbleWidth, offset) {
var bubblePosition = {};
var tailOffset = 9;
var widthOffset = bubbleWidth / 2;
// Calculate top position
bubblePosition.top = offset.top + offset.innerHeight;
// Calculate bottom position
bubblePosition.bottom = offset.bottom + offset.innerHeight + tailOffset;
// Calculate left position
if (offset.left < widthOffset) {
bubblePosition.left = 3;
}
else if ((offset.left + widthOffset) > offset.outerWidth) {
bubblePosition.left = offset.outerWidth - bubbleWidth - 3;
}
else {
bubblePosition.left = offset.left - widthOffset + (offset.innerWidth / 2);
}
return bubblePosition;
}
/**
* Calculate position for speech bubble tail
*
* @param {number} bubbleWidth The width of the speech bubble
* @param {object} bubblePosition Speech bubble position
* @param {object} offset
* @param {number} iconWidth The width of the tip icon
* @return {object} Return position for the tail
*/
function getTailPosition(bubbleWidth, bubblePosition, offset, iconWidth) {
var tailPosition = {};
// Magic numbers. Tuned by hand so that the tail fits visually within
// the bounds of the speech bubble.
var leftBoundary = 9;
var rightBoundary = bubbleWidth - 20;
tailPosition.left = offset.left - bubblePosition.left + (iconWidth / 2) - 6;
if (tailPosition.left < leftBoundary) {
tailPosition.left = leftBoundary;
}
if (tailPosition.left > rightBoundary) {
tailPosition.left = rightBoundary;
}
tailPosition.top = -6;
tailPosition.bottom = -6;
return tailPosition;
}
/**
* Return bubble CSS for the desired growth direction
*
* @param {string} direction The direction the speech bubble will grow
* @param {number} width The width of the speech bubble
* @param {object} position Speech bubble position
* @param {number} fontSize The size of the bubbles font
* @return {object} Return CSS
*/
function bubbleCSS(direction, width, position, fontSize) {
if (direction === 'top') {
return {
width: width + 'px',
bottom: position.bottom + 'px',
left: position.left + 'px',
fontSize: fontSize + 'px',
top: ''
};
}
else {
return {
width: width + 'px',
top: position.top + 'px',
left: position.left + 'px',
fontSize: fontSize + 'px',
bottom: ''
};
}
}
/**
* Return tail CSS for the desired growth direction
*
* @param {string} direction The direction the speech bubble will grow
* @param {object} position Tail position
* @return {object} Return CSS
*/
function tailCSS(direction, position) {
if (direction === 'top') {
return {
bottom: position.bottom + 'px',
left: position.left + 'px',
top: ''
};
}
else {
return {
top: position.top + 'px',
left: position.left + 'px',
bottom: ''
};
}
}
/**
* Calculates the offset between an element inside a container and the
* container. Only works if all the edges of the inner element are inside the
* outer element.
* Width/height of the elements is included as a convenience.
*
* @param {H5P.jQuery} $outer
* @param {H5P.jQuery} $inner
* @return {object} Position offset
*/
function getOffsetBetween($outer, $inner) {
var outer = $outer[0].getBoundingClientRect();
var inner = $inner[0].getBoundingClientRect();
return {
top: inner.top - outer.top,
right: outer.right - inner.right,
bottom: outer.bottom - inner.bottom,
left: inner.left - outer.left,
innerWidth: inner.width,
innerHeight: inner.height,
outerWidth: outer.width,
outerHeight: outer.height
};
}
return JoubelSpeechBubble;
})(H5P.jQuery);

View File

@@ -0,0 +1,19 @@
var H5P = H5P || {};
H5P.JoubelThrobber = (function ($) {
/**
* Creates a new tip
*/
function JoubelThrobber() {
// h5p-throbber css is described in core
var $throbber = $('<div/>', {
'class': 'h5p-throbber'
});
return $throbber;
}
return JoubelThrobber;
}(H5P.jQuery));

View File

@@ -0,0 +1,106 @@
H5P.JoubelTip = (function ($) {
var $conv = $('<div/>');
/**
* Creates a new tip element.
*
* NOTE that this may look like a class but it doesn't behave like one.
* It returns a jQuery object.
*
* @param {string} tipHtml The text to display in the popup
* @param {Object} [behaviour] Options
* @param {string} [behaviour.tipLabel] Set to use a custom label for the tip button (you want this for good A11Y)
* @param {boolean} [behaviour.helpIcon] Set to 'true' to Add help-icon classname to Tip button (changes the icon)
* @param {boolean} [behaviour.showSpeechBubble] Set to 'false' to disable functionality (you may this in the editor)
* @param {boolean} [behaviour.tabcontrol] Set to 'true' if you plan on controlling the tabindex in the parent (tabindex="-1")
* @return {H5P.jQuery|undefined} Tip button jQuery element or 'undefined' if invalid tip
*/
function JoubelTip(tipHtml, behaviour) {
// Keep track of the popup that appears when you click the Tip button
var speechBubble;
// Parse tip html to determine text
var tipText = $conv.html(tipHtml).text().trim();
if (tipText === '') {
return; // The tip has no textual content, i.e. it's invalid.
}
// Set default behaviour
behaviour = $.extend({
tipLabel: tipText,
helpIcon: false,
showSpeechBubble: true,
tabcontrol: false
}, behaviour);
// Create Tip button
var $tipButton = $('<div/>', {
class: 'joubel-tip-container' + (behaviour.showSpeechBubble ? '' : ' be-quiet'),
title: behaviour.tipLabel,
'aria-label': behaviour.tipLabel,
'aria-expanded': false,
role: 'button',
tabindex: (behaviour.tabcontrol ? -1 : 0),
click: function (event) {
// Toggle show/hide popup
toggleSpeechBubble();
event.preventDefault();
},
keydown: function (event) {
if (event.which === 32 || event.which === 13) { // Space & enter key
// Toggle show/hide popup
toggleSpeechBubble();
event.stopPropagation();
event.preventDefault();
}
else { // Any other key
// Toggle hide popup
toggleSpeechBubble(false);
}
},
// Add markup to render icon
html: '<span class="joubel-icon-tip-normal ' + (behaviour.helpIcon ? ' help-icon': '') + '">' +
'<span class="h5p-icon-shadow"></span>' +
'<span class="h5p-icon-speech-bubble"></span>' +
'<span class="h5p-icon-info"></span>' +
'</span>'
// IMPORTANT: All of the markup elements must have 'pointer-events: none;'
});
const $tipAnnouncer = $('<div>', {
'class': 'hidden-but-read',
'aria-live': 'polite',
appendTo: $tipButton,
});
/**
* Tip button interaction handler.
* Toggle show or hide the speech bubble popup when interacting with the
* Tip button.
*
* @private
* @param {boolean} [force] 'true' shows and 'false' hides.
*/
var toggleSpeechBubble = function (force) {
if (speechBubble !== undefined && speechBubble.isCurrent($tipButton)) {
// Hide current popup
speechBubble.remove();
speechBubble = undefined;
$tipButton.attr('aria-expanded', false);
$tipAnnouncer.html('');
}
else if (force !== false && behaviour.showSpeechBubble) {
// Create and show new popup
speechBubble = H5P.JoubelSpeechBubble($tipButton, tipHtml);
$tipButton.attr('aria-expanded', true);
$tipAnnouncer.html(tipHtml);
}
};
return $tipButton;
}
return JoubelTip;
})(H5P.jQuery);

View File

@@ -0,0 +1,183 @@
var H5P = H5P || {};
/**
* H5P Joubel UI library.
*
* This is a utility library, which does not implement attach. I.e, it has to bee actively used by
* other libraries
* @module
*/
H5P.JoubelUI = (function ($) {
/**
* The internal object to return
* @class H5P.JoubelUI
* @static
*/
function JoubelUI() {}
/* Public static functions */
/**
* Create a tip icon
* @method H5P.JoubelUI.createTip
* @param {string} text The textual tip
* @param {Object} params Parameters
* @return {H5P.JoubelTip}
*/
JoubelUI.createTip = function (text, params) {
return new H5P.JoubelTip(text, params);
};
/**
* Create message dialog
* @method H5P.JoubelUI.createMessageDialog
* @param {H5P.jQuery} $container The dom container
* @param {string} message The message
* @return {H5P.JoubelMessageDialog}
*/
JoubelUI.createMessageDialog = function ($container, message) {
return new H5P.JoubelMessageDialog($container, message);
};
/**
* Create help text dialog
* @method H5P.JoubelUI.createHelpTextDialog
* @param {string} header The textual header
* @param {string} message The textual message
* @param {string} closeButtonTitle The title for the close button
* @return {H5P.JoubelHelpTextDialog}
*/
JoubelUI.createHelpTextDialog = function (header, message, closeButtonTitle) {
return new H5P.JoubelHelpTextDialog(header, message, closeButtonTitle);
};
/**
* Create progress circle
* @method H5P.JoubelUI.createProgressCircle
* @param {number} number The progress (0 to 100)
* @param {string} progressColor The progress color in hex value
* @param {string} fillColor The fill color in hex value
* @param {string} backgroundColor The background color in hex value
* @return {H5P.JoubelProgressCircle}
*/
JoubelUI.createProgressCircle = function (number, progressColor, fillColor, backgroundColor) {
return new H5P.JoubelProgressCircle(number, progressColor, fillColor, backgroundColor);
};
/**
* Create throbber for loading
* @method H5P.JoubelUI.createThrobber
* @return {H5P.JoubelThrobber}
*/
JoubelUI.createThrobber = function () {
return new H5P.JoubelThrobber();
};
/**
* Create simple rounded button
* @method H5P.JoubelUI.createSimpleRoundedButton
* @param {string} text The button label
* @return {H5P.SimpleRoundedButton}
*/
JoubelUI.createSimpleRoundedButton = function (text) {
return new H5P.SimpleRoundedButton(text);
};
/**
* Create Slider
* @method H5P.JoubelUI.createSlider
* @param {Object} [params] Parameters
* @return {H5P.JoubelSlider}
*/
JoubelUI.createSlider = function (params) {
return new H5P.JoubelSlider(params);
};
/**
* Create Score Bar
* @method H5P.JoubelUI.createScoreBar
* @param {number=} maxScore The maximum score
* @param {string} [label] Makes it easier for readspeakers to identify the scorebar
* @return {H5P.JoubelScoreBar}
*/
JoubelUI.createScoreBar = function (maxScore, label, helpText, scoreExplanationButtonLabel) {
return new H5P.JoubelScoreBar(maxScore, label, helpText, scoreExplanationButtonLabel);
};
/**
* Create Progressbar
* @method H5P.JoubelUI.createProgressbar
* @param {number=} numSteps The total numer of steps
* @param {Object} [options] Additional options
* @param {boolean} [options.disableAria] Disable readspeaker assistance
* @param {string} [options.progressText] A progress text for describing
* current progress out of total progress for readspeakers.
* e.g. "Slide :num of :total"
* @return {H5P.JoubelProgressbar}
*/
JoubelUI.createProgressbar = function (numSteps, options) {
return new H5P.JoubelProgressbar(numSteps, options);
};
/**
* Create standard Joubel button
*
* @method H5P.JoubelUI.createButton
* @param {object} params
* May hold any properties allowed by jQuery. If href is set, an A tag
* is used, if not a button tag is used.
* @return {H5P.jQuery} The jquery element created
*/
JoubelUI.createButton = function(params) {
var type = 'button';
if (params.href) {
type = 'a';
}
else {
params.type = 'button';
}
if (params.class) {
params.class += ' h5p-joubelui-button';
}
else {
params.class = 'h5p-joubelui-button';
}
return $('<' + type + '/>', params);
};
/**
* Fix for iframe scoll bug in IOS. When focusing an element that doesn't have
* focus support by default the iframe will scroll the parent frame so that
* the focused element is out of view. This varies dependening on the elements
* of the parent frame.
*/
if (H5P.isFramed && !H5P.hasiOSiframeScrollFix &&
/iPad|iPhone|iPod/.test(navigator.userAgent)) {
H5P.hasiOSiframeScrollFix = true;
// Keep track of original focus function
var focus = HTMLElement.prototype.focus;
// Override the original focus
HTMLElement.prototype.focus = function () {
// Only focus the element if it supports it natively
if ( (this instanceof HTMLAnchorElement ||
this instanceof HTMLInputElement ||
this instanceof HTMLSelectElement ||
this instanceof HTMLTextAreaElement ||
this instanceof HTMLButtonElement ||
this instanceof HTMLIFrameElement ||
this instanceof HTMLAreaElement) && // HTMLAreaElement isn't supported by Safari yet.
!this.getAttribute('role')) { // Focus breaks if a different role has been set
// In theory this.isContentEditable should be able to recieve focus,
// but it didn't work when tested.
// Trigger the original focus with the proper context
focus.call(this);
}
};
}
return JoubelUI;
})(H5P.jQuery);

View File

@@ -0,0 +1,107 @@
{
"title": "Joubel UI",
"contentType": "Utility",
"description": "UI utility library",
"majorVersion": 1,
"minorVersion": 3,
"patchVersion": 9,
"runnable": 0,
"coreApi": {
"majorVersion": 1,
"minorVersion": 3
},
"machineName": "H5P.JoubelUI",
"author": "Joubel",
"preloadedJs": [
{
"path": "js/joubel-help-dialog.js"
},
{
"path": "js/joubel-message-dialog.js"
},
{
"path": "js/joubel-progress-circle.js"
},
{
"path": "js/joubel-simple-rounded-button.js"
},
{
"path": "js/joubel-speech-bubble.js"
},
{
"path": "js/joubel-throbber.js"
},
{
"path": "js/joubel-tip.js"
},
{
"path": "js/joubel-slider.js"
},
{
"path": "js/joubel-score-bar.js"
},
{
"path": "js/joubel-progressbar.js"
},
{
"path": "js/joubel-ui.js"
}
],
"preloadedCss": [
{
"path": "css/joubel-help-dialog.css"
},
{
"path": "css/joubel-message-dialog.css"
},
{
"path": "css/joubel-progress-circle.css"
},
{
"path": "css/joubel-simple-rounded-button.css"
},
{
"path": "css/joubel-speech-bubble.css"
},
{
"path": "css/joubel-tip.css"
},
{
"path": "css/joubel-slider.css"
},
{
"path": "css/joubel-score-bar.css"
},
{
"path": "css/joubel-progressbar.css"
},
{
"path": "css/joubel-ui.css"
},
{
"path": "css/joubel-icon.css"
}
],
"preloadedDependencies": [
{
"machineName": "FontAwesome",
"majorVersion": 4,
"minorVersion": 5
},
{
"machineName": "H5P.Transition",
"majorVersion": 1,
"minorVersion": 0
},
{
"machineName": "Drop",
"majorVersion": 1,
"minorVersion": 0
},
{
"machineName": "H5P.FontIcons",
"majorVersion": 1,
"minorVersion": 0
}
]
}