installed plugin AudioIgniter
version 1.7.3
This commit is contained in:
parent
dbc3ae04ae
commit
55ea6cf8dd
1
wp-content/plugins/audioigniter/.prettierignore
Normal file
1
wp-content/plugins/audioigniter/.prettierignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
player/build
|
106
wp-content/plugins/audioigniter/assets/css/_mixins.scss
Normal file
106
wp-content/plugins/audioigniter/assets/css/_mixins.scss
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
//
|
||||||
|
// Mixins
|
||||||
|
//
|
||||||
|
@mixin clearfix() {
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ai-spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin spinner($color: #fff, $opacity: .35, $size: 40px) {
|
||||||
|
border: 6px solid rgba($color, $opacity);
|
||||||
|
border-top-color: rgba($color, $opacity*2.5);
|
||||||
|
border-radius: 100%;
|
||||||
|
height: $size;
|
||||||
|
width: $size;
|
||||||
|
animation: ai-spin .8s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin btn-reset {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: normal;
|
||||||
|
border: 0;
|
||||||
|
appearance: none;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
border-radius: 0;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
background-image: none;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes backgroundPosition {
|
||||||
|
0% {
|
||||||
|
background-position: -140px 0
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-position: 140px 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animatedBackground($width: 140px, $height: 8px, $top: 0, $left: 0) {
|
||||||
|
content: '';
|
||||||
|
width: $width;
|
||||||
|
height: $height;
|
||||||
|
background: linear-gradient(to right, $control-color 8%, lighten($control-color, 6%) 18%, $control-color 33%);
|
||||||
|
background-size: 500px;
|
||||||
|
position: absolute;
|
||||||
|
top: $top;
|
||||||
|
left: $left;
|
||||||
|
opacity: 1;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-name: backgroundPosition;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dashicon($icon) {
|
||||||
|
content: $icon;
|
||||||
|
display: inline-block;
|
||||||
|
font: 400 20px/1 dashicons;
|
||||||
|
speak: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
155
wp-content/plugins/audioigniter/assets/css/admin-styles.css
Normal file
155
wp-content/plugins/audioigniter/assets/css/admin-styles.css
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
@keyframes ai-spin { 0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); } }
|
||||||
|
@keyframes backgroundPosition { 0% { background-position: -140px 0; }
|
||||||
|
100% { background-position: 140px 0; } }
|
||||||
|
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; }
|
||||||
|
|
||||||
|
.ai-row { margin-left: -15px; margin-right: -15px; box-sizing: border-box; }
|
||||||
|
.ai-row::after { content: ""; display: table; clear: both; }
|
||||||
|
|
||||||
|
[class^="ai-col"] { float: left; padding-left: 15px; padding-right: 15px; width: 50%; box-sizing: border-box; }
|
||||||
|
|
||||||
|
.ai-btn { display: inline-block; font-weight: normal; margin: 0; line-height: normal; border: 0; box-shadow: none; text-align: center; vertical-align: middle; cursor: pointer; white-space: nowrap; user-select: none; border-radius: 2px; width: auto; height: auto; background-image: none; padding: 11px 20px 11px; font-size: 12px; text-transform: uppercase; background-color: #1c4866; color: #ffffff; text-decoration: none; }
|
||||||
|
.ai-btn:hover, .ai-btn:focus { color: #ffffff; background-color: #173a52; }
|
||||||
|
|
||||||
|
.ai-btn-green { background-color: #14b552; }
|
||||||
|
.ai-btn-green:hover, .ai-btn-green:focus { color: #ffffff; background-color: #119e48; }
|
||||||
|
|
||||||
|
.ai-brand-module { background-color: #1c4866; padding: 15px; color: #ffffff; font-size: 12px; }
|
||||||
|
.ai-brand-module p { font-size: 12px; }
|
||||||
|
.ai-brand-module a:not(.ai-btn) { color: #ffcc00; text-decoration: none; }
|
||||||
|
|
||||||
|
.ai-brand-module-actions { text-align: right; }
|
||||||
|
.ai-brand-module-actions p { margin: 0; }
|
||||||
|
|
||||||
|
.ai-header { margin: 12px 0 -12px; height: 40px; }
|
||||||
|
|
||||||
|
.ai-header-actions { text-align: right; }
|
||||||
|
|
||||||
|
.ai-logo { display: inline-block; position: relative; top: -2px; }
|
||||||
|
.ai-logo img { height: 44px; }
|
||||||
|
|
||||||
|
.ai-note { font-style: italic; }
|
||||||
|
|
||||||
|
.ai-list-inline { margin: 0; padding: 0; list-style: none; }
|
||||||
|
.ai-list-inline li { display: inline-block; margin: 0; }
|
||||||
|
|
||||||
|
.ai-footer-links a::after { content: "\007c"; color: #ffffff; opacity: .5; margin: 0 7px; }
|
||||||
|
.ai-footer-links li:last-child a::after { display: none; }
|
||||||
|
|
||||||
|
.ai-module { border: 1px solid #eeeeee; margin-top: 12px; padding: 15px; }
|
||||||
|
.ai-module::after { content: ""; display: table; clear: both; }
|
||||||
|
|
||||||
|
.ai-container { margin-top: 12px; }
|
||||||
|
|
||||||
|
.ai-field-controls-wrap { padding: 15px; border: 1px solid #eeeeee; }
|
||||||
|
.ai-field-controls-wrap::after { content: ""; display: table; clear: both; }
|
||||||
|
|
||||||
|
.ai-field-controls { float: left; }
|
||||||
|
.ai-field-controls .button { margin-right: 5px; }
|
||||||
|
|
||||||
|
.ai-field-controls-visibility { float: right; padding-top: 4px; }
|
||||||
|
|
||||||
|
.ai-field-controls-visibility a { text-decoration: none; }
|
||||||
|
|
||||||
|
.ai-fields-expand-all { margin-right: 8px; padding-right: 6px; border-right: 1px solid #f1f1f1; }
|
||||||
|
|
||||||
|
.ai-fields-container { padding: 15px; border-left: 1px solid #eeeeee; border-right: 1px solid #eeeeee; }
|
||||||
|
|
||||||
|
.ai-field-repeatable { margin-bottom: 15px; border: 1px solid #d7d7d7; box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.07); }
|
||||||
|
.ai-field-repeatable:last-child { margin-bottom: 0; }
|
||||||
|
.ai-field-repeatable:only-child .ai-remove-field { display: none; }
|
||||||
|
|
||||||
|
.ai-field-container { padding: 15px; background-color: #ffffff; }
|
||||||
|
.ai-field-container::after { content: ""; display: table; clear: both; }
|
||||||
|
|
||||||
|
.ai-field-head { padding: 8px 15px 5px; line-height: normal; background-color: #d7d7d7; background: linear-gradient(to bottom, #f1f1f1, #d7d7d7); border-bottom: 1px solid #cccccc; }
|
||||||
|
.ai-field-head::after { content: ""; display: table; clear: both; }
|
||||||
|
.ai-field-head .toggle-indicator { border-radius: 50%; }
|
||||||
|
.ai-fields-sortable .ai-field-head { cursor: move; }
|
||||||
|
|
||||||
|
.ai-field-sort-handle { position: relative; top: 1px; color: #0073aa; }
|
||||||
|
.ai-field-sort-handle .dashicons { font-size: 18px; }
|
||||||
|
|
||||||
|
.ai-field-title { font-weight: bold; font-size: 1.05em; margin-left: 8px; padding-top: 3px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width: 80%; display: inline-block; }
|
||||||
|
|
||||||
|
.ai-field-toggle { float: right; }
|
||||||
|
|
||||||
|
.ai-field-cover { float: left; width: 100px; height: 100px; margin-right: 15px; background-color: #eeeeee; border: 1px solid #cccccc; }
|
||||||
|
|
||||||
|
.ai-field-split { float: left; width: calc(50% - 71px); margin-right: 15px; }
|
||||||
|
.ai-field-split:nth-child(2n + 1) { margin-right: 0; }
|
||||||
|
|
||||||
|
.ai-container .button .dashicons, .ai-module .button .dashicons { font-size: 1.2em; line-height: 1.7em; }
|
||||||
|
|
||||||
|
.ai-form-field-group { padding: 15px; border: 1px solid #f1f1f1; margin-bottom: 15px; }
|
||||||
|
.ai-form-field-group :last-child { margin-bottom: 0; }
|
||||||
|
.ai-form-field-group-title { margin-top: 0; }
|
||||||
|
|
||||||
|
.ai-form-field { margin-bottom: 15px; }
|
||||||
|
.ai-form-field label { display: inline-block; font-weight: bold; margin-bottom: 3px; }
|
||||||
|
.ai-form-field input[type="text"], .ai-form-field input[type="url"], .ai-form-field input[type="search"], .ai-form-field input[type="email"], .ai-form-field input[type="password"], .ai-form-field input[type="number"], .ai-form-field input[type="tel"], .ai-form-field input[type="date"], .ai-form-field textarea { width: 100%; }
|
||||||
|
.ai-form-field input[type="checkbox"], .ai-form-field input[type="radio"] { display: inline-block; position: relative; top: 1px; }
|
||||||
|
|
||||||
|
.ai-module-settings .ai-form-field input[type="text"], .ai-module-settings .ai-form-field input[type="url"], .ai-module-settings .ai-form-field input[type="search"], .ai-module-settings .ai-form-field input[type="email"], .ai-module-settings .ai-form-field input[type="password"], .ai-module-settings .ai-form-field input[type="number"], .ai-module-settings .ai-form-field input[type="tel"], .ai-module-settings .ai-form-field input[type="date"], .ai-module-settings .ai-form-field textarea, .ai-module-settings .ai-form-field select { width: 200px; max-width: 100%; display: block; }
|
||||||
|
|
||||||
|
.ai-form-field-addon { position: relative; }
|
||||||
|
.ai-form-field-addon input { padding-right: 80px; }
|
||||||
|
.ai-form-field-addon button { position: absolute; top: 0; right: -2px; }
|
||||||
|
|
||||||
|
.ai-field-help { margin: 5px 0 0; font-style: italic; color: #999; }
|
||||||
|
|
||||||
|
.ai-remove-field { float: right; }
|
||||||
|
|
||||||
|
.ai-field-upload-cover { display: block; position: relative; width: 100px; height: 100px; text-decoration: none; color: initial; overflow: hidden; }
|
||||||
|
.ai-field-upload-cover img { max-width: 100%; display: none; }
|
||||||
|
|
||||||
|
.ai-has-cover .ai-remove-cover { display: block; }
|
||||||
|
.ai-has-cover .ai-field-cover-placeholder { display: none; }
|
||||||
|
.ai-has-cover img { display: inline-block; }
|
||||||
|
|
||||||
|
.ai-field-cover-placeholder { text-align: center; font-style: normal; font-size: .9em; opacity: .8; padding-top: 28px; }
|
||||||
|
.ai-field-cover-placeholder::before { content: ""; display: inline-block; font: 400 20px/1 dashicons; speak: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-decoration: none !important; display: block; }
|
||||||
|
.ai-track-loading .ai-field-cover-placeholder::before { content: "\f463"; animation: rotation 1.2s infinite linear; }
|
||||||
|
|
||||||
|
.ai-remove-cover { color: #ffffff; background-color: #ff0000; width: 16px; height: 16px; font-size: 12px; cursor: pointer; position: absolute; top: 0; right: 0; opacity: .9; transition: opacity 0.18s ease-in; display: none; text-align: center; }
|
||||||
|
.ai-remove-cover:hover { opacity: 1; }
|
||||||
|
.ai-remove-cover .dashicons { font-size: 16px; width: 100%; height: 100%; }
|
||||||
|
|
||||||
|
.ai-remove-all-fields .dashicons, .ai-remove-field .dashicons { color: #ff0000; }
|
||||||
|
|
||||||
|
.ai-add-field-batch .dashicons, .ai-add-field .dashicons { color: #0073aa; }
|
||||||
|
|
||||||
|
.ai-info-box { background: #fffce6; color: #948832; font-size: 12px; border: solid 1px #eeeac9; padding: 15px; margin: 0 0 15px 0; }
|
||||||
|
|
||||||
|
.ai-player-type-message { display: none; }
|
||||||
|
|
||||||
|
.ai-drop-placeholder { background-color: #f1f1f1; border: 2px dashed #cccccc; opacity: 0.5; margin-bottom: 15px; }
|
||||||
|
|
||||||
|
.ai-collapsed .ai-field-container { display: none; }
|
||||||
|
.ai-collapsed .toggle-indicator::before { content: "\f140" !important; }
|
||||||
|
|
||||||
|
.ai-module-shortcode .code { display: block; width: 100%; margin-top: 3px; padding: 10px 10px 8px; font-weight: bold; background: #f1f1f1; }
|
||||||
|
|
||||||
|
.ai-sync-soundcloud.button { display: none; }
|
||||||
|
.ai-sync-soundcloud.button::before { content: "\f463"; color: #d54e21; display: inline-block; font: 400 19px/1 dashicons; speak: none; position: relative; left: -1px; top: 4px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; vertical-align: top; }
|
||||||
|
.ai-track-loading .ai-sync-soundcloud.button::before { animation: rotation 1.2s infinite linear; }
|
||||||
|
|
||||||
|
.ai-soundcloud-track .ai-sync-soundcloud { display: inline-block; }
|
||||||
|
.ai-soundcloud-track .ai-upload { display: none; }
|
||||||
|
|
||||||
|
@media (max-width: 1100px) { .ai-field-controls, .ai-field-controls-visibility { margin: 0; float: none; width: 100%; }
|
||||||
|
.ai-field-controls { margin-bottom: 5px; }
|
||||||
|
.ai-field-split { float: none; width: 100%; }
|
||||||
|
.ai-field-cover { margin-bottom: 15px; }
|
||||||
|
.ai-footer { text-align: center; }
|
||||||
|
.ai-footer .ai-brand-module-actions { text-align: center; margin-top: 10px; }
|
||||||
|
.ai-footer [class^="ai-col"] { width: 100%; } }
|
||||||
|
@media (max-width: 782px) { .ai-container .button .dashicons, .ai-module .button .dashicons { line-height: 1.2em; }
|
||||||
|
.ai-form-field-addon .button { top: 2px; } }
|
||||||
|
@media (max-width: 600px) { .ai-field-controls .button { width: 100%; }
|
||||||
|
.ai-header { text-align: center; }
|
||||||
|
.ai-header .ai-brand-module-actions { margin-top: 10px; }
|
||||||
|
.ai-header .ai-btn { display: block; }
|
||||||
|
.ai-header [class^="ai-col"] { width: 100%; } }
|
1
wp-content/plugins/audioigniter/assets/css/admin-styles.min.css
vendored
Normal file
1
wp-content/plugins/audioigniter/assets/css/admin-styles.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
660
wp-content/plugins/audioigniter/assets/css/admin-styles.scss
Normal file
660
wp-content/plugins/audioigniter/assets/css/admin-styles.scss
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
@import 'mixins';
|
||||||
|
|
||||||
|
$brand-color: #1c4866 !default;
|
||||||
|
$brand-secondary-color: #ffcc00 !default;
|
||||||
|
$lighter-grey: #f1f1f1 !default;
|
||||||
|
$light-grey: #eeeeee !default;
|
||||||
|
$medium-grey: #d7d7d7 !default;
|
||||||
|
$dark-grey: #cccccc !default;
|
||||||
|
$white: #ffffff !default;
|
||||||
|
$blue: #0073aa !default;
|
||||||
|
$red: #ff0000 !default;
|
||||||
|
$green: #14b552 !default;
|
||||||
|
$info-box-bg-color: #fffce6 !default;
|
||||||
|
$info-box-border-color: #eeeac9 !default;
|
||||||
|
$info-box-text-color: #948832 !default;
|
||||||
|
$base-pad: 15px !default;
|
||||||
|
$transition-time: .18s !default;
|
||||||
|
$border-color: $lighter-grey !default;
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
@include sr-only;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-row {
|
||||||
|
@include clearfix;
|
||||||
|
margin-left: -15px;
|
||||||
|
margin-right: -15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class^="ai-col"] {
|
||||||
|
float: left;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
width: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-btn {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
line-height: normal;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
border-radius: 2px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
background-image: none;
|
||||||
|
padding: 11px 20px 11px;
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: $brand-color;
|
||||||
|
color: $white;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $white;
|
||||||
|
background-color: darken($brand-color, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-btn-green {
|
||||||
|
background-color: $green;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $white;
|
||||||
|
background-color: darken($green, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Brand Modules
|
||||||
|
//
|
||||||
|
.ai-brand-module {
|
||||||
|
background-color: $brand-color;
|
||||||
|
padding: $base-pad;
|
||||||
|
color: $white;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not(.ai-btn) {
|
||||||
|
color: $brand-secondary-color;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-brand-module-actions {
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Header
|
||||||
|
//
|
||||||
|
.ai-header {
|
||||||
|
margin: 12px 0 -12px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-header-actions {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-logo {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Footer
|
||||||
|
//
|
||||||
|
.ai-note {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-list-inline {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-footer-links {
|
||||||
|
a {
|
||||||
|
&::after {
|
||||||
|
content: "\007c";
|
||||||
|
color: $white;
|
||||||
|
opacity: .5;
|
||||||
|
margin: 0 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
&:last-child {
|
||||||
|
a::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// General
|
||||||
|
//
|
||||||
|
.ai-module {
|
||||||
|
@include clearfix;
|
||||||
|
border: 1px solid $light-grey;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: $base-pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-container {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Field Controls
|
||||||
|
//
|
||||||
|
.ai-field-controls-wrap {
|
||||||
|
@include clearfix;
|
||||||
|
padding: $base-pad;
|
||||||
|
border: 1px solid $light-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-controls {
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-controls-visibility {
|
||||||
|
float: right;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-controls-visibility {
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-fields-expand-all {
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-right: 6px;
|
||||||
|
border-right: 1px solid $lighter-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Fields general structure
|
||||||
|
//
|
||||||
|
.ai-fields-container {
|
||||||
|
padding: $base-pad;
|
||||||
|
border-left: 1px solid $light-grey;
|
||||||
|
border-right: 1px solid $light-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-repeatable {
|
||||||
|
margin-bottom: $base-pad;
|
||||||
|
border: 1px solid $medium-grey;
|
||||||
|
box-shadow: 1px 1px 2px rgba(black, .07);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:only-child {
|
||||||
|
.ai-remove-field {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-container {
|
||||||
|
@include clearfix;
|
||||||
|
padding: $base-pad;
|
||||||
|
background-color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-head {
|
||||||
|
@include clearfix;
|
||||||
|
padding: 8px $base-pad 5px;
|
||||||
|
line-height: normal;
|
||||||
|
background-color: $medium-grey;
|
||||||
|
background: linear-gradient(to bottom, $lighter-grey, $medium-grey);
|
||||||
|
border-bottom: 1px solid $dark-grey;
|
||||||
|
|
||||||
|
.toggle-indicator {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-fields-sortable & {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-sort-handle {
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
color: $blue;
|
||||||
|
|
||||||
|
.dashicons {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.05em;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding-top: 3px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 80%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-toggle {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-cover {
|
||||||
|
float: left;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
margin-right: $base-pad;
|
||||||
|
background-color: $light-grey;
|
||||||
|
border: 1px solid $dark-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-split {
|
||||||
|
float: left;
|
||||||
|
width: calc(50% - 71px);
|
||||||
|
margin-right: $base-pad;
|
||||||
|
|
||||||
|
&:nth-child(2n + 1) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Form elements
|
||||||
|
//
|
||||||
|
.ai-container,
|
||||||
|
.ai-module {
|
||||||
|
.button {
|
||||||
|
.dashicons {
|
||||||
|
font-size: 1.2em;
|
||||||
|
line-height: 1.7em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-form-field-group {
|
||||||
|
padding: $base-pad;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
margin-bottom: $base-pad;
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-form-field {
|
||||||
|
margin-bottom: $base-pad;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="url"],
|
||||||
|
input[type="search"],
|
||||||
|
input[type="email"],
|
||||||
|
input[type="password"],
|
||||||
|
input[type="number"],
|
||||||
|
input[type="tel"],
|
||||||
|
input[type="date"],
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-module-settings {
|
||||||
|
.ai-form-field {
|
||||||
|
input[type="text"],
|
||||||
|
input[type="url"],
|
||||||
|
input[type="search"],
|
||||||
|
input[type="email"],
|
||||||
|
input[type="password"],
|
||||||
|
input[type="number"],
|
||||||
|
input[type="tel"],
|
||||||
|
input[type="date"],
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 200px;
|
||||||
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-form-field-addon {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding-right: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-help {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
font-style: italic;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-remove-field {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-upload-cover {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: initial;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-has-cover {
|
||||||
|
.ai-remove-cover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-cover-placeholder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-cover-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: .9em;
|
||||||
|
opacity: .8;
|
||||||
|
padding-top: 28px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
@include dashicon($icon: "\f128");
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-track-loading & {
|
||||||
|
&::before {
|
||||||
|
content: "\f463";
|
||||||
|
animation: rotation 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-remove-cover {
|
||||||
|
color: $white;
|
||||||
|
background-color: $red;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
opacity: .9;
|
||||||
|
transition: opacity $transition-time ease-in;
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashicons {
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-remove-all-fields,
|
||||||
|
.ai-remove-field {
|
||||||
|
.dashicons {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-add-field-batch,
|
||||||
|
.ai-add-field {
|
||||||
|
.dashicons {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-info-box {
|
||||||
|
background: $info-box-bg-color;
|
||||||
|
color: $info-box-text-color;
|
||||||
|
font-size: 12px;
|
||||||
|
border: solid 1px $info-box-border-color;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-player-type-message {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sortable specific
|
||||||
|
//
|
||||||
|
.ai-drop-placeholder {
|
||||||
|
background-color: $lighter-grey;
|
||||||
|
border: 2px dashed $dark-grey;
|
||||||
|
opacity: 0.5;
|
||||||
|
margin-bottom: $base-pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Collapsible
|
||||||
|
//
|
||||||
|
.ai-collapsed {
|
||||||
|
.ai-field-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-indicator {
|
||||||
|
&::before {
|
||||||
|
content: "\f140" !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Shortcode field
|
||||||
|
//
|
||||||
|
.ai-module-shortcode {
|
||||||
|
.code {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 3px;
|
||||||
|
padding: 10px 10px 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
background: $lighter-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Soundcloud module
|
||||||
|
//
|
||||||
|
.ai-sync-soundcloud.button { // Overcoming specificity
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "\f463";
|
||||||
|
color: #d54e21;
|
||||||
|
display: inline-block;
|
||||||
|
font: 400 19px/1 dashicons;
|
||||||
|
speak: none;
|
||||||
|
position: relative;
|
||||||
|
left: -1px;
|
||||||
|
top: 4px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-track-loading & {
|
||||||
|
&::before {
|
||||||
|
animation: rotation 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-soundcloud-track {
|
||||||
|
.ai-sync-soundcloud {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-upload {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Media queries
|
||||||
|
//
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.ai-field-controls,
|
||||||
|
.ai-field-controls-visibility {
|
||||||
|
margin: 0;
|
||||||
|
float: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-controls {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-split {
|
||||||
|
float: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-field-cover {
|
||||||
|
margin-bottom: $base-pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-footer {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.ai-brand-module-actions {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class^="ai-col"] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 782px) {
|
||||||
|
.ai-container,
|
||||||
|
.ai-module {
|
||||||
|
.button {
|
||||||
|
.dashicons {
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-form-field-addon {
|
||||||
|
.button {
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.ai-field-controls {
|
||||||
|
.button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-header {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.ai-brand-module-actions {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-btn {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class^="ai-col"] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
wp-content/plugins/audioigniter/assets/images/logo.svg
Normal file
1
wp-content/plugins/audioigniter/assets/images/logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.6 KiB |
BIN
wp-content/plugins/audioigniter/assets/images/vc_icon.png
Normal file
BIN
wp-content/plugins/audioigniter/assets/images/vc_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
529
wp-content/plugins/audioigniter/assets/js/audioigniter.js
Normal file
529
wp-content/plugins/audioigniter/assets/js/audioigniter.js
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
/* eslint-env browser, jquery */
|
||||||
|
/* eslint-disable prefer-arrow-callback, prefer-template, func-names, no-var, object-shorthand, no-alert */
|
||||||
|
/* global wp ai_scripts */
|
||||||
|
|
||||||
|
jQuery(function($) {
|
||||||
|
// Return early if ai_scripts are not available
|
||||||
|
if (!ai_scripts) {
|
||||||
|
// eslint-disable-line camelcase
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line vars-on-top
|
||||||
|
var AudioIgniter = (function() {
|
||||||
|
var el = {
|
||||||
|
$trackContainer: $(".ai-fields-container"),
|
||||||
|
trackFieldClassName: ".ai-field-repeatable",
|
||||||
|
$addTrackButtonTop: $(".ai-add-field-top"),
|
||||||
|
$addTrackButtonBottom: $(".ai-add-field-bottom"),
|
||||||
|
removeFieldButtonClassName: ".ai-remove-field",
|
||||||
|
$removeAllTracksButton: $(".ai-remove-all-fields"),
|
||||||
|
$batchUploadButton: $(".ai-add-field-batch"),
|
||||||
|
audioUploadButtonClassName: ".ai-upload",
|
||||||
|
coverUploadButtonClassName: ".ai-field-upload-cover",
|
||||||
|
coverRemoveClassName: ".ai-remove-cover",
|
||||||
|
fieldTitleClassName: ".ai-field-title",
|
||||||
|
trackTitleClassName: ".ai-track-title",
|
||||||
|
trackArtistClassName: ".ai-track-artist",
|
||||||
|
trackLyricsClassName: ".ai-track-lyrics",
|
||||||
|
trackUrlClassName: ".ai-track-url",
|
||||||
|
hasCoverClass: "ai-has-cover",
|
||||||
|
fieldHeadClassName: ".ai-field-head",
|
||||||
|
fieldCollapsedClass: "ai-collapsed",
|
||||||
|
$expandAllButton: $(".ai-fields-expand-all"),
|
||||||
|
$collapseAllButton: $(".ai-fields-collapse-all"),
|
||||||
|
$shortcodeInputField: $("#ai_shortcode"),
|
||||||
|
soundCloudTrackClass: "ai-soundcloud-track"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a rfc4122 version 4 compliant UUID
|
||||||
|
* http://stackoverflow.com/a/2117523
|
||||||
|
*
|
||||||
|
* @returns {string} - UUID
|
||||||
|
*/
|
||||||
|
function uuid() {
|
||||||
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(
|
||||||
|
c
|
||||||
|
) {
|
||||||
|
var r = (Math.random() * 16) | 0;
|
||||||
|
var v = c === "x" ? r : (r & 0x3) | 0x8;
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if field is collapsed
|
||||||
|
*
|
||||||
|
* @param {Object} $field - jQuery object
|
||||||
|
* @returns {*|boolean}
|
||||||
|
*/
|
||||||
|
function isFieldCollapsed($field) {
|
||||||
|
return $field.hasClass(el.fieldCollapsedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse a field
|
||||||
|
*
|
||||||
|
* @param {Object} $field - jQuery object
|
||||||
|
*/
|
||||||
|
function collapseField($field) {
|
||||||
|
$field.addClass(el.fieldCollapsedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand a field
|
||||||
|
*
|
||||||
|
* @param {Object} $field - jQuery object
|
||||||
|
*/
|
||||||
|
function expandField($field) {
|
||||||
|
$field.removeClass(el.fieldCollapsedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the cover image placeholder state
|
||||||
|
*
|
||||||
|
* @param {Object} $field - the remove button jQuery object
|
||||||
|
*/
|
||||||
|
function resetCoverImage($field) {
|
||||||
|
var $coverWrap = $field.find("." + el.hasCoverClass);
|
||||||
|
|
||||||
|
$coverWrap
|
||||||
|
.removeClass(el.hasCoverClass)
|
||||||
|
.find("img")
|
||||||
|
.attr("src", "")
|
||||||
|
.attr("alt", "");
|
||||||
|
$coverWrap
|
||||||
|
.parent()
|
||||||
|
.find("input")
|
||||||
|
.val("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets a form field
|
||||||
|
* - Clears input values
|
||||||
|
* - Clears thumbnail
|
||||||
|
*
|
||||||
|
* @param {object} $field - the field's jQuery object
|
||||||
|
* @param {string} [hash] - UUID or random hash
|
||||||
|
*/
|
||||||
|
function resetField($field, hash) {
|
||||||
|
var fieldHash = $field.data("uid");
|
||||||
|
var newHash = hash || uuid();
|
||||||
|
|
||||||
|
$field.attr("data-uid", newHash);
|
||||||
|
$field
|
||||||
|
.find("input, textarea")
|
||||||
|
.not(":button")
|
||||||
|
.each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
$this.attr("id", $this.attr("id").replace(fieldHash, newHash));
|
||||||
|
$this.attr("name", $this.attr("name").replace(fieldHash, newHash));
|
||||||
|
$this.val("");
|
||||||
|
});
|
||||||
|
$field.find("label").each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
$this.attr("for", $this.attr("for").replace(fieldHash, newHash));
|
||||||
|
});
|
||||||
|
$field.find(el.fieldTitleClassName).text("");
|
||||||
|
$field.removeClass(el.soundCloudTrackClass);
|
||||||
|
expandField($field);
|
||||||
|
resetCoverImage($field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a track field is clear of values
|
||||||
|
*
|
||||||
|
* @param {object} $field - Track field jQuery object
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isTrackFieldEmpty($field) {
|
||||||
|
var isEmpty = true;
|
||||||
|
var $inputs = $field.find("input");
|
||||||
|
$inputs.each(function() {
|
||||||
|
if ($(this).val()) {
|
||||||
|
isEmpty = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first field from $trackContainer
|
||||||
|
* and appends it back after resetting it
|
||||||
|
*
|
||||||
|
* @param {string} [hash] - UUID or random hash
|
||||||
|
*
|
||||||
|
* return {Object} - jQuery object
|
||||||
|
*/
|
||||||
|
function getNewTrackField(hash) {
|
||||||
|
var newHash = hash || uuid();
|
||||||
|
var $clone = el.$trackContainer
|
||||||
|
.find(el.trackFieldClassName)
|
||||||
|
.first()
|
||||||
|
.clone()
|
||||||
|
.hide()
|
||||||
|
.fadeIn();
|
||||||
|
resetField($clone, newHash);
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an element (or many) from the DOM
|
||||||
|
* by fading it out first
|
||||||
|
*
|
||||||
|
* @param {Object} $el - jQuery object of the element(s) to be removed
|
||||||
|
* @param {Function} [callback] - Optional callback
|
||||||
|
*/
|
||||||
|
function removeElement($el, callback) {
|
||||||
|
$el.fadeOut("fast", function() {
|
||||||
|
$(this).remove();
|
||||||
|
|
||||||
|
if (callback && typeof callback === "function") {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates a track field
|
||||||
|
*
|
||||||
|
* @param {Object} $field - The field's jQuery object
|
||||||
|
* @param {Object} media - WP Media Manager media object
|
||||||
|
*/
|
||||||
|
function populateTrackField($field, media) {
|
||||||
|
var $urlInput = $field.find(el.trackUrlClassName);
|
||||||
|
var $titleInput = $field.find(el.trackTitleClassName);
|
||||||
|
var $artistInput = $field.find(el.trackArtistClassName);
|
||||||
|
var $fieldTitle = $field.find(el.fieldTitleClassName);
|
||||||
|
|
||||||
|
if (media.url) {
|
||||||
|
$urlInput.val(media.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media.title && $titleInput.val() === "") {
|
||||||
|
$titleInput.val(media.title);
|
||||||
|
$fieldTitle.text(media.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media.meta && media.meta.artist && $artistInput.val() === "") {
|
||||||
|
$artistInput.val(media.meta.artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a cover image for the field
|
||||||
|
*
|
||||||
|
* @param $field - The field's jQuery object
|
||||||
|
* @param {Object} cover - Cover object
|
||||||
|
* @param {number} cover.id - Image ID
|
||||||
|
* @param {string} cover.url - Image URL
|
||||||
|
* @param {string} [cover.alt] - Image alt text
|
||||||
|
*/
|
||||||
|
function setTrackFieldCover($field, cover) {
|
||||||
|
var $coverField = $field.find(el.coverUploadButtonClassName);
|
||||||
|
|
||||||
|
if (!cover || !cover.url || !cover.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$coverField
|
||||||
|
.find("img")
|
||||||
|
.attr("src", cover.url)
|
||||||
|
.attr("alt", cover.alt || "");
|
||||||
|
|
||||||
|
$coverField
|
||||||
|
.addClass(el.hasCoverClass)
|
||||||
|
.siblings("input")
|
||||||
|
.val(cover.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the WordPress Media Manager
|
||||||
|
*
|
||||||
|
* @param {Object} opts - Options object
|
||||||
|
* @param {string} opts.handler - Handler identifier of the media frame,
|
||||||
|
* this allows multiple media manager frames with different functionalities
|
||||||
|
* @param {string} [opts.type] - Filter media manager by type (audio, image etc)
|
||||||
|
* @param {string} [opts.title=Select Media] - Title of the media manager frame
|
||||||
|
* @param {boolean} [opts.multiple=false] - Accept multiple selections
|
||||||
|
* @param {Function} [opts.onMediaSelect] - Do something after media selection
|
||||||
|
*/
|
||||||
|
function wpMediaInit(opts) {
|
||||||
|
if (!opts.handler) {
|
||||||
|
throw new Error("Missing `handler` option");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
var multiple = opts.multiple || false;
|
||||||
|
var title = opts.title || "Select media";
|
||||||
|
var mediaManager = wp.media.frames[opts.handler];
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
|
if (mediaManager) {
|
||||||
|
mediaManager.open();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaManager = wp.media({
|
||||||
|
title: title,
|
||||||
|
multiple: multiple,
|
||||||
|
library: {
|
||||||
|
type: opts.type
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mediaManager.open();
|
||||||
|
|
||||||
|
mediaManager.on("select", function() {
|
||||||
|
var attachments;
|
||||||
|
var attachmentModels = mediaManager.state().get("selection");
|
||||||
|
|
||||||
|
if (multiple) {
|
||||||
|
attachments = attachmentModels.map(function(attachment) {
|
||||||
|
return attachment.toJSON();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
attachments = attachmentModels.first().toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.onMediaSelect && typeof opts.onMediaSelect === "function") {
|
||||||
|
opts.onMediaSelect(attachments);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapsible bindings
|
||||||
|
*/
|
||||||
|
el.$trackContainer.on("click", el.fieldHeadClassName, function(e) {
|
||||||
|
var $this = $(this);
|
||||||
|
var $parentField = $this.parents(el.trackFieldClassName);
|
||||||
|
|
||||||
|
if (isFieldCollapsed($parentField)) {
|
||||||
|
expandField($parentField);
|
||||||
|
} else {
|
||||||
|
collapseField($parentField);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
el.$expandAllButton.on("click", function(e) {
|
||||||
|
expandField(el.$trackContainer.find(el.trackFieldClassName));
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
el.$collapseAllButton.on("click", function(e) {
|
||||||
|
collapseField(el.$trackContainer.find(el.trackFieldClassName));
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field control bindings
|
||||||
|
* (Add, remove buttons etc)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Bind track title to title input value */
|
||||||
|
el.$trackContainer.on("keyup", el.trackTitleClassName, function() {
|
||||||
|
var $this = $(this);
|
||||||
|
var $fieldTitle = $this
|
||||||
|
.parents(el.trackFieldClassName)
|
||||||
|
.find(el.fieldTitleClassName);
|
||||||
|
$fieldTitle.text($this.val());
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Add Track Top*/
|
||||||
|
el.$addTrackButtonTop.on("click", function() {
|
||||||
|
el.$trackContainer.prepend(getNewTrackField());
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Add Track Bottom*/
|
||||||
|
el.$addTrackButtonBottom.on("click", function () {
|
||||||
|
el.$trackContainer.append(getNewTrackField());
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Remove Track */
|
||||||
|
el.$trackContainer.on("click", el.removeFieldButtonClassName, function() {
|
||||||
|
var $this = $(this);
|
||||||
|
removeElement($this.parents(".ai-field-repeatable"));
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Remove All Tracks */
|
||||||
|
el.$removeAllTracksButton.on("click", function() {
|
||||||
|
var $trackFields = el.$trackContainer.find(el.trackFieldClassName);
|
||||||
|
|
||||||
|
if (window.confirm(ai_scripts.messages.confirm_clear_tracks)) {
|
||||||
|
if ($trackFields.length > 1) {
|
||||||
|
removeElement($trackFields.slice(1));
|
||||||
|
resetField($trackFields);
|
||||||
|
} else {
|
||||||
|
resetField($trackFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind media uploaders
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Audio upload */
|
||||||
|
el.$trackContainer.on("click", el.audioUploadButtonClassName, function() {
|
||||||
|
var $this = $(this);
|
||||||
|
var $parentTrackField = $this.parents(el.trackFieldClassName);
|
||||||
|
|
||||||
|
wpMediaInit({
|
||||||
|
handler: "ai-audio",
|
||||||
|
title: ai_scripts.messages.media_title_upload,
|
||||||
|
type: "audio",
|
||||||
|
onMediaSelect: function(media) {
|
||||||
|
populateTrackField($parentTrackField, media);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cover image upload
|
||||||
|
*
|
||||||
|
* Element `coverUploadButtonClassName` *must* have
|
||||||
|
* an `img` and `coverRemoveClassName` elements
|
||||||
|
* as children
|
||||||
|
*/
|
||||||
|
el.$trackContainer
|
||||||
|
.on("click", el.coverUploadButtonClassName, function(e) {
|
||||||
|
var $this = $(this);
|
||||||
|
|
||||||
|
wpMediaInit({
|
||||||
|
handler: "ai-cover",
|
||||||
|
title: ai_scripts.messages.media_title_upload_cover,
|
||||||
|
type: "image",
|
||||||
|
onMediaSelect: function(media) {
|
||||||
|
setTrackFieldCover($this.parents(el.trackFieldClassName), {
|
||||||
|
id: media.id,
|
||||||
|
url: media.sizes.thumbnail.url,
|
||||||
|
alt: media.alt
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
})
|
||||||
|
/* Remove Image */
|
||||||
|
.on("click", el.coverRemoveClassName, function(e) {
|
||||||
|
var $this = $(this);
|
||||||
|
resetCoverImage($this.parents(el.trackFieldClassName));
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide / show options based on player type
|
||||||
|
*
|
||||||
|
* Different player types support different kind of options.
|
||||||
|
* E.g. "Simple Player" doesn't support tracklist height, etc.
|
||||||
|
*/
|
||||||
|
var $settingsWrap = $(".ai-module-settings");
|
||||||
|
var $typeSelect = $(".ai-form-select-player-type");
|
||||||
|
|
||||||
|
function getUnsupportedSettings($el) {
|
||||||
|
var settingsString = $el.data("no-support");
|
||||||
|
|
||||||
|
if (typeof settingsString !== "string") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingsString
|
||||||
|
.replace(/\s/g, "") // remove all whitespace
|
||||||
|
.split(",")
|
||||||
|
.map(function(x) {
|
||||||
|
return "_audioigniter_" + x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterUIBasedOnPlayerType($el) {
|
||||||
|
var type = $el.val();
|
||||||
|
|
||||||
|
// Reset styles
|
||||||
|
var $shortcodeMetaBox = $("#ai-meta-box-shortcode");
|
||||||
|
var $messageBox = $(".ai-player-type-message");
|
||||||
|
var info = $el.data("info");
|
||||||
|
|
||||||
|
$shortcodeMetaBox.show();
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
$messageBox.text(info).show();
|
||||||
|
} else {
|
||||||
|
$messageBox.text("").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player specific controls
|
||||||
|
switch (type) {
|
||||||
|
case "global-footer":
|
||||||
|
$shortcodeMetaBox.hide();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterSettings() {
|
||||||
|
var $formFields;
|
||||||
|
var $type = $typeSelect.find(":selected");
|
||||||
|
var unsupportedSettings = getUnsupportedSettings($type);
|
||||||
|
|
||||||
|
filterUIBasedOnPlayerType($type);
|
||||||
|
|
||||||
|
if (unsupportedSettings.length === 0) {
|
||||||
|
$formFields = $settingsWrap.find(".ai-form-field");
|
||||||
|
$formFields.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settingsWrap.find("input", "select", "textarea").each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
var $parent = $this.parents(".ai-form-field");
|
||||||
|
if (unsupportedSettings.indexOf($this.attr("name")) > -1) {
|
||||||
|
$parent.hide();
|
||||||
|
} else {
|
||||||
|
$parent.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterSettings();
|
||||||
|
$typeSelect.on("change", filterSettings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcode select on click
|
||||||
|
*/
|
||||||
|
el.$shortcodeInputField.on("click", function() {
|
||||||
|
$(this).select();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export public methods and variables
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
elements: el,
|
||||||
|
uuid: uuid,
|
||||||
|
collapseField: collapseField,
|
||||||
|
expandField: expandField,
|
||||||
|
isFieldCollapsed: isFieldCollapsed,
|
||||||
|
isTrackFieldEmpty: isTrackFieldEmpty,
|
||||||
|
resetField: resetField,
|
||||||
|
resetCoverImage: resetCoverImage,
|
||||||
|
getNewTrackField: getNewTrackField,
|
||||||
|
removeElement: removeElement,
|
||||||
|
populateTrackField: populateTrackField,
|
||||||
|
setTrackFieldCover: setTrackFieldCover,
|
||||||
|
wpMediaInit: wpMediaInit
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Expose the AudioIgniter instance as a global
|
||||||
|
if (!window.AudioIgniter) {
|
||||||
|
window.AudioIgniter = AudioIgniter;
|
||||||
|
}
|
||||||
|
});
|
1375
wp-content/plugins/audioigniter/audioigniter.php
Normal file
1375
wp-content/plugins/audioigniter/audioigniter.php
Normal file
File diff suppressed because it is too large
Load Diff
367
wp-content/plugins/audioigniter/class-audioigniter-sanitizer.php
Normal file
367
wp-content/plugins/audioigniter/class-audioigniter-sanitizer.php
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AudioIgniter_Sanitizer
|
||||||
|
*
|
||||||
|
* Provides static sanitization functions.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
class AudioIgniter_Sanitizer {
|
||||||
|
/**
|
||||||
|
* Sanitizes the player type.
|
||||||
|
*
|
||||||
|
* @version 1.4.0
|
||||||
|
* @since 1.4.0
|
||||||
|
*
|
||||||
|
* @uses AudioIgniter->get_player_types()
|
||||||
|
*
|
||||||
|
* @param string $value Player type to sanitize.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function player_type( $value ) {
|
||||||
|
$choices = AudioIgniter()->get_player_types();
|
||||||
|
if ( array_key_exists( $value, $choices ) ) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'full';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a playlist (repeatable tracks).
|
||||||
|
*
|
||||||
|
* @version 1.2.0
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @uses AudioIgniter_Sanitizer::playlist_track()
|
||||||
|
*
|
||||||
|
* @param array $post_tracks Input values to sanitize, as passed by the playlist metabox.
|
||||||
|
* @param int|null $post_id Optional. Post ID where the track belongs to.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function metabox_playlist( $post_tracks, $post_id = null ) {
|
||||||
|
if ( empty( $post_tracks ) || ! is_array( $post_tracks ) ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$tracks = array();
|
||||||
|
|
||||||
|
foreach ( $post_tracks as $uid => $track_data ) {
|
||||||
|
$track = self::playlist_track( $track_data, $post_id, $uid );
|
||||||
|
if ( false !== $track ) {
|
||||||
|
$tracks[] = $track;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply_filters( 'audioigniter_sanitize_playlist', $tracks, $post_tracks, $post_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a single playlist track.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @uses AudioIgniter::get_default_track_values()
|
||||||
|
*
|
||||||
|
* @param array $track Input values to sanitize.
|
||||||
|
* @param int|null $post_id Optional. Post ID where the track belongs to.
|
||||||
|
* @param string $track_uid Optional. UID that identifies the track in the metabox list.
|
||||||
|
*
|
||||||
|
* @return array|false Array if at least one field is completed, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function playlist_track( $track, $post_id = null, $track_uid = '' ) {
|
||||||
|
$track = wp_parse_args( $track, AudioIgniter::get_default_track_values() );
|
||||||
|
|
||||||
|
$sanitized_track = array();
|
||||||
|
|
||||||
|
$sanitized_track['cover_id'] = intval( $track['cover_id'] );
|
||||||
|
$sanitized_track['title'] = sanitize_text_field( $track['title'] );
|
||||||
|
$sanitized_track['artist'] = sanitize_text_field( $track['artist'] );
|
||||||
|
$sanitized_track['track_url'] = esc_url_raw( $track['track_url'] );
|
||||||
|
$sanitized_track['buy_link'] = esc_url_raw( $track['buy_link'] );
|
||||||
|
$sanitized_track['download_url'] = esc_url_raw( $track['download_url'] );
|
||||||
|
|
||||||
|
$sanitized_track = array_map( 'trim', $sanitized_track );
|
||||||
|
|
||||||
|
$tmp = array_filter( $sanitized_track );
|
||||||
|
if ( empty( $tmp ) ) {
|
||||||
|
$sanitized_track = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply_filters( 'audioigniter_sanitize_playlist_track', $sanitized_track, $track, $post_id, $track_uid );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a checkbox value.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param int|string|bool $input Input value to sanitize.
|
||||||
|
*
|
||||||
|
* @return int|string Returns 1 if $input evaluates to 1, an empty string otherwise.
|
||||||
|
*/
|
||||||
|
public static function checkbox( $input ) {
|
||||||
|
if ( 1 == $input ) { // WPCS: loose comparison ok.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a checkbox value. Value is passed by reference.
|
||||||
|
*
|
||||||
|
* Useful when sanitizing form checkboxes. Since browsers don't send any data when a checkbox
|
||||||
|
* is not checked, checkbox() throws an error.
|
||||||
|
* checkbox_ref() however evaluates &$input as null so no errors are thrown.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param int|string|bool &$input Input value to sanitize.
|
||||||
|
*
|
||||||
|
* @return int|string Returns 1 if $input evaluates to 1, an empty string otherwise.
|
||||||
|
*/
|
||||||
|
public static function checkbox_ref( &$input ) {
|
||||||
|
if ( 1 == $input ) { // WPCS: loose comparison ok.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes integer input while differentiating zero from empty string.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param mixed $input Input value to sanitize.
|
||||||
|
*
|
||||||
|
* @return int|string Integer value (including zero), or an empty string otherwise.
|
||||||
|
*/
|
||||||
|
public static function intval_or_empty( $input ) {
|
||||||
|
if ( is_null( $input ) || false === $input || '' === $input ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 0 == $input ) { // WPCS: loose comparison ok.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intval( $input );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a sanitized hex color code.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param string $str The color string to be sanitized.
|
||||||
|
* @param bool $return_hash Whether to return the color code prepended by a hash.
|
||||||
|
* @param string $return_fail The value to return on failure.
|
||||||
|
*
|
||||||
|
* @return string A valid hex color code on success, an empty string on failure.
|
||||||
|
*/
|
||||||
|
public static function hex_color( $str, $return_hash = true, $return_fail = '' ) {
|
||||||
|
if ( false === $str || empty( $str ) || 'false' === $str ) {
|
||||||
|
return $return_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow keywords and predefined colors
|
||||||
|
if ( in_array( $str, array( 'transparent', 'initial', 'inherit', 'black', 'silver', 'gray', 'grey', 'white', 'maroon', 'red', 'purple', 'fuchsia', 'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua', 'orange', 'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque', 'blanchedalmond', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen', 'linen', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'whitesmoke', 'yellowgreen', 'rebeccapurple' ), true ) ) {
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the hash if not there.
|
||||||
|
// The regex below depends on in.
|
||||||
|
if ( substr( $str, 0, 1 ) !== '#' ) {
|
||||||
|
$str = '#' . $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match( '/(#)([0-9a-fA-F]{6})/', $str, $matches );
|
||||||
|
|
||||||
|
if ( count( $matches ) === 3 ) {
|
||||||
|
if ( $return_hash ) {
|
||||||
|
return $matches[1] . $matches[2];
|
||||||
|
} else {
|
||||||
|
return $matches[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a CSS color.
|
||||||
|
*
|
||||||
|
* Tries to validate and sanitize values in these formats:
|
||||||
|
* - rgba()
|
||||||
|
* - 3 and 6 digit hex values, optionally prefixed with `#`
|
||||||
|
* - Predefined CSS named colors/keywords, such as 'transparent', 'initial', 'inherit', 'black', 'silver', etc.
|
||||||
|
*
|
||||||
|
* @since 1.7.1
|
||||||
|
*
|
||||||
|
* @param string $color The color value to sanitize
|
||||||
|
* @param bool $return_hash Whether to return hex color prefixed with a `#`
|
||||||
|
* @param string $return_fail Value to return when $color fails validation.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function rgba_color( $color, $return_hash = true, $return_fail = '' ) {
|
||||||
|
if ( false === $color || empty( $color ) || 'false' === $color ) {
|
||||||
|
return $return_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow keywords and predefined colors
|
||||||
|
if ( in_array( $color, array( 'transparent', 'initial', 'inherit', 'black', 'silver', 'gray', 'grey', 'white', 'maroon', 'red', 'purple', 'fuchsia', 'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua', 'orange', 'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque', 'blanchedalmond', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen', 'linen', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'whitesmoke', 'yellowgreen', 'rebeccapurple' ), true ) ) {
|
||||||
|
return $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match( '/rgba\(\s*(\d{1,3}\.?\d*\%?)\s*,\s*(\d{1,3}\.?\d*\%?)\s*,\s*(\d{1,3}\.?\d*\%?)\s*,\s*(\d{1}\.?\d*\%?)\s*\)/', $color, $rgba_matches );
|
||||||
|
if ( ! empty( $rgba_matches ) && 5 === count( $rgba_matches ) ) {
|
||||||
|
for ( $i = 1; $i < 4; $i++ ) {
|
||||||
|
if ( strpos( $rgba_matches[ $i ], '%' ) !== false ) {
|
||||||
|
$rgba_matches[ $i ] = self::float_0_100( $rgba_matches[ $i ] );
|
||||||
|
} else {
|
||||||
|
$rgba_matches[ $i ] = self::int_0_255( $rgba_matches[ $i ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rgba_matches[4] = self::float_0_1( $rgba_matches[ $i ] );
|
||||||
|
return sprintf( 'rgba(%s, %s, %s, %s)', $rgba_matches[1], $rgba_matches[2], $rgba_matches[3], $rgba_matches[4] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a color function either. Let's see if it's a hex color.
|
||||||
|
|
||||||
|
// Include the hash if not there.
|
||||||
|
// The regex below depends on in.
|
||||||
|
if ( substr( $color, 0, 1 ) !== '#' ) {
|
||||||
|
$color = '#' . $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match( '/(#)([0-9a-fA-F]{6})/', $color, $matches );
|
||||||
|
|
||||||
|
if ( 3 === count( $matches ) ) {
|
||||||
|
if ( $return_hash ) {
|
||||||
|
return $matches[1] . $matches[2];
|
||||||
|
} else {
|
||||||
|
return $matches[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a percentage value, 0% - 100%
|
||||||
|
*
|
||||||
|
* Accepts float values with or without the percentage sign `%`
|
||||||
|
* Returns a string suffixed with the percentage sign `%`.
|
||||||
|
*
|
||||||
|
* @since 1.7.1
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return string A percentage value, including the percentage sign.
|
||||||
|
*/
|
||||||
|
public static function float_0_100( $value ) {
|
||||||
|
$value = str_replace( '%', '', $value );
|
||||||
|
if ( floatval( $value ) > 100 ) {
|
||||||
|
$value = 100;
|
||||||
|
} elseif ( floatval( $value ) < 0 ) {
|
||||||
|
$value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return floatval( $value ) . '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a decimal CSS color value, 0 - 255.
|
||||||
|
*
|
||||||
|
* Accepts float values with or without the percentage sign `%`
|
||||||
|
* Returns a string suffixed with the percentage sign `%`.
|
||||||
|
*
|
||||||
|
* @since 1.7.1
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return int A number between 0-255.
|
||||||
|
*/
|
||||||
|
public static function int_0_255( $value ) {
|
||||||
|
if ( intval( $value ) > 255 ) {
|
||||||
|
$value = 255;
|
||||||
|
} elseif ( intval( $value ) < 0 ) {
|
||||||
|
$value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intval( $value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a CSS opacity value, 0 - 1.
|
||||||
|
*
|
||||||
|
* @since 1.7.1
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return float A number between 0-1.
|
||||||
|
*/
|
||||||
|
public static function float_0_1( $value ) {
|
||||||
|
if ( floatval( $value ) > 1 ) {
|
||||||
|
$value = 1;
|
||||||
|
} elseif ( floatval( $value ) < 0 ) {
|
||||||
|
$value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return floatval( $value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes elements whose keys are not valid data-attribute names.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param array $array Input array to sanitize.
|
||||||
|
*
|
||||||
|
* @return array()
|
||||||
|
*/
|
||||||
|
public static function html_data_attributes_array( $array ) {
|
||||||
|
$keys = array_keys( $array );
|
||||||
|
$key_prefix = 'data-';
|
||||||
|
|
||||||
|
// Remove keys that are not data attributes.
|
||||||
|
foreach ( $keys as $key ) {
|
||||||
|
if ( substr( $key, 0, strlen( $key_prefix ) ) !== $key_prefix ) {
|
||||||
|
unset( $array[ $key ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false when value is empty or null.
|
||||||
|
* Only use with array_filter() or similar, as the naming can lead to confusion.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*
|
||||||
|
* @param mixed $value Array value to check whether empty or null.
|
||||||
|
*
|
||||||
|
* @return bool false if empty or null, true otherwise.
|
||||||
|
*/
|
||||||
|
public static function array_filter_empty_null( $value ) {
|
||||||
|
if ( '' === $value || is_null( $value ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
wp-content/plugins/audioigniter/ignitefile.js
Normal file
8
wp-content/plugins/audioigniter/ignitefile.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
name: 'audioigniter',
|
||||||
|
paths: {
|
||||||
|
src: {
|
||||||
|
styles: ['./assets/css/*.scss'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
391
wp-content/plugins/audioigniter/languages/audioigniter.pot
Normal file
391
wp-content/plugins/audioigniter/languages/audioigniter.pot
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: AudioIgniter\n"
|
||||||
|
"POT-Creation-Date: 2021-12-14 12:04+0200\n"
|
||||||
|
"PO-Revision-Date: 2016-08-29 19:22+0300\n"
|
||||||
|
"Last-Translator: Anastis Sourgoutsidis <anastis@cssigniter.com>\n"
|
||||||
|
"Language-Team: Anastis Sourgoutsidis <anastis@cssigniter.com>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||||
|
"X-Generator: Poedit 3.0.1\n"
|
||||||
|
"X-Poedit-Basepath: ..\n"
|
||||||
|
"X-Poedit-WPHeader: audioigniter.php\n"
|
||||||
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
|
||||||
|
"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
|
||||||
|
"_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
|
||||||
|
"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
|
||||||
|
"X-Poedit-SearchPath-0: .\n"
|
||||||
|
"X-Poedit-SearchPathExcluded-0: *.js\n"
|
||||||
|
|
||||||
|
#. translators: %s is the track's title.
|
||||||
|
#: audioigniter.php:213
|
||||||
|
#, php-format
|
||||||
|
msgid "Play %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %s is the track's title.
|
||||||
|
#: audioigniter.php:215
|
||||||
|
#, php-format
|
||||||
|
msgid "Pause %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:216
|
||||||
|
msgid "Previous track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:217
|
||||||
|
msgid "Next track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:218
|
||||||
|
msgid "Toggle track listing repeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:219
|
||||||
|
msgid "Toggle track repeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:220
|
||||||
|
msgid "Toggle track listing visibility"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:221
|
||||||
|
msgid "Buy this track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:222
|
||||||
|
msgid "Download this track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:223
|
||||||
|
msgid "Volume Up"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:224
|
||||||
|
msgid "Volume Down"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:225
|
||||||
|
msgid "Open track lyrics"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:226
|
||||||
|
msgid "Set playback rate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:227
|
||||||
|
msgid "Skip forward"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:228
|
||||||
|
msgid "Skip backward"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:229
|
||||||
|
msgid "Shuffle"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:234
|
||||||
|
msgid ""
|
||||||
|
"Do you really want to remove all tracks? (This will not delete your audio "
|
||||||
|
"files)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:235
|
||||||
|
msgid "Select or upload audio media"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:236
|
||||||
|
msgid "Select a cover image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:273
|
||||||
|
msgctxt "post type general name"
|
||||||
|
msgid "Playlists"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:274 audioigniter.php:289
|
||||||
|
msgctxt "post type singular name"
|
||||||
|
msgid "Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:275
|
||||||
|
msgctxt "admin menu"
|
||||||
|
msgid "Playlists"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:276
|
||||||
|
msgctxt "add new on admin bar"
|
||||||
|
msgid "Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:277 audioigniter.php:278
|
||||||
|
msgid "Add New Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:279
|
||||||
|
msgid "Edit Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:280
|
||||||
|
msgid "New Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:281
|
||||||
|
msgid "View Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:282
|
||||||
|
msgid "Search Playlists"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:283
|
||||||
|
msgid "No playlists found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:284
|
||||||
|
msgid "No playlists found in the trash"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:309 audioigniter.php:830
|
||||||
|
msgid "Tracks"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:310
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:311
|
||||||
|
msgid "Shortcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:370
|
||||||
|
msgid "AudioIgniter Logo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:379
|
||||||
|
msgid "Upgrade to Pro"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:403
|
||||||
|
msgid "Support"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:407
|
||||||
|
msgid "Documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:411
|
||||||
|
msgid "Rate this plugin"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %s is a URL.
|
||||||
|
#: audioigniter.php:434
|
||||||
|
#, php-format
|
||||||
|
msgid ""
|
||||||
|
"Thank you for creating with <a href=\"%s\" target=\"_blank\">AudioIgniter</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:479
|
||||||
|
msgid "Toggle track visibility"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:490
|
||||||
|
msgid "Remove Cover Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:503
|
||||||
|
msgid "Upload Cover"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:521 audioigniter.php:528
|
||||||
|
msgid "Title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:536 audioigniter.php:543
|
||||||
|
msgid "Artist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:552 audioigniter.php:559
|
||||||
|
msgid "Buy link"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:572 audioigniter.php:581
|
||||||
|
msgid "Audio file or radio stream"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:585
|
||||||
|
msgid "Upload"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:596 audioigniter.php:603
|
||||||
|
msgid "Download URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:612
|
||||||
|
msgid "Remove Track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:627
|
||||||
|
msgid "Add Track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:634
|
||||||
|
msgid "Clear Playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:640
|
||||||
|
msgid "Expand All"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:643
|
||||||
|
msgid "Collapse All"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:682
|
||||||
|
msgid "Player & Track listing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:687
|
||||||
|
msgid "Player Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:718
|
||||||
|
msgid "Show track listing by default"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:732
|
||||||
|
msgid "Show track listing visibility toggle button"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:746
|
||||||
|
msgid "Reverse track order"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:752
|
||||||
|
msgid "Starting volume"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:763
|
||||||
|
msgid "0-100"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:768
|
||||||
|
msgid "Enter a value between 0 and 100 in increments of 10"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:782
|
||||||
|
msgid "Limit track listing height"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:788 audioigniter.php:798
|
||||||
|
msgid "Track listing height"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:803
|
||||||
|
msgid "Set a number of pixels"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:809
|
||||||
|
msgid "Maximum player width"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:817
|
||||||
|
msgid "Automatic width"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:822
|
||||||
|
#, php-format
|
||||||
|
msgid ""
|
||||||
|
"Set a number of pixels, or leave empty to automatically cover 100% of the "
|
||||||
|
"available area (recommended)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:842
|
||||||
|
msgid "Show track numbers in tracklist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:856
|
||||||
|
msgid "Show track covers in tracklist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:870
|
||||||
|
msgid "Show active track's cover"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:884
|
||||||
|
msgid "Show artist names"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:898
|
||||||
|
msgid "Show track extra buttons (buy link, download button etc)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:912
|
||||||
|
msgid "Open buy links in new window"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:920
|
||||||
|
msgid "Track & Track listing repeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:932
|
||||||
|
msgid "Repeat track listing enabled by default"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:946
|
||||||
|
msgid "Show track listing repeat toggle button"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:963
|
||||||
|
msgid "Show \"Powered by AudioIgniter\" link"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:967
|
||||||
|
msgid ""
|
||||||
|
"We've put a great deal of effort into building this plugin. If you feel like "
|
||||||
|
"it, let others know about it by enabling this option."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:987
|
||||||
|
msgid "Grab the shortcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:1021
|
||||||
|
msgid "Full Player"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:1026
|
||||||
|
msgid "Simple Player"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: audioigniter.php:1248
|
||||||
|
msgid "ID doesn't match a playlist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Plugin Name of the plugin/theme
|
||||||
|
msgid "AudioIgniter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Plugin URI of the plugin/theme
|
||||||
|
msgid "https://www.cssigniter.com/plugins/audioigniter/"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Description of the plugin/theme
|
||||||
|
msgid ""
|
||||||
|
"AudioIgniter lets you create music playlists and embed them in your "
|
||||||
|
"WordPress posts, pages or custom post types and serve your audio content in "
|
||||||
|
"style!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Author of the plugin/theme
|
||||||
|
msgid "The CSSIgniter Team"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Author URI of the plugin/theme
|
||||||
|
msgid "https://www.cssigniter.com"
|
||||||
|
msgstr ""
|
7
wp-content/plugins/audioigniter/player/.babelrc
Normal file
7
wp-content/plugins/audioigniter/player/.babelrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"es2015",
|
||||||
|
"react",
|
||||||
|
"stage-2"
|
||||||
|
]
|
||||||
|
}
|
18
wp-content/plugins/audioigniter/player/.editorconfig
Normal file
18
wp-content/plugins/audioigniter/player/.editorconfig
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.php]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.js]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
indent_size = 4
|
2
wp-content/plugins/audioigniter/player/.eslintignore
Normal file
2
wp-content/plugins/audioigniter/player/.eslintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
build/
|
||||||
|
node_modules/
|
38
wp-content/plugins/audioigniter/player/.eslintrc
Normal file
38
wp-content/plugins/audioigniter/player/.eslintrc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"airbnb",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
"prettier/react"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"import"
|
||||||
|
],
|
||||||
|
"globals": {
|
||||||
|
"aiStrings": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"arrow-body-style": 0,
|
||||||
|
"no-confusing-arrow": 0,
|
||||||
|
"global-require": 0,
|
||||||
|
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
|
||||||
|
"import/prefer-default-export": 0,
|
||||||
|
"react/jsx-filename-extension": 0,
|
||||||
|
"react/require-default-props": 0,
|
||||||
|
"react/forbid-prop-types": 0,
|
||||||
|
"react/default-props-match-prop-types": 0,
|
||||||
|
"react/prefer-stateless-function": 0,
|
||||||
|
"react/jsx-curly-spacing": [2, {
|
||||||
|
"when": "never",
|
||||||
|
"children": true
|
||||||
|
}],
|
||||||
|
"react/no-array-index-key": 0,
|
||||||
|
"jsx-a11y/anchor-is-valid": 0,
|
||||||
|
"jsx-a11y/no-static-element-interactions": 0,
|
||||||
|
"react/destructuring-assignment": 0,
|
||||||
|
"react/button-has-type": 0,
|
||||||
|
"jsx-a11y/label-has-for": 0
|
||||||
|
}
|
||||||
|
}
|
1
wp-content/plugins/audioigniter/player/.nvmrc
Normal file
1
wp-content/plugins/audioigniter/player/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
v8.12.0
|
13
wp-content/plugins/audioigniter/player/.prettierrc
Normal file
13
wp-content/plugins/audioigniter/player/.prettierrc
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"printWidth": 80,
|
||||||
|
"proseWrap": "never",
|
||||||
|
"requirePragma": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"useTabs": false
|
||||||
|
}
|
1
wp-content/plugins/audioigniter/player/build/app.js
Normal file
1
wp-content/plugins/audioigniter/player/build/app.js
Normal file
File diff suppressed because one or more lines are too long
100
wp-content/plugins/audioigniter/player/build/index.html
Normal file
100
wp-content/plugins/audioigniter/player/build/index.html
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<title>AudioIgniter</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding-bottom: 120px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link href="style.css" rel="stylesheet"></head>
|
||||||
|
<body>
|
||||||
|
<div
|
||||||
|
class="audioigniter-root"
|
||||||
|
data-player-type="full"
|
||||||
|
data-tracks-url="/dev-tracks.json"
|
||||||
|
data-display-active-cover="true"
|
||||||
|
data-display-tracklist-covers="true"
|
||||||
|
data-display-credits="true"
|
||||||
|
data-display-tracklist="true"
|
||||||
|
data-allow-tracklist-toggle="true"
|
||||||
|
data-allow-tracklist-loop="true"
|
||||||
|
data-allow-track-loop="true"
|
||||||
|
data-allow-playback-rate="true"
|
||||||
|
data-display-track-no="true"
|
||||||
|
data-display-artist-names="true"
|
||||||
|
data-display-buy-buttons="true"
|
||||||
|
data-buy-buttons-target="true"
|
||||||
|
data-volume="50"
|
||||||
|
data-cycle-tracks="true"
|
||||||
|
data-limit-tracklist-height="true"
|
||||||
|
data-tracklist-height="185"
|
||||||
|
data-reverse-track-order="false"
|
||||||
|
data-skip-amount="15"
|
||||||
|
data-max-width="600px"
|
||||||
|
data-initial-track="1"
|
||||||
|
data-stop-on-finish="false"
|
||||||
|
data-tracks-delay="5"
|
||||||
|
data-timer-countdown="false"
|
||||||
|
data-shuffle="true"
|
||||||
|
data-shuffle-default="false"
|
||||||
|
data-soundcloud-client-id=""
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="audioigniter-root"
|
||||||
|
data-player-type="simple"
|
||||||
|
data-tracks-url="/dev-tracks.json"
|
||||||
|
data-display-credits="true"
|
||||||
|
data-display-track-no="true"
|
||||||
|
data-allow-playback-rate="true"
|
||||||
|
data-display-artist-names="true"
|
||||||
|
data-display-buy-buttons="true"
|
||||||
|
data-buy-buttons-target="true"
|
||||||
|
data-allow-track-loop="true"
|
||||||
|
data-volume="50"
|
||||||
|
data-reverse-track-order="false"
|
||||||
|
data-max-width="600px"
|
||||||
|
data-initial-track="3"
|
||||||
|
data-stop-on-finish="false"
|
||||||
|
data-tracks-delay="0"
|
||||||
|
data-timer-countdown="false"
|
||||||
|
data-shuffle="true"
|
||||||
|
data-soundcloud-client-id=""
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="audioigniter-root"
|
||||||
|
data-player-type="global-footer"
|
||||||
|
data-tracks-url="/dev-tracks.json"
|
||||||
|
data-display-active-cover="true"
|
||||||
|
data-display-tracklist-covers="true"
|
||||||
|
data-display-credits="true"
|
||||||
|
data-display-tracklist="false"
|
||||||
|
data-allow-tracklist-toggle="true"
|
||||||
|
data-allow-tracklist-loop="true"
|
||||||
|
data-allow-track-loop="true"
|
||||||
|
data-display-track-no="true"
|
||||||
|
data-allow-playback-rate="true"
|
||||||
|
data-display-artist-names="true"
|
||||||
|
data-display-buy-buttons="true"
|
||||||
|
data-buy-buttons-target="true"
|
||||||
|
data-volume="50"
|
||||||
|
data-skip-amount="15"
|
||||||
|
data-cycle-tracks="false"
|
||||||
|
data-limit-tracklist-height="true"
|
||||||
|
data-tracklist-height="185"
|
||||||
|
data-reverse-track-order="false"
|
||||||
|
data-max-width="600px"
|
||||||
|
data-initial-track="1"
|
||||||
|
data-stop-on-finish="false"
|
||||||
|
data-tracks-delay="0"
|
||||||
|
data-timer-countdown="true"
|
||||||
|
data-shuffle="true"
|
||||||
|
data-soundcloud-client-id=""
|
||||||
|
></div>
|
||||||
|
<script type="text/javascript" src="app.js"></script><script type="text/javascript" src="style.js"></script></body>
|
||||||
|
</html>
|
1
wp-content/plugins/audioigniter/player/build/style.css
Normal file
1
wp-content/plugins/audioigniter/player/build/style.css
Normal file
File diff suppressed because one or more lines are too long
1
wp-content/plugins/audioigniter/player/build/style.js
Normal file
1
wp-content/plugins/audioigniter/player/build/style.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
!function(n){function r(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var t={};r.m=n,r.c=t,r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e})},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,r){return Object.prototype.hasOwnProperty.call(n,r)},r.p="",r(r.s=28)}({28:function(n,r){}});
|
64
wp-content/plugins/audioigniter/player/dev-tracks.json
Normal file
64
wp-content/plugins/audioigniter/player/dev-tracks.json
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Sunrise",
|
||||||
|
"subtitle": "Thoribass",
|
||||||
|
"audio": "https://www.cssigniter.com/assets/audioigniter/sunrise.mp3",
|
||||||
|
"buyUrl": "https://www.cssigniter.com",
|
||||||
|
"downloadUrl": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3",
|
||||||
|
"cover": "https:\/\/www.cssigniter.com\/preview\/audioigniter\/files\/2016\/08\/Thoribass-Sunrise.jpg",
|
||||||
|
"lyrics": "Here in my mind\nYou know you might find\nSomething that you\n\nYou thought you once knew\nBut now it's all gone\nAnd you know it's no fun\n\nYeah I know it's no fun\nOh I know it's no fun\nI'm free to be whatever I\nWhatever I choose\nAnd I'll sing the blues if I want\nI'm free to be whatever I\nWhatever I choose\nAnd I'll sing the blues if I want\nWhatever you do\nWhatever you say\nYeah I know it's alright"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Seriously long title lorem ipsum dolor sit amet, consectetur adipiscing elit. Non est enim.",
|
||||||
|
"subtitle": "The Fisherman",
|
||||||
|
"audio": "https://www.cssigniter.com/assets/audioigniter/sunrise.mp3",
|
||||||
|
"buyUrl": "https://www.cssigniter.com",
|
||||||
|
"downloadUrl": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3",
|
||||||
|
"cover": "https:\/\/www.cssigniter.com\/preview\/audioigniter\/files\/2016\/08\/The-Fisherman-Another-Day.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Remix Safety Guide",
|
||||||
|
"subtitle": "Rocavaco",
|
||||||
|
"audio": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/remix.mp3",
|
||||||
|
"buyUrl": "https://www.cssigniter.com",
|
||||||
|
"cover": "https:\/\/www.cssigniter.com\/preview\/audioigniter\/files\/2016\/08\/Rocavaco-Remix-Safety-Guide.jpg",
|
||||||
|
"lyrics": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. At eius hic illo natus vitae. Assumenda commodi eaque eos est eum excepturi fugiat provident, quidem saepe? Aut doloremque, unde? Delectus, dolorum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tomorrow",
|
||||||
|
"subtitle": "MegaEnx",
|
||||||
|
"audio": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/tomorrow.mp3",
|
||||||
|
"buyUrl": "",
|
||||||
|
"downloadUrl": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3",
|
||||||
|
"cover": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Is It Because I'm Black (David August Reconstruction)",
|
||||||
|
"subtitle": "Syl Johnson (SoundCloud)",
|
||||||
|
"audio": "https://soundcloud.com/enterofficial/maceo-plex-b2b-richie-hawtin-enterweek-11-sake-bar-space-ibiza-september-10th-2015",
|
||||||
|
"buyUrl": "",
|
||||||
|
"cover": "https://www.cssigniter.com/preview/audioigniter/files/2016/08/artworks-000103551140-ez6k4x-t500x500.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Deep House Radio",
|
||||||
|
"subtitle": "",
|
||||||
|
"audio": "https://deephouseradio.radioca.st/stream/1/",
|
||||||
|
"buyUrl": "https://www.cssigniter.com",
|
||||||
|
"cover": "https://www.cssigniter.com/preview/audioigniter/files/2016/08/Rocavaco-Remix-Safety-Guide.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Flash of Light",
|
||||||
|
"subtitle": "Kxmode",
|
||||||
|
"audio": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/flashlight.mp3",
|
||||||
|
"buyUrl": "https://www.cssigniter.com",
|
||||||
|
"cover": "https:\/\/www.cssigniter.com\/preview\/audioigniter\/files\/2016\/08\/Kxmode-Flash-of-Light.jpg",
|
||||||
|
"lyrics": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. At eius hic illo natus vitae. Assumenda commodi eaque eos est eum excepturi fugiat provident, quidem saepe? Aut doloremque, unde? Delectus, dolorum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "We Get Mental",
|
||||||
|
"subtitle": "BitBurner",
|
||||||
|
"audio": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/mental.mp3",
|
||||||
|
"buyUrl": "",
|
||||||
|
"cover": "https:\/\/www.cssigniter.com\/preview\/audioigniter\/files\/2016\/08\/BitBurner-We-Get-Mental.jpg"
|
||||||
|
}
|
||||||
|
]
|
62
wp-content/plugins/audioigniter/player/package.json
Normal file
62
wp-content/plugins/audioigniter/player/package.json
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"name": "audioigniter",
|
||||||
|
"version": "1.6.1",
|
||||||
|
"description": "React audio player",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack-dev-server",
|
||||||
|
"build": "rm -rf ./build && webpack",
|
||||||
|
"lint": "eslint ./src --ext .js --ext .jsx --cache || true",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"audio",
|
||||||
|
"audio player",
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"author": "vmasto",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^7.1.2",
|
||||||
|
"babel-core": "^6.25.0",
|
||||||
|
"babel-loader": "^7.1.1",
|
||||||
|
"babel-preset-es2015": "^6.24.1",
|
||||||
|
"babel-preset-react": "^6.24.1",
|
||||||
|
"babel-preset-react-hmre": "1.1.1",
|
||||||
|
"babel-preset-stage-1": "^6.24.1",
|
||||||
|
"babel-preset-stage-2": "^6.24.1",
|
||||||
|
"css-loader": "^0.28.4",
|
||||||
|
"eslint": "3.19.0",
|
||||||
|
"eslint-config-airbnb": "15.0.2",
|
||||||
|
"eslint-config-airbnb-base": "^11.2.0",
|
||||||
|
"eslint-config-prettier": "^4.1.0",
|
||||||
|
"eslint-plugin-import": "2.7.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "5.1.1",
|
||||||
|
"eslint-plugin-prettier": "^3.0.1",
|
||||||
|
"eslint-plugin-react": "7.1.0",
|
||||||
|
"extract-text-webpack-plugin": "^3.0.0",
|
||||||
|
"html-webpack-plugin": "^2.29.0",
|
||||||
|
"node-sass": "^4.5.3",
|
||||||
|
"postcss-loader": "^2.0.6",
|
||||||
|
"precss": "^2.0.0",
|
||||||
|
"prettier": "^1.16.4",
|
||||||
|
"sass-loader": "^6.0.6",
|
||||||
|
"style-loader": "^0.18.2",
|
||||||
|
"webpack": "^3.2.0",
|
||||||
|
"webpack-dev-server": "^2.5.1",
|
||||||
|
"webpack-merge": "^4.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"classnames": "2.3.0",
|
||||||
|
"es6-promise": "^4.1.1",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react": "^16.8.3",
|
||||||
|
"react-custom-scrollbars": "^4.1.2",
|
||||||
|
"react-dom": "^16.8.3",
|
||||||
|
"react-modal": "^3.8.1",
|
||||||
|
"react-sound": "^1.2.0",
|
||||||
|
"soundmanager2": "^2.97.20170602",
|
||||||
|
"sprintf-js": "1.1.1",
|
||||||
|
"whatwg-fetch": "0.11.1"
|
||||||
|
}
|
||||||
|
}
|
64
wp-content/plugins/audioigniter/player/src/App.js
Normal file
64
wp-content/plugins/audioigniter/player/src/App.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { Fragment, useState, createContext } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Player from './player/Player';
|
||||||
|
import SimplePlayer from './player/SimplePlayer';
|
||||||
|
import GlobalFooterPlayer from './player/GlobalFooterPlayer';
|
||||||
|
import TrackLyricsModal from './player/components/TrackLyricsModal';
|
||||||
|
|
||||||
|
export const AppContext = createContext();
|
||||||
|
|
||||||
|
const App = ({ type, ...props }) => {
|
||||||
|
const [modal, setModalState] = useState({
|
||||||
|
open: false,
|
||||||
|
track: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleLyricsModal = (open, track) =>
|
||||||
|
setModalState(prevState => ({
|
||||||
|
...prevState,
|
||||||
|
track,
|
||||||
|
open,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { track, open } = modal;
|
||||||
|
|
||||||
|
const PlayerActual = (() => {
|
||||||
|
if (type === 'simple') {
|
||||||
|
return SimplePlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'global-footer') {
|
||||||
|
return GlobalFooterPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Player;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<AppContext.Provider
|
||||||
|
value={{
|
||||||
|
toggleLyricsModal,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayerActual {...props} />
|
||||||
|
</AppContext.Provider>
|
||||||
|
|
||||||
|
{track && track.lyrics && (
|
||||||
|
<TrackLyricsModal
|
||||||
|
isOpen={open}
|
||||||
|
closeModal={() => toggleLyricsModal(false)}
|
||||||
|
>
|
||||||
|
{track && track.lyrics}
|
||||||
|
</TrackLyricsModal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
type: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
100
wp-content/plugins/audioigniter/player/src/index.ejs
Normal file
100
wp-content/plugins/audioigniter/player/src/index.ejs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding-bottom: 120px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div
|
||||||
|
class="audioigniter-root"
|
||||||
|
data-player-type="full"
|
||||||
|
data-tracks-url="/dev-tracks.json"
|
||||||
|
data-display-active-cover="true"
|
||||||
|
data-display-tracklist-covers="true"
|
||||||
|
data-display-credits="true"
|
||||||
|
data-display-tracklist="true"
|
||||||
|
data-allow-tracklist-toggle="true"
|
||||||
|
data-allow-tracklist-loop="true"
|
||||||
|
data-allow-track-loop="true"
|
||||||
|
data-allow-playback-rate="true"
|
||||||
|
data-display-track-no="true"
|
||||||
|
data-display-artist-names="true"
|
||||||
|
data-display-buy-buttons="true"
|
||||||
|
data-buy-buttons-target="true"
|
||||||
|
data-volume="50"
|
||||||
|
data-cycle-tracks="true"
|
||||||
|
data-limit-tracklist-height="true"
|
||||||
|
data-tracklist-height="185"
|
||||||
|
data-reverse-track-order="false"
|
||||||
|
data-skip-amount="15"
|
||||||
|
data-max-width="600px"
|
||||||
|
data-initial-track="1"
|
||||||
|
data-stop-on-finish="false"
|
||||||
|
data-tracks-delay="5"
|
||||||
|
data-timer-countdown="false"
|
||||||
|
data-shuffle="true"
|
||||||
|
data-shuffle-default="false"
|
||||||
|
data-soundcloud-client-id=""
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="audioigniter-root"
|
||||||
|
data-player-type="simple"
|
||||||
|
data-tracks-url="/dev-tracks.json"
|
||||||
|
data-display-credits="true"
|
||||||
|
data-display-track-no="true"
|
||||||
|
data-allow-playback-rate="true"
|
||||||
|
data-display-artist-names="true"
|
||||||
|
data-display-buy-buttons="true"
|
||||||
|
data-buy-buttons-target="true"
|
||||||
|
data-allow-track-loop="true"
|
||||||
|
data-volume="50"
|
||||||
|
data-reverse-track-order="false"
|
||||||
|
data-max-width="600px"
|
||||||
|
data-initial-track="3"
|
||||||
|
data-stop-on-finish="false"
|
||||||
|
data-tracks-delay="0"
|
||||||
|
data-timer-countdown="false"
|
||||||
|
data-shuffle="true"
|
||||||
|
data-soundcloud-client-id=""
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="audioigniter-root"
|
||||||
|
data-player-type="global-footer"
|
||||||
|
data-tracks-url="/dev-tracks.json"
|
||||||
|
data-display-active-cover="true"
|
||||||
|
data-display-tracklist-covers="true"
|
||||||
|
data-display-credits="true"
|
||||||
|
data-display-tracklist="false"
|
||||||
|
data-allow-tracklist-toggle="true"
|
||||||
|
data-allow-tracklist-loop="true"
|
||||||
|
data-allow-track-loop="true"
|
||||||
|
data-display-track-no="true"
|
||||||
|
data-allow-playback-rate="true"
|
||||||
|
data-display-artist-names="true"
|
||||||
|
data-display-buy-buttons="true"
|
||||||
|
data-buy-buttons-target="true"
|
||||||
|
data-volume="50"
|
||||||
|
data-skip-amount="15"
|
||||||
|
data-cycle-tracks="false"
|
||||||
|
data-limit-tracklist-height="true"
|
||||||
|
data-tracklist-height="185"
|
||||||
|
data-reverse-track-order="false"
|
||||||
|
data-max-width="600px"
|
||||||
|
data-initial-track="1"
|
||||||
|
data-stop-on-finish="false"
|
||||||
|
data-tracks-delay="0"
|
||||||
|
data-timer-countdown="true"
|
||||||
|
data-shuffle="true"
|
||||||
|
data-soundcloud-client-id=""
|
||||||
|
></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
97
wp-content/plugins/audioigniter/player/src/index.js
Normal file
97
wp-content/plugins/audioigniter/player/src/index.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import 'es6-promise/auto';
|
||||||
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
// Set up translatable strings here
|
||||||
|
// for development purposes only. The production build
|
||||||
|
// gets them from WordPress's injection
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
window.aiStrings = {
|
||||||
|
play_title: 'Play %s',
|
||||||
|
pause_title: 'Pause %s',
|
||||||
|
previous: 'Previous track',
|
||||||
|
next: 'Next track',
|
||||||
|
toggle_list_repeat: 'Toggle track listing repeat',
|
||||||
|
toggle_list_visible: 'Toggle track listing visibility',
|
||||||
|
toggle_track_repeat: 'Toggle track repeat',
|
||||||
|
buy_track: 'Buy this track',
|
||||||
|
download_track: 'Download this track',
|
||||||
|
volume_up: 'Volume Up',
|
||||||
|
volume_down: 'Volume Down',
|
||||||
|
open_track_lyrics: 'Open track lyrics',
|
||||||
|
set_playback_rate: 'Set playback rate',
|
||||||
|
skip_forward: 'Skip forward',
|
||||||
|
skip_backward: 'Skip backward',
|
||||||
|
shuffle: 'Shuffle',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodes = document.getElementsByClassName('audioigniter-root');
|
||||||
|
|
||||||
|
function renderApp(node) {
|
||||||
|
const type = node.getAttribute('data-player-type');
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
tracksUrl: node.getAttribute('data-tracks-url'),
|
||||||
|
displayTracklistCovers: JSON.parse(
|
||||||
|
node.getAttribute('data-display-tracklist-covers'),
|
||||||
|
),
|
||||||
|
displayActiveCover: JSON.parse(
|
||||||
|
node.getAttribute('data-display-active-cover'),
|
||||||
|
),
|
||||||
|
displayCredits: JSON.parse(node.getAttribute('data-display-credits')),
|
||||||
|
displayTracklist: JSON.parse(node.getAttribute('data-display-tracklist')),
|
||||||
|
allowTracklistToggle: JSON.parse(
|
||||||
|
node.getAttribute('data-allow-tracklist-toggle'),
|
||||||
|
),
|
||||||
|
allowPlaybackRate: JSON.parse(
|
||||||
|
node.getAttribute('data-allow-playback-rate'),
|
||||||
|
),
|
||||||
|
allowTracklistLoop: JSON.parse(
|
||||||
|
node.getAttribute('data-allow-tracklist-loop'),
|
||||||
|
),
|
||||||
|
allowTrackLoop: JSON.parse(node.getAttribute('data-allow-track-loop')),
|
||||||
|
displayTrackNo: JSON.parse(node.getAttribute('data-display-track-no')),
|
||||||
|
displayBuyButtons: JSON.parse(
|
||||||
|
node.getAttribute('data-display-buy-buttons'),
|
||||||
|
),
|
||||||
|
buyButtonsTarget: JSON.parse(node.getAttribute('data-buy-buttons-target')),
|
||||||
|
volume: parseInt(node.getAttribute('data-volume'), 10),
|
||||||
|
displayArtistNames: JSON.parse(
|
||||||
|
node.getAttribute('data-display-artist-names'),
|
||||||
|
),
|
||||||
|
cycleTracks: JSON.parse(node.getAttribute('data-cycle-tracks')),
|
||||||
|
limitTracklistHeight: JSON.parse(
|
||||||
|
node.getAttribute('data-limit-tracklist-height'),
|
||||||
|
),
|
||||||
|
tracklistHeight: parseInt(node.getAttribute('data-tracklist-height'), 10),
|
||||||
|
reverseTrackOrder: JSON.parse(
|
||||||
|
node.getAttribute('data-reverse-track-order'),
|
||||||
|
),
|
||||||
|
maxWidth: node.getAttribute('data-max-width'),
|
||||||
|
soundcloudClientId: node.getAttribute('data-soundcloud-client-id'),
|
||||||
|
skipAmount: parseInt(node.getAttribute('data-skip-amount'), 10),
|
||||||
|
initialTrack: parseInt(node.getAttribute('data-initial-track'), 10),
|
||||||
|
delayBetweenTracks: parseInt(node.getAttribute('data-tracks-delay'), 10),
|
||||||
|
stopOnTrackFinish: JSON.parse(node.getAttribute('data-stop-on-finish')),
|
||||||
|
defaultShuffle: JSON.parse(node.getAttribute('data-shuffle')),
|
||||||
|
shuffleEnabled: JSON.parse(node.getAttribute('data-shuffle')),
|
||||||
|
countdownTimerByDefault: JSON.parse(
|
||||||
|
node.getAttribute('data-timer-countdown'),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
render(<App type={type} {...props} />, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.slice.call(nodes).forEach(node => {
|
||||||
|
renderApp(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
window.__CI_AUDIOIGNITER_MANUAL_INIT__ = node => {
|
||||||
|
renderApp(node);
|
||||||
|
};
|
@ -0,0 +1,357 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Sound from 'react-sound';
|
||||||
|
import { sprintf } from 'sprintf-js';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import soundProvider from './soundProvider';
|
||||||
|
import Cover from './components/Cover';
|
||||||
|
import Button from './components/Button';
|
||||||
|
import ProgressBar from './components/ProgressBar';
|
||||||
|
import Time from './components/Time';
|
||||||
|
import VolumeControl from './components/VolumeControl';
|
||||||
|
import TracklistWrap from './components/TracklistWrap';
|
||||||
|
import {
|
||||||
|
PlayIcon,
|
||||||
|
PauseIcon,
|
||||||
|
NextIcon,
|
||||||
|
PreviousIcon,
|
||||||
|
PlaylistIcon,
|
||||||
|
RefreshIcon,
|
||||||
|
LyricsIcon,
|
||||||
|
} from './components/Icons';
|
||||||
|
import { AppContext } from '../App';
|
||||||
|
import typographyDisabled from '../utils/typography-disabled';
|
||||||
|
|
||||||
|
class GlobalFooterPlayer extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isTrackListOpen: this.props.displayTracklist,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toggleTracklist = this.toggleTracklist.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTracklist() {
|
||||||
|
this.setState(state => ({
|
||||||
|
isTrackListOpen: !state.isTrackListOpen,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isTrackListOpen } = this.state;
|
||||||
|
|
||||||
|
const {
|
||||||
|
tracks,
|
||||||
|
playStatus,
|
||||||
|
activeIndex,
|
||||||
|
volume,
|
||||||
|
position,
|
||||||
|
duration,
|
||||||
|
playbackRate,
|
||||||
|
|
||||||
|
currentTrack,
|
||||||
|
playTrack,
|
||||||
|
togglePlay,
|
||||||
|
nextTrack,
|
||||||
|
prevTrack,
|
||||||
|
setPosition,
|
||||||
|
setVolume,
|
||||||
|
toggleTracklistCycling,
|
||||||
|
cycleTracks,
|
||||||
|
setTrackCycling,
|
||||||
|
setPlaybackRate,
|
||||||
|
|
||||||
|
allowPlaybackRate,
|
||||||
|
allowTracklistToggle,
|
||||||
|
allowTracklistLoop,
|
||||||
|
allowTrackLoop,
|
||||||
|
reverseTrackOrder,
|
||||||
|
displayTrackNo,
|
||||||
|
displayTracklistCovers,
|
||||||
|
displayActiveCover,
|
||||||
|
limitTracklistHeight,
|
||||||
|
tracklistHeight,
|
||||||
|
displayBuyButtons,
|
||||||
|
buyButtonsTarget,
|
||||||
|
displayArtistNames,
|
||||||
|
repeatingTrackIndex,
|
||||||
|
skipAmount,
|
||||||
|
skipPosition,
|
||||||
|
countdownTimerByDefault,
|
||||||
|
buffering,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
'ai-wrap': true,
|
||||||
|
'ai-type-global-footer': true,
|
||||||
|
'ai-is-loading': !tracks.length,
|
||||||
|
'ai-with-typography': !typographyDisabled(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const audioControlClasses = classNames({
|
||||||
|
'ai-audio-control': true,
|
||||||
|
'ai-audio-playing': playStatus === Sound.status.PLAYING,
|
||||||
|
'ai-audio-loading': buffering,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref => (this.root = ref)} // eslint-disable-line no-return-assign
|
||||||
|
className={classes}
|
||||||
|
>
|
||||||
|
<div className="ai-control-wrap">
|
||||||
|
{displayActiveCover && (
|
||||||
|
<Cover
|
||||||
|
className="ai-thumb ai-control-wrap-thumb"
|
||||||
|
src={currentTrack.cover}
|
||||||
|
alt={currentTrack.title}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="ai-control-wrap-controls">
|
||||||
|
<ProgressBar
|
||||||
|
setPosition={setPosition}
|
||||||
|
duration={duration}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="ai-audio-controls-main">
|
||||||
|
<Button
|
||||||
|
onClick={togglePlay}
|
||||||
|
className={audioControlClasses}
|
||||||
|
ariaLabel={
|
||||||
|
playStatus === Sound.status.PLAYING
|
||||||
|
? sprintf(aiStrings.pause_title, currentTrack.title)
|
||||||
|
: sprintf(aiStrings.play_title, currentTrack.title)
|
||||||
|
}
|
||||||
|
ariaPressed={playStatus === Sound.status.PLAYING}
|
||||||
|
>
|
||||||
|
{playStatus === Sound.status.PLAYING ? (
|
||||||
|
<PauseIcon />
|
||||||
|
) : (
|
||||||
|
<PlayIcon />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span className="ai-control-spinner" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="ai-audio-controls-meta">
|
||||||
|
{tracks.length > 1 && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-tracklist-prev"
|
||||||
|
onClick={prevTrack}
|
||||||
|
ariaLabel={aiStrings.previous}
|
||||||
|
>
|
||||||
|
<PreviousIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{tracks.length > 1 && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-tracklist-next"
|
||||||
|
onClick={nextTrack}
|
||||||
|
ariaLabel={aiStrings.next}
|
||||||
|
>
|
||||||
|
<NextIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<VolumeControl
|
||||||
|
volume={volume}
|
||||||
|
// eslint-disable-next-line no-shadow
|
||||||
|
setVolume={setVolume}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{allowTracklistLoop && (
|
||||||
|
<Button
|
||||||
|
className={`ai-btn ai-btn-repeat ${cycleTracks &&
|
||||||
|
'ai-btn-active'}`}
|
||||||
|
onClick={toggleTracklistCycling}
|
||||||
|
ariaLabel={aiStrings.toggle_list_repeat}
|
||||||
|
>
|
||||||
|
<RefreshIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowPlaybackRate && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-btn-playback-rate"
|
||||||
|
onClick={setPlaybackRate}
|
||||||
|
ariaLabel={aiStrings.set_playback_rate}
|
||||||
|
>
|
||||||
|
<Fragment>×{playbackRate}</Fragment>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{skipAmount > 0 && (
|
||||||
|
<Fragment>
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-btn-skip-position"
|
||||||
|
onClick={() => skipPosition(-1)}
|
||||||
|
ariaLabel={aiStrings.skip_backward}
|
||||||
|
>
|
||||||
|
-{skipAmount}s
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-btn-skip-position"
|
||||||
|
onClick={() => skipPosition(1)}
|
||||||
|
ariaLabel={aiStrings.skip_forward}
|
||||||
|
>
|
||||||
|
+{skipAmount}s
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{currentTrack && currentTrack.lyrics && !isTrackListOpen && (
|
||||||
|
<AppContext.Consumer>
|
||||||
|
{({ toggleLyricsModal }) => (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-lyrics"
|
||||||
|
onClick={() => toggleLyricsModal(true, currentTrack)}
|
||||||
|
ariaLabel={aiStrings.open_track_lyrics}
|
||||||
|
title={aiStrings.open_track_lyrics}
|
||||||
|
>
|
||||||
|
<LyricsIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</AppContext.Consumer>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-track-info">
|
||||||
|
<p className="ai-track-title">
|
||||||
|
<span>{currentTrack.title}</span>
|
||||||
|
</p>
|
||||||
|
{(tracks.length === 0 || currentTrack.subtitle) &&
|
||||||
|
displayArtistNames && (
|
||||||
|
<p className="ai-track-subtitle">
|
||||||
|
<span>{currentTrack.subtitle}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-audio-controls-meta-right">
|
||||||
|
<Time
|
||||||
|
duration={duration}
|
||||||
|
position={position}
|
||||||
|
countdown={countdownTimerByDefault}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{allowTracklistToggle && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-tracklist-toggle"
|
||||||
|
onClick={this.toggleTracklist}
|
||||||
|
ariaLabel={aiStrings.toggle_list_visible}
|
||||||
|
>
|
||||||
|
<PlaylistIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`ai-tracklist-wrap ${
|
||||||
|
isTrackListOpen ? 'ai-tracklist-open' : ''
|
||||||
|
}`}
|
||||||
|
style={{ display: isTrackListOpen ? 'block' : 'none' }}
|
||||||
|
>
|
||||||
|
<TracklistWrap
|
||||||
|
className="ai-tracklist"
|
||||||
|
trackClassName="ai-track"
|
||||||
|
tracks={tracks}
|
||||||
|
activeTrackIndex={activeIndex}
|
||||||
|
isOpen={isTrackListOpen}
|
||||||
|
displayTrackNo={displayTrackNo}
|
||||||
|
displayCovers={displayTracklistCovers}
|
||||||
|
displayBuyButtons={displayBuyButtons}
|
||||||
|
buyButtonsTarget={buyButtonsTarget}
|
||||||
|
displayArtistNames={displayArtistNames}
|
||||||
|
reverseTrackOrder={reverseTrackOrder}
|
||||||
|
limitTracklistHeight={limitTracklistHeight}
|
||||||
|
tracklistHeight={tracklistHeight}
|
||||||
|
onTrackClick={playTrack}
|
||||||
|
onTrackLoop={allowTrackLoop ? setTrackCycling : undefined}
|
||||||
|
repeatingTrackIndex={repeatingTrackIndex}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalFooterPlayer.propTypes = {
|
||||||
|
tracks: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
playStatus: PropTypes.oneOf([
|
||||||
|
Sound.status.PLAYING,
|
||||||
|
Sound.status.PAUSED,
|
||||||
|
Sound.status.STOPPED,
|
||||||
|
]),
|
||||||
|
activeIndex: PropTypes.number,
|
||||||
|
volume: PropTypes.number,
|
||||||
|
position: PropTypes.number,
|
||||||
|
duration: PropTypes.number,
|
||||||
|
currentTrack: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
playTrack: PropTypes.func.isRequired,
|
||||||
|
togglePlay: PropTypes.func.isRequired,
|
||||||
|
nextTrack: PropTypes.func.isRequired,
|
||||||
|
prevTrack: PropTypes.func.isRequired,
|
||||||
|
setPosition: PropTypes.func.isRequired,
|
||||||
|
setVolume: PropTypes.func.isRequired,
|
||||||
|
toggleTracklistCycling: PropTypes.func.isRequired,
|
||||||
|
cycleTracks: PropTypes.bool.isRequired,
|
||||||
|
displayTracklist: PropTypes.bool,
|
||||||
|
allowTracklistToggle: PropTypes.bool,
|
||||||
|
allowTracklistLoop: PropTypes.bool,
|
||||||
|
reverseTrackOrder: PropTypes.bool,
|
||||||
|
displayTrackNo: PropTypes.bool,
|
||||||
|
displayActiveCover: PropTypes.bool,
|
||||||
|
displayTracklistCovers: PropTypes.bool,
|
||||||
|
limitTracklistHeight: PropTypes.bool,
|
||||||
|
tracklistHeight: PropTypes.number,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
setTrackCycling: PropTypes.func.isRequired,
|
||||||
|
repeatingTrackIndex: PropTypes.number,
|
||||||
|
allowTrackLoop: PropTypes.bool,
|
||||||
|
playbackRate: PropTypes.number,
|
||||||
|
setPlaybackRate: PropTypes.func,
|
||||||
|
skipAmount: PropTypes.number,
|
||||||
|
skipPosition: PropTypes.func.isRequired,
|
||||||
|
countdownTimerByDefault: PropTypes.bool,
|
||||||
|
allowPlaybackRate: PropTypes.bool,
|
||||||
|
buffering: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default soundProvider(GlobalFooterPlayer, {
|
||||||
|
onFinishedPlaying(props) {
|
||||||
|
const {
|
||||||
|
repeatingTrackIndex,
|
||||||
|
cycleTracks,
|
||||||
|
nextTrack,
|
||||||
|
activeIndex,
|
||||||
|
playTrack,
|
||||||
|
trackQueue,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
if (repeatingTrackIndex != null) {
|
||||||
|
playTrack(repeatingTrackIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycleTracks) {
|
||||||
|
nextTrack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if not the last track
|
||||||
|
if (activeIndex !== trackQueue[trackQueue.length - 1]) {
|
||||||
|
nextTrack();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
403
wp-content/plugins/audioigniter/player/src/player/Player.js
Normal file
403
wp-content/plugins/audioigniter/player/src/player/Player.js
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Sound from 'react-sound';
|
||||||
|
import { sprintf } from 'sprintf-js';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import TracklistWrap from './components/TracklistWrap';
|
||||||
|
import ProgressBar from './components/ProgressBar';
|
||||||
|
import Time from './components/Time';
|
||||||
|
import VolumeControl from './components/VolumeControl';
|
||||||
|
import Button from './components/Button';
|
||||||
|
import Cover from './components/Cover';
|
||||||
|
import {
|
||||||
|
PlayIcon,
|
||||||
|
PauseIcon,
|
||||||
|
NextIcon,
|
||||||
|
PreviousIcon,
|
||||||
|
PlaylistIcon,
|
||||||
|
RefreshIcon,
|
||||||
|
LyricsIcon,
|
||||||
|
ShuffleIcon,
|
||||||
|
} from './components/Icons';
|
||||||
|
import soundProvider from './soundProvider';
|
||||||
|
import { AppContext } from '../App';
|
||||||
|
import typographyDisabled from '../utils/typography-disabled';
|
||||||
|
|
||||||
|
class Player extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isTrackListOpen: this.props.displayTracklist,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toggleTracklist = this.toggleTracklist.bind(this);
|
||||||
|
this.isNarrowContext = this.isNarrowContext.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
isNarrowContext() {
|
||||||
|
return this.root && this.root.offsetWidth < 480 && window.innerWidth > 480;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTracklist() {
|
||||||
|
this.setState(state => ({
|
||||||
|
isTrackListOpen: !state.isTrackListOpen,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isTrackListOpen } = this.state;
|
||||||
|
|
||||||
|
const {
|
||||||
|
tracks,
|
||||||
|
playStatus,
|
||||||
|
activeIndex,
|
||||||
|
volume,
|
||||||
|
position,
|
||||||
|
duration,
|
||||||
|
playbackRate,
|
||||||
|
shuffle,
|
||||||
|
shuffleEnabled,
|
||||||
|
|
||||||
|
currentTrack,
|
||||||
|
playTrack,
|
||||||
|
togglePlay,
|
||||||
|
nextTrack,
|
||||||
|
prevTrack,
|
||||||
|
setPosition,
|
||||||
|
setVolume,
|
||||||
|
setPlaybackRate,
|
||||||
|
toggleTracklistCycling,
|
||||||
|
cycleTracks,
|
||||||
|
toggleShuffle,
|
||||||
|
|
||||||
|
allowTracklistToggle,
|
||||||
|
allowTracklistLoop,
|
||||||
|
allowPlaybackRate,
|
||||||
|
allowTrackLoop,
|
||||||
|
setTrackCycling,
|
||||||
|
reverseTrackOrder,
|
||||||
|
displayTrackNo,
|
||||||
|
displayTracklistCovers,
|
||||||
|
displayActiveCover,
|
||||||
|
displayCredits,
|
||||||
|
limitTracklistHeight,
|
||||||
|
tracklistHeight,
|
||||||
|
displayBuyButtons,
|
||||||
|
buyButtonsTarget,
|
||||||
|
displayArtistNames,
|
||||||
|
maxWidth,
|
||||||
|
repeatingTrackIndex,
|
||||||
|
skipAmount,
|
||||||
|
skipPosition,
|
||||||
|
countdownTimerByDefault,
|
||||||
|
buffering,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
'ai-wrap': true,
|
||||||
|
'ai-type-full': true,
|
||||||
|
'ai-is-loading': !tracks.length,
|
||||||
|
'ai-narrow': this.isNarrowContext(),
|
||||||
|
'ai-with-typography': !typographyDisabled(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const audioControlClasses = classNames({
|
||||||
|
'ai-audio-control': true,
|
||||||
|
'ai-audio-playing': playStatus === Sound.status.PLAYING,
|
||||||
|
'ai-audio-loading': buffering,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref => (this.root = ref)} // eslint-disable-line no-return-assign
|
||||||
|
className={classes}
|
||||||
|
style={{ maxWidth }}
|
||||||
|
>
|
||||||
|
<div className="ai-control-wrap">
|
||||||
|
{displayActiveCover && (
|
||||||
|
<Cover
|
||||||
|
className="ai-thumb ai-control-wrap-thumb"
|
||||||
|
src={currentTrack.cover}
|
||||||
|
alt={currentTrack.title}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="ai-control-wrap-controls">
|
||||||
|
<div className="ai-audio-controls-main">
|
||||||
|
<Button
|
||||||
|
onClick={togglePlay}
|
||||||
|
className={audioControlClasses}
|
||||||
|
ariaLabel={
|
||||||
|
playStatus === Sound.status.PLAYING
|
||||||
|
? sprintf(aiStrings.pause_title, currentTrack.title)
|
||||||
|
: sprintf(aiStrings.play_title, currentTrack.title)
|
||||||
|
}
|
||||||
|
ariaPressed={playStatus === Sound.status.PLAYING}
|
||||||
|
>
|
||||||
|
{playStatus === Sound.status.PLAYING ? (
|
||||||
|
<PauseIcon />
|
||||||
|
) : (
|
||||||
|
<PlayIcon />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span className="ai-control-spinner" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="ai-track-info">
|
||||||
|
<p className="ai-track-title">
|
||||||
|
<span>{currentTrack.title}</span>
|
||||||
|
</p>
|
||||||
|
{(tracks.length === 0 || currentTrack.subtitle) &&
|
||||||
|
displayArtistNames && (
|
||||||
|
<p className="ai-track-subtitle">
|
||||||
|
<span>{currentTrack.subtitle}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-audio-controls-progress">
|
||||||
|
<ProgressBar
|
||||||
|
setPosition={setPosition}
|
||||||
|
duration={duration}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Time
|
||||||
|
duration={duration}
|
||||||
|
position={position}
|
||||||
|
countdown={countdownTimerByDefault}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-audio-controls-meta">
|
||||||
|
{tracks.length > 1 && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-tracklist-prev"
|
||||||
|
onClick={prevTrack}
|
||||||
|
ariaLabel={aiStrings.previous}
|
||||||
|
title={aiStrings.previous}
|
||||||
|
>
|
||||||
|
<PreviousIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{tracks.length > 1 && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-tracklist-next"
|
||||||
|
onClick={nextTrack}
|
||||||
|
ariaLabel={aiStrings.next}
|
||||||
|
title={aiStrings.next}
|
||||||
|
>
|
||||||
|
<NextIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<VolumeControl
|
||||||
|
volume={volume}
|
||||||
|
// eslint-disable-next-line no-shadow
|
||||||
|
setVolume={setVolume}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{allowTracklistLoop && (
|
||||||
|
<Button
|
||||||
|
className={`ai-btn ai-btn-repeat ${cycleTracks &&
|
||||||
|
'ai-btn-active'}`}
|
||||||
|
onClick={toggleTracklistCycling}
|
||||||
|
ariaLabel={aiStrings.toggle_list_repeat}
|
||||||
|
>
|
||||||
|
<RefreshIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{shuffleEnabled && (
|
||||||
|
<Button
|
||||||
|
className={`ai-btn ai-btn-shuffle ${shuffle &&
|
||||||
|
'ai-btn-active'}`}
|
||||||
|
onClick={toggleShuffle}
|
||||||
|
ariaLabel={aiStrings.shuffle}
|
||||||
|
>
|
||||||
|
<ShuffleIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowPlaybackRate && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-btn-playback-rate"
|
||||||
|
onClick={setPlaybackRate}
|
||||||
|
ariaLabel={aiStrings.set_playback_rate}
|
||||||
|
>
|
||||||
|
<Fragment>×{playbackRate}</Fragment>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{skipAmount > 0 && (
|
||||||
|
<Fragment>
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-btn-skip-position"
|
||||||
|
onClick={() => skipPosition(-1)}
|
||||||
|
ariaLabel={aiStrings.skip_backward}
|
||||||
|
>
|
||||||
|
-{skipAmount}s
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-btn-skip-position"
|
||||||
|
onClick={() => skipPosition(1)}
|
||||||
|
ariaLabel={aiStrings.skip_forward}
|
||||||
|
>
|
||||||
|
+{skipAmount}s
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{currentTrack && currentTrack.lyrics && !isTrackListOpen && (
|
||||||
|
<AppContext.Consumer>
|
||||||
|
{({ toggleLyricsModal }) => (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-lyrics"
|
||||||
|
onClick={() => toggleLyricsModal(true, currentTrack)}
|
||||||
|
ariaLabel={aiStrings.open_track_lyrics}
|
||||||
|
title={aiStrings.open_track_lyrics}
|
||||||
|
>
|
||||||
|
<LyricsIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</AppContext.Consumer>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowTracklistToggle && (
|
||||||
|
<Button
|
||||||
|
className="ai-btn ai-tracklist-toggle"
|
||||||
|
onClick={this.toggleTracklist}
|
||||||
|
ariaLabel={aiStrings.toggle_list_visible}
|
||||||
|
ariaExpanded={isTrackListOpen}
|
||||||
|
>
|
||||||
|
<PlaylistIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`ai-tracklist-wrap ${
|
||||||
|
isTrackListOpen ? 'ai-tracklist-open' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<TracklistWrap
|
||||||
|
className="ai-tracklist"
|
||||||
|
trackClassName="ai-track"
|
||||||
|
tracks={tracks}
|
||||||
|
activeTrackIndex={activeIndex}
|
||||||
|
isOpen={isTrackListOpen}
|
||||||
|
displayTrackNo={displayTrackNo}
|
||||||
|
displayCovers={displayTracklistCovers}
|
||||||
|
displayBuyButtons={displayBuyButtons}
|
||||||
|
buyButtonsTarget={buyButtonsTarget}
|
||||||
|
displayArtistNames={displayArtistNames}
|
||||||
|
reverseTrackOrder={reverseTrackOrder}
|
||||||
|
limitTracklistHeight={limitTracklistHeight}
|
||||||
|
tracklistHeight={tracklistHeight}
|
||||||
|
onTrackClick={playTrack}
|
||||||
|
onTrackLoop={allowTrackLoop ? setTrackCycling : undefined}
|
||||||
|
repeatingTrackIndex={repeatingTrackIndex}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{displayCredits && (
|
||||||
|
<div className="ai-footer">
|
||||||
|
<p>
|
||||||
|
Powered by{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.cssigniter.com/plugins/audioigniter?utm_source=player&utm_medium=link&utm_content=audioigniter&utm_campaign=footer-link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
AudioIgniter
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.propTypes = {
|
||||||
|
tracks: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
playStatus: PropTypes.oneOf([
|
||||||
|
Sound.status.PLAYING,
|
||||||
|
Sound.status.PAUSED,
|
||||||
|
Sound.status.STOPPED,
|
||||||
|
]),
|
||||||
|
activeIndex: PropTypes.number,
|
||||||
|
volume: PropTypes.number,
|
||||||
|
position: PropTypes.number,
|
||||||
|
duration: PropTypes.number,
|
||||||
|
currentTrack: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
playTrack: PropTypes.func.isRequired,
|
||||||
|
togglePlay: PropTypes.func.isRequired,
|
||||||
|
nextTrack: PropTypes.func.isRequired,
|
||||||
|
prevTrack: PropTypes.func.isRequired,
|
||||||
|
setPosition: PropTypes.func.isRequired,
|
||||||
|
setVolume: PropTypes.func.isRequired,
|
||||||
|
toggleTracklistCycling: PropTypes.func.isRequired,
|
||||||
|
setTrackCycling: PropTypes.func.isRequired,
|
||||||
|
cycleTracks: PropTypes.bool.isRequired,
|
||||||
|
displayTracklist: PropTypes.bool,
|
||||||
|
allowTracklistToggle: PropTypes.bool,
|
||||||
|
allowTracklistLoop: PropTypes.bool,
|
||||||
|
allowTrackLoop: PropTypes.bool,
|
||||||
|
reverseTrackOrder: PropTypes.bool,
|
||||||
|
displayTrackNo: PropTypes.bool,
|
||||||
|
displayCredits: PropTypes.bool,
|
||||||
|
displayActiveCover: PropTypes.bool,
|
||||||
|
displayTracklistCovers: PropTypes.bool,
|
||||||
|
limitTracklistHeight: PropTypes.bool,
|
||||||
|
tracklistHeight: PropTypes.number,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
maxWidth: PropTypes.string,
|
||||||
|
repeatingTrackIndex: PropTypes.number,
|
||||||
|
playbackRate: PropTypes.number,
|
||||||
|
setPlaybackRate: PropTypes.func,
|
||||||
|
skipAmount: PropTypes.number,
|
||||||
|
skipPosition: PropTypes.func.isRequired,
|
||||||
|
countdownTimerByDefault: PropTypes.bool,
|
||||||
|
allowPlaybackRate: PropTypes.bool,
|
||||||
|
buffering: PropTypes.bool,
|
||||||
|
shuffleEnabled: PropTypes.bool,
|
||||||
|
shuffle: PropTypes.bool,
|
||||||
|
toggleShuffle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default soundProvider(Player, {
|
||||||
|
onFinishedPlaying(props) {
|
||||||
|
const {
|
||||||
|
repeatingTrackIndex,
|
||||||
|
cycleTracks,
|
||||||
|
nextTrack,
|
||||||
|
activeIndex,
|
||||||
|
playTrack,
|
||||||
|
trackQueue,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
if (repeatingTrackIndex != null) {
|
||||||
|
playTrack(repeatingTrackIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycleTracks) {
|
||||||
|
nextTrack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if not the last track
|
||||||
|
if (activeIndex !== trackQueue[trackQueue.length - 1]) {
|
||||||
|
nextTrack();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
@ -0,0 +1,124 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Sound from 'react-sound';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import soundProvider from './soundProvider';
|
||||||
|
import Tracklist from './components/Tracklist';
|
||||||
|
import typographyDisabled from '../utils/typography-disabled';
|
||||||
|
|
||||||
|
const SimplePlayer = props => {
|
||||||
|
const { playStatus } = props;
|
||||||
|
const activeIndex =
|
||||||
|
playStatus === Sound.status.PLAYING || playStatus === Sound.status.PAUSED
|
||||||
|
? props.activeIndex
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
'ai-wrap': true,
|
||||||
|
'ai-type-simple': true,
|
||||||
|
'ai-with-typography': !typographyDisabled(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes} style={{ maxWidth: props.maxWidth }}>
|
||||||
|
<div className="ai-tracklist ai-tracklist-open">
|
||||||
|
<Tracklist
|
||||||
|
tracks={props.tracks}
|
||||||
|
playStatus={props.playStatus}
|
||||||
|
activeTrackIndex={activeIndex}
|
||||||
|
onTrackClick={props.togglePlay}
|
||||||
|
setPosition={props.setPosition}
|
||||||
|
duration={props.duration}
|
||||||
|
position={props.position}
|
||||||
|
playbackRate={props.playbackRate}
|
||||||
|
className="ai-tracklist"
|
||||||
|
trackClassName="ai-track"
|
||||||
|
reverseTrackOrder={props.reverseTrackOrder}
|
||||||
|
displayTrackNo={props.displayTrackNo}
|
||||||
|
displayBuyButtons={props.displayBuyButtons}
|
||||||
|
buyButtonsTarget={props.buyButtonsTarget}
|
||||||
|
displayArtistNames={props.displayArtistNames}
|
||||||
|
standaloneTracks
|
||||||
|
onTrackLoop={props.allowTrackLoop ? props.setTrackCycling : undefined}
|
||||||
|
repeatingTrackIndex={props.repeatingTrackIndex}
|
||||||
|
setPlaybackRate={props.setPlaybackRate}
|
||||||
|
allowPlaybackRate={props.allowPlaybackRate}
|
||||||
|
buffering={props.buffering}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{props.displayCredits && (
|
||||||
|
<div className="ai-footer">
|
||||||
|
<p>
|
||||||
|
Powered by{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.cssigniter.com/plugins/audioigniter?utm_source=player&utm_medium=link&utm_content=audioigniter&utm_campaign=footer-link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
AudioIgniter
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SimplePlayer.propTypes = {
|
||||||
|
tracks: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
playStatus: PropTypes.oneOf([
|
||||||
|
Sound.status.PLAYING,
|
||||||
|
Sound.status.PAUSED,
|
||||||
|
Sound.status.STOPPED,
|
||||||
|
]),
|
||||||
|
activeIndex: PropTypes.number,
|
||||||
|
position: PropTypes.number,
|
||||||
|
duration: PropTypes.number,
|
||||||
|
setPosition: PropTypes.func.isRequired,
|
||||||
|
togglePlay: PropTypes.func.isRequired,
|
||||||
|
setTrackCycling: PropTypes.func.isRequired,
|
||||||
|
allowTrackLoop: PropTypes.bool,
|
||||||
|
|
||||||
|
maxWidth: PropTypes.string,
|
||||||
|
reverseTrackOrder: PropTypes.bool,
|
||||||
|
displayTrackNo: PropTypes.bool,
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
displayCredits: PropTypes.bool,
|
||||||
|
repeatingTrackIndex: PropTypes.number,
|
||||||
|
playbackRate: PropTypes.number,
|
||||||
|
setPlaybackRate: PropTypes.func,
|
||||||
|
allowPlaybackRate: PropTypes.bool,
|
||||||
|
buffering: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default soundProvider(SimplePlayer, {
|
||||||
|
onFinishedPlaying(props) {
|
||||||
|
const {
|
||||||
|
repeatingTrackIndex,
|
||||||
|
cycleTracks,
|
||||||
|
nextTrack,
|
||||||
|
activeIndex,
|
||||||
|
playTrack,
|
||||||
|
trackQueue,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
if (repeatingTrackIndex != null) {
|
||||||
|
playTrack(repeatingTrackIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycleTracks) {
|
||||||
|
nextTrack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if not the last track
|
||||||
|
if (activeIndex !== trackQueue[trackQueue.length - 1]) {
|
||||||
|
nextTrack();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const Button = ({
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
|
ariaLabel,
|
||||||
|
ariaPressed,
|
||||||
|
ariaExpanded,
|
||||||
|
ariaControls,
|
||||||
|
}) => (
|
||||||
|
<button
|
||||||
|
className={className}
|
||||||
|
onClick={onClick}
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
aria-pressed={ariaPressed}
|
||||||
|
aria-expanded={ariaExpanded}
|
||||||
|
aria-controls={ariaControls}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
Button.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
children: PropTypes.node,
|
||||||
|
ariaLabel: PropTypes.string,
|
||||||
|
ariaPressed: PropTypes.bool,
|
||||||
|
ariaExpanded: PropTypes.bool,
|
||||||
|
ariaControls: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { MusicNoteIcon } from './Icons';
|
||||||
|
|
||||||
|
const Cover = ({ className, title, src, onClick }) => (
|
||||||
|
<div
|
||||||
|
className={className + (src ? '' : ' ai-track-no-thumb')}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{src ? <img src={src} alt={title || ''} /> : <MusicNoteIcon />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
Cover.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
src: PropTypes.string,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Cover;
|
@ -0,0 +1,105 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const PlayIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 24">
|
||||||
|
<path d="M18 12c0 .712-.37 1.355-.99 1.72L3.159 23.625C2.757 23.889 2.382 24 2 24c-1.103 0-2-.897-2-2V2C0 .897.897 0 2 0c.385 0 .76.111 1.085.323l13.962 9.981c.583.34.953.983.953 1.695z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PauseIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M9 2v20c0 1.103-.897 2-2 2H2c-1.103 0-2-.897-2-2V2C0 .897.897 0 2 0h5c1.103 0 2 .897 2 2zm13-2h-5c-1.103 0-2 .897-2 2v20c0 1.103.897 2 2 2h5c1.103 0 2-.897 2-2V2c0-1.103-.897-2-2-2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NextIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M24 1.999v19.989c0 1.102-.897 1.999-2 1.999h-5c-1.103 0-2-.897-2-1.999v-6.837L3.16 23.612C1.597 24.635 0 23.472 0 21.988V1.999C0 .897.897 0 2 0c.384 0 .76.111 1.085.322L15 8.837V1.999C15 .897 15.897 0 17 0h5c1.103 0 2 .897 2 1.999z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PreviousIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M24 2.014v19.987C24 23.103 23.103 24 22 24c-.385 0-.76-.111-1.085-.323L9 15.164v6.838c0 1.102-.897 1.999-2 1.999H2c-1.103 0-2-.897-2-1.999V2.015C0 .913.897.016 2 .016h5c1.103 0 2 .897 2 1.999v6.837L20.841.391C22.41-.636 24 .533 24 2.016z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PlaylistIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M.871 5h10.758c.488 0 .871-.439.871-1s-.383-1-.871-1H.871C.383 3 0 3.439 0 4s.383 1 .871 1zM.871 10.25h10.758c.488 0 .871-.439.871-1s-.383-1-.871-1H.871c-.488 0-.871.439-.871 1s.383 1 .871 1zM23.595 3.129l-.002-.001c-.254-.156-.574-.17-.833-.036l-7.449 3.756c-.291.148-.472.442-.472.77v8.259c-.5-.234-1.055-.356-1.626-.356-1.841 0-3.339 1.229-3.339 2.74s1.498 2.74 3.339 2.74 3.338-1.229 3.338-2.74V8.15l5.736-2.893v8.116c-.5-.233-1.056-.355-1.627-.355-1.841 0-3.338 1.229-3.338 2.739s1.497 2.74 3.338 2.74 3.339-1.229 3.339-2.74V3.862c0-.3-.151-.574-.405-.733zM8.129 13.5H.871c-.488 0-.871.439-.871 1s.383 1 .871 1h7.258c.488 0 .871-.439.871-1s-.383-1-.871-1z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VolumeUpIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M24 11v2c0 1.103-.897 2-2 2h-7v7c0 1.103-.897 2-2 2h-2c-1.103 0-2-.897-2-2v-7H2c-1.103 0-2-.897-2-2v-2c0-1.103.897-2 2-2h7V2c0-1.103.897-2 2-2h2c1.103 0 2 .897 2 2v7h7c1.103 0 2 .897 2 2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VolumeDownIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 24">
|
||||||
|
<path d="M24 11v2c0 1.103-.897 2-2 2H2c-1.103 0-2-.897-2-2v-2c0-1.103.897-2 2-2h20c1.103 0 2 .897 2 2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MusicNoteIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 24">
|
||||||
|
<path d="M18 2v16c0 1.654-1.794 3-4 3s-4-1.346-4-3 1.794-3 4-3V4.5L8 6.374V21c0 1.654-1.794 3-4 3s-4-1.346-4-3 1.794-3 4-3V5c0-.966.691-1.793 1.645-1.966L15.238.157c.204-.097.481-.157.763-.157 1.103 0 2 .897 2 2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CartIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M8.707 15h9.898c1.042 0 1.985-.657 2.346-1.636l2.94-7.979c.072-.196.109-.402.109-.616 0-.976-.794-1.77-1.77-1.77H5.734l-.339-1.188C5.09.744 4.101-.001 2.991-.001H.5c-.276 0-.5.224-.5.5s.224.5.5.5h2.491c.666 0 1.259.447 1.442 1.088l3.505 12.267-2.379 2.379c-.361.36-.56.841-.56 1.356 0 1.054.857 1.91 1.91 1.91h15.59c.276 0 .5-.224.5-.5s-.224-.5-.5-.5H6.909c-.502 0-.91-.408-.91-.916 0-.243.095-.472.267-.644l2.44-2.44zM18 12h-7.5c-.276 0-.5-.224-.5-.5s.224-.5.5-.5H18c.276 0 .5.224.5.5s-.224.5-.5.5zm.5-2.5H10c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h8.5c.276 0 .5.224.5.5s-.224.5-.5.5zM9.5 6H20c.276 0 .5.224.5.5s-.224.5-.5.5H9.5c-.276 0-.5-.224-.5-.5s.224-.5.5-.5zM21 20c1.103 0 2 .897 2 2s-.897 2-2 2-2-.897-2-2 .897-2 2-2zM8 20c1.103 0 2 .897 2 2s-.897 2-2 2-2-.897-2-2 .897-2 2-2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RefreshIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M24 12c0 2.756-2.243 4.999-5 4.999-.004 0-.02.001-.047.001-.295 0-1.919-.082-3.953-1.398v.397c0 .553-.447 1-1 1s-1-.447-1-1v-2.5c0-.553.447-1 1-1h2.5c.553 0 1 .447 1 1 0 .403-.241.745-.584.903 1.193.589 2.011.604 2.055.597 1.683 0 3.028-1.345 3.028-3s-1.346-3-3-3c-2.151 0-4.213 1.832-6.396 3.772-2.338 2.078-4.756 4.227-7.604 4.227-2.757 0-5-2.243-5-4.999S2.242 7 4.999 7c.046-.002 1.777-.044 4 1.394V8c0-.553.447-1 1-1s1 .447 1 1v2.5c0 .553-.447 1-1 1h-2.5c-.553 0-1-.447-1-1 0-.403.241-.746.585-.904-1.186-.587-1.997-.6-2.056-.596C3.345 9 2 10.346 2 12s1.346 3 3 3c2.089 0 4.122-1.807 6.275-3.722C13.641 9.176 16.087 7.001 19 7.001c2.757 0 5 2.243 5 4.999z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DownloadIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M24 15c0 2.757-2.243 5-5 5h-.183c-.177 0-.333-.092-.422-.23-.05-.078-.078-.17-.078-.269 0-.078.018-.153.05-.219.419-.882.632-1.819.632-2.782 0-3.584-2.916-6.5-6.5-6.5s-6.5 2.916-6.5 6.5c0 .923.196 1.823.583 2.676.074.087.119.2.119.324 0 .276-.224.5-.5.5-.005.001-.013 0-.02 0h-.183c-3.309 0-6-2.691-6-6 0-2.158 1.143-4.121 3.003-5.193C3.104 5.036 6.203 2 9.998 2c2.759 0 5.205 1.58 6.35 4.062.227-.042.439-.063.65-.063 2.206 0 4 1.794 4 4 0 .142-.008.283-.024.428 1.825.785 3.024 2.572 3.024 4.572zm-6 1.5c0 3.032-2.468 5.5-5.5 5.5S7 19.532 7 16.5 9.468 11 12.5 11s5.5 2.468 5.5 5.5zm-3.146.646c-.195-.195-.512-.195-.707 0l-1.146 1.146v-4.793c0-.276-.224-.5-.5-.5s-.5.224-.5.5v4.793l-1.146-1.146c-.195-.195-.512-.195-.707 0s-.195.512 0 .707l2 2c.046.046.1.083.161.108.059.025.124.038.192.038.065 0 .129-.013.19-.038h.002c.002-.001.003-.003.005-.004.057-.024.111-.058.157-.105l2-2c.195-.195.195-.512 0-.707z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LyricsIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M0 4.5C0 3.673.673 3 1.5 3h21c.827 0 1.5.673 1.5 1.5S23.327 6 22.5 6h-21C.673 6 0 5.327 0 4.5zM1.5 11h15c.827 0 1.5-.673 1.5-1.5S17.327 8 16.5 8h-15C.673 8 0 8.673 0 9.5S.673 11 1.5 11zm15 7h-15c-.827 0-1.5.673-1.5 1.5S.673 21 1.5 21h15c.827 0 1.5-.673 1.5-1.5s-.673-1.5-1.5-1.5zm6-5h-21c-.827 0-1.5.673-1.5 1.5S.673 16 1.5 16h21c.827 0 1.5-.673 1.5-1.5s-.673-1.5-1.5-1.5z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ShuffleIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M23.927 16.827c.098.23.098.504-.004.743-.044.111-.119.223-.212.314l-2.876 2.833c-.184.182-.428.282-.688.282s-.506-.101-.69-.283c-.187-.183-.289-.428-.289-.689s.103-.506.29-.69l1.188-1.171h-.86c-1.881 0-3.649-.722-4.979-2.034l-2.14-2.107c-.187-.185-.289-.43-.289-.69 0-.176.062-.336.149-.484l-2.372 2.337c-1.329 1.312-3.098 2.034-4.979 2.034H.98c-.54 0-.979-.436-.979-.972s.438-.972.979-.972h4.196c1.36 0 2.639-.522 3.599-1.469l2.354-2.319c-.148.086-.308.146-.484.146-.26 0-.505-.1-.689-.282l-1.179-1.163c-.962-.947-2.24-1.469-3.601-1.469H.98c-.54 0-.979-.436-.979-.972s.438-.972.979-.972h4.196c1.88 0 3.648.722 4.979 2.033l1.179 1.163c.188.184.29.429.29.69 0 .177-.063.339-.152.487l3.333-3.284c1.33-1.312 3.099-2.034 4.979-2.034h.86l-1.188-1.171c-.188-.184-.29-.429-.29-.69s.103-.506.29-.69c.379-.375.998-.375 1.379.001l2.874 2.833c.096.094.168.202.217.323.098.231.098.505-.004.743-.044.111-.116.219-.21.312l-2.878 2.835c-.363.363-1.013.365-1.38-.001-.186-.182-.288-.428-.288-.689s.104-.506.29-.69l1.188-1.17h-.86c-1.36 0-2.639.521-3.601 1.469l-3.313 3.265c.374-.215.855-.181 1.174.134l2.139 2.108c.963.947 2.241 1.469 3.602 1.469h.86l-1.188-1.171c-.188-.184-.29-.429-.29-.69s.104-.506.29-.69c.379-.374.998-.375 1.379.001l2.877 2.834c.094.094.166.202.214.321z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export default class ProgressBar extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick(event) {
|
||||||
|
const { duration, setPosition } = this.props;
|
||||||
|
|
||||||
|
if (setPosition == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsetX =
|
||||||
|
event.pageX - event.currentTarget.getBoundingClientRect().left;
|
||||||
|
const posX = offsetX / event.currentTarget.offsetWidth;
|
||||||
|
|
||||||
|
setPosition(posX * duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { position, duration } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span onClick={this.handleClick} className="ai-track-progress-bar">
|
||||||
|
<span
|
||||||
|
className="ai-track-progress"
|
||||||
|
style={{ width: `${(position * 100) / duration}%` }}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar.propTypes = {
|
||||||
|
setPosition: PropTypes.func,
|
||||||
|
position: PropTypes.number.isRequired,
|
||||||
|
duration: PropTypes.number.isRequired,
|
||||||
|
};
|
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export default class Time extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { countdown } = this.props;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
showRemaining: countdown || false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretty prints time remaining/elapsed
|
||||||
|
*
|
||||||
|
* @param {number} position - Track position in milliseconds
|
||||||
|
* @param {number} duration - Track duration in milliseconds
|
||||||
|
* @returns {string} - Time pretty formatted
|
||||||
|
*/
|
||||||
|
formatTime(position, duration) {
|
||||||
|
const { showRemaining } = this.state;
|
||||||
|
const positionInSeconds = showRemaining
|
||||||
|
? (duration - position) / 1000
|
||||||
|
: position / 1000;
|
||||||
|
const hours = Math.floor(positionInSeconds / 3600);
|
||||||
|
let min = Math.floor((positionInSeconds % 3600) / 60);
|
||||||
|
let sec = Math.floor(positionInSeconds % 60);
|
||||||
|
let time = '00:00';
|
||||||
|
|
||||||
|
min = min >= 10 ? min : `0${min}`;
|
||||||
|
sec = sec >= 10 ? sec : `0${sec}`;
|
||||||
|
|
||||||
|
if (!isNaN(sec)) {
|
||||||
|
if (hours) {
|
||||||
|
time = `${hours}:${min}:${sec}`;
|
||||||
|
} else {
|
||||||
|
time = `${min}:${sec}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return showRemaining ? `-${time}` : time;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick() {
|
||||||
|
const { showRemaining } = this.state;
|
||||||
|
this.setState({ showRemaining: !showRemaining });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { position, duration } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="ai-track-time" onClick={this.handleClick}>
|
||||||
|
{this.formatTime(position, duration)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Time.propTypes = {
|
||||||
|
position: PropTypes.number.isRequired,
|
||||||
|
duration: PropTypes.number.isRequired,
|
||||||
|
countdown: PropTypes.bool.isRequired,
|
||||||
|
};
|
@ -0,0 +1,153 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Sound from 'react-sound';
|
||||||
|
import { sprintf } from 'sprintf-js';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import TrackTitle from './TrackTitle';
|
||||||
|
import Cover from './Cover';
|
||||||
|
import TrackButtons from './TrackButtons';
|
||||||
|
import ProgressBar from './ProgressBar';
|
||||||
|
import { PlayIcon, PauseIcon } from './Icons';
|
||||||
|
import { AppContext } from '../../App';
|
||||||
|
|
||||||
|
const Track = ({
|
||||||
|
track,
|
||||||
|
index,
|
||||||
|
trackNo,
|
||||||
|
isActive,
|
||||||
|
playStatus,
|
||||||
|
duration,
|
||||||
|
position,
|
||||||
|
setPosition,
|
||||||
|
isStandalone,
|
||||||
|
buyButtonsTarget,
|
||||||
|
displayArtistNames,
|
||||||
|
displayCovers,
|
||||||
|
displayBuyButtons,
|
||||||
|
onTrackClick,
|
||||||
|
onTrackLoop,
|
||||||
|
className,
|
||||||
|
isLooping,
|
||||||
|
playbackRate,
|
||||||
|
setPlaybackRate,
|
||||||
|
allowPlaybackRate,
|
||||||
|
buffering,
|
||||||
|
}) => {
|
||||||
|
const { toggleLyricsModal } = useContext(AppContext);
|
||||||
|
const isPlaying = isActive && playStatus === Sound.status.PLAYING;
|
||||||
|
const hasProgressBar =
|
||||||
|
typeof position !== 'undefined' &&
|
||||||
|
typeof duration !== 'undefined' &&
|
||||||
|
isActive &&
|
||||||
|
isStandalone;
|
||||||
|
const classes = classNames({
|
||||||
|
[className]: !!className,
|
||||||
|
'ai-track-active': isActive,
|
||||||
|
'ai-track-loading': isActive && buffering,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className={classes}>
|
||||||
|
{displayCovers && (
|
||||||
|
<Cover
|
||||||
|
className="ai-track-thumb"
|
||||||
|
src={track.cover}
|
||||||
|
alt={track.title}
|
||||||
|
onClick={() => onTrackClick(index)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isStandalone && (
|
||||||
|
<button
|
||||||
|
className={classNames({
|
||||||
|
'ai-track-btn ai-track-inline-play-btn': true,
|
||||||
|
'ai-is-loading': isActive && buffering,
|
||||||
|
})}
|
||||||
|
onClick={() => onTrackClick(index)}
|
||||||
|
aria-label={
|
||||||
|
isPlaying
|
||||||
|
? sprintf(aiStrings.pause_title, track.title)
|
||||||
|
: sprintf(aiStrings.play_title, track.title)
|
||||||
|
}
|
||||||
|
aria-pressed={isPlaying}
|
||||||
|
>
|
||||||
|
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||||
|
<span className="ai-track-spinner" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="ai-track-control" onClick={() => onTrackClick(index)}>
|
||||||
|
<TrackTitle
|
||||||
|
className="ai-track-name"
|
||||||
|
track={track}
|
||||||
|
trackNo={trackNo}
|
||||||
|
displayArtistNames={displayArtistNames}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TrackButtons
|
||||||
|
buyButtonsTarget={buyButtonsTarget}
|
||||||
|
buyUrl={track.buyUrl}
|
||||||
|
downloadUrl={track.downloadUrl}
|
||||||
|
downloadFilename={track.downloadFilename}
|
||||||
|
onTrackLoop={onTrackLoop && (() => onTrackLoop(index))}
|
||||||
|
isLooping={isLooping}
|
||||||
|
displayBuyButtons={displayBuyButtons}
|
||||||
|
onOpenTrackLyrics={
|
||||||
|
track.lyrics && (() => toggleLyricsModal(true, track))
|
||||||
|
}
|
||||||
|
playbackRate={playbackRate}
|
||||||
|
setPlaybackRate={setPlaybackRate}
|
||||||
|
allowPlaybackRate={allowPlaybackRate}
|
||||||
|
isPlaying={isPlaying}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{hasProgressBar && (
|
||||||
|
<ProgressBar
|
||||||
|
setPosition={setPosition}
|
||||||
|
duration={duration}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Track.propTypes = {
|
||||||
|
track: PropTypes.shape({
|
||||||
|
audio: PropTypes.string,
|
||||||
|
buyUrl: PropTypes.string,
|
||||||
|
cover: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
subtitle: PropTypes.string,
|
||||||
|
lyrics: PropTypes.string,
|
||||||
|
downloadUrl: PropTypes.string,
|
||||||
|
}),
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
trackNo: PropTypes.number,
|
||||||
|
isActive: PropTypes.bool,
|
||||||
|
position: PropTypes.number,
|
||||||
|
duration: PropTypes.number,
|
||||||
|
setPosition: PropTypes.func,
|
||||||
|
playStatus: PropTypes.oneOf([
|
||||||
|
Sound.status.PLAYING,
|
||||||
|
Sound.status.PAUSED,
|
||||||
|
Sound.status.STOPPED,
|
||||||
|
]),
|
||||||
|
onTrackClick: PropTypes.func.isRequired,
|
||||||
|
onTrackLoop: PropTypes.func,
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
isStandalone: PropTypes.bool,
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
displayCovers: PropTypes.bool,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
isLooping: PropTypes.bool,
|
||||||
|
playbackRate: PropTypes.number,
|
||||||
|
setPlaybackRate: PropTypes.func,
|
||||||
|
allowPlaybackRate: PropTypes.bool,
|
||||||
|
buffering: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Track;
|
@ -0,0 +1,131 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CartIcon, DownloadIcon, LyricsIcon, RefreshIcon } from './Icons';
|
||||||
|
|
||||||
|
const TrackButtons = ({
|
||||||
|
buyButtonsTarget,
|
||||||
|
buyUrl,
|
||||||
|
downloadUrl,
|
||||||
|
downloadFilename,
|
||||||
|
onTrackLoop,
|
||||||
|
isLooping,
|
||||||
|
displayBuyButtons,
|
||||||
|
onOpenTrackLyrics,
|
||||||
|
setPlaybackRate,
|
||||||
|
playbackRate,
|
||||||
|
allowPlaybackRate,
|
||||||
|
isPlaying,
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
buyUrl == null &&
|
||||||
|
downloadUrl == null &&
|
||||||
|
!onTrackLoop &&
|
||||||
|
!onOpenTrackLyrics
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ai-track-control-buttons">
|
||||||
|
{buyUrl && displayBuyButtons && (
|
||||||
|
<a
|
||||||
|
href={buyUrl}
|
||||||
|
className="ai-track-btn"
|
||||||
|
rel={buyButtonsTarget ? 'noopener noreferrer' : undefined}
|
||||||
|
target={buyButtonsTarget ? '_blank' : '_self'}
|
||||||
|
role="button"
|
||||||
|
aria-label={aiStrings.buy_track}
|
||||||
|
title={aiStrings.buy_track}
|
||||||
|
>
|
||||||
|
<CartIcon />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{downloadUrl && downloadFilename && displayBuyButtons && (
|
||||||
|
<a
|
||||||
|
href={downloadUrl}
|
||||||
|
download={downloadFilename}
|
||||||
|
className="ai-track-btn"
|
||||||
|
role="button"
|
||||||
|
aria-label={aiStrings.download_track}
|
||||||
|
title={aiStrings.download_track}
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{onOpenTrackLyrics && (
|
||||||
|
// eslint-disable-next-line
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="ai-track-btn"
|
||||||
|
role="button"
|
||||||
|
aria-label={aiStrings.open_track_lyrics}
|
||||||
|
title={aiStrings.open_track_lyrics}
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault();
|
||||||
|
onOpenTrackLyrics();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LyricsIcon />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{allowPlaybackRate && isPlaying && (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="ai-track-btn ai-btn-playback-rate"
|
||||||
|
role="button"
|
||||||
|
aria-label={aiStrings.set_playback_rate}
|
||||||
|
title={aiStrings.set_playback_rate}
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault();
|
||||||
|
setPlaybackRate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Fragment>×{playbackRate}</Fragment>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{onTrackLoop && (
|
||||||
|
// eslint-disable-next-line
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="ai-track-btn ai-track-btn-repeat"
|
||||||
|
role="button"
|
||||||
|
aria-label={aiStrings.toggle_track_repeat}
|
||||||
|
title={aiStrings.toggle_track_repeat}
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault();
|
||||||
|
onTrackLoop();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
opacity: isLooping ? 1 : 0.3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RefreshIcon />
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TrackButtons.propTypes = {
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
buyUrl: PropTypes.string,
|
||||||
|
downloadUrl: PropTypes.string,
|
||||||
|
downloadFilename: PropTypes.string,
|
||||||
|
onTrackLoop: PropTypes.func,
|
||||||
|
isLooping: PropTypes.bool,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
onOpenTrackLyrics: PropTypes.func,
|
||||||
|
playbackRate: PropTypes.number,
|
||||||
|
setPlaybackRate: PropTypes.func,
|
||||||
|
allowPlaybackRate: PropTypes.bool,
|
||||||
|
isPlaying: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TrackButtons;
|
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Modal from 'react-modal';
|
||||||
|
|
||||||
|
if (document.querySelector('.audioigniter-root')) {
|
||||||
|
Modal.setAppElement('.audioigniter-root');
|
||||||
|
}
|
||||||
|
|
||||||
|
const TrackLyricsModal = ({ isOpen, closeModal, children }) => {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
closeModal={closeModal}
|
||||||
|
onRequestClose={closeModal}
|
||||||
|
overlayClassName="ai-modal-overlay"
|
||||||
|
className="ai-modal"
|
||||||
|
>
|
||||||
|
<div className="ai-modal-wrap">
|
||||||
|
<div className="ai-modal-header">
|
||||||
|
<button
|
||||||
|
className="ai-modal-dismiss"
|
||||||
|
type="button"
|
||||||
|
onClick={closeModal}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ai-modal-content">{children}</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
closeModal: PropTypes.func.isRequired,
|
||||||
|
children: PropTypes.any,
|
||||||
|
};
|
||||||
|
|
||||||
|
TrackLyricsModal.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default TrackLyricsModal;
|
@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const TrackTitle = ({
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
track,
|
||||||
|
trackNo,
|
||||||
|
displayArtistNames,
|
||||||
|
}) => {
|
||||||
|
let trackTitle = track.title;
|
||||||
|
|
||||||
|
if (displayArtistNames && track.subtitle) {
|
||||||
|
trackTitle = `${track.title} - ${track.subtitle}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackNo != null) {
|
||||||
|
trackTitle = `${trackNo}. ${trackTitle}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={className} style={style}>
|
||||||
|
{trackTitle}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TrackTitle.propTypes = {
|
||||||
|
track: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
trackNo: PropTypes.number,
|
||||||
|
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
|
className: PropTypes.string,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TrackTitle;
|
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Sound from 'react-sound';
|
||||||
|
import Track from './Track';
|
||||||
|
|
||||||
|
const Tracklist = ({ ...props }) => {
|
||||||
|
const { tracks } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className={props.className} aria-expanded="true">
|
||||||
|
{tracks &&
|
||||||
|
tracks.map((track, index) => {
|
||||||
|
const trackNo = props.reverseTrackOrder
|
||||||
|
? tracks.length - index
|
||||||
|
: index + 1;
|
||||||
|
const isLooping = index === props.repeatingTrackIndex;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Track
|
||||||
|
key={index}
|
||||||
|
track={track}
|
||||||
|
index={index}
|
||||||
|
trackNo={props.displayTrackNo ? trackNo : undefined}
|
||||||
|
playStatus={props.playStatus}
|
||||||
|
isActive={props.activeTrackIndex === index}
|
||||||
|
buyButtonsTarget={props.buyButtonsTarget}
|
||||||
|
displayArtistNames={props.displayArtistNames}
|
||||||
|
displayBuyButtons={props.displayBuyButtons}
|
||||||
|
displayCovers={props.displayCovers}
|
||||||
|
onTrackClick={props.onTrackClick}
|
||||||
|
onTrackLoop={props.onTrackLoop}
|
||||||
|
setPosition={props.setPosition}
|
||||||
|
duration={props.duration}
|
||||||
|
position={props.position}
|
||||||
|
className={props.trackClassName}
|
||||||
|
isStandalone={props.standaloneTracks}
|
||||||
|
isLooping={isLooping}
|
||||||
|
playbackRate={props.playbackRate}
|
||||||
|
setPlaybackRate={props.setPlaybackRate}
|
||||||
|
allowPlaybackRate={props.allowPlaybackRate}
|
||||||
|
buffering={props.buffering}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Tracklist.propTypes = {
|
||||||
|
tracks: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
playStatus: PropTypes.oneOf([
|
||||||
|
Sound.status.PLAYING,
|
||||||
|
Sound.status.PAUSED,
|
||||||
|
Sound.status.STOPPED,
|
||||||
|
]),
|
||||||
|
activeTrackIndex: PropTypes.number,
|
||||||
|
position: PropTypes.number,
|
||||||
|
duration: PropTypes.number,
|
||||||
|
setPosition: PropTypes.func,
|
||||||
|
standaloneTracks: PropTypes.bool,
|
||||||
|
onTrackClick: PropTypes.func.isRequired,
|
||||||
|
onTrackLoop: PropTypes.func,
|
||||||
|
className: PropTypes.string,
|
||||||
|
trackClassName: PropTypes.string,
|
||||||
|
reverseTrackOrder: PropTypes.bool,
|
||||||
|
displayTrackNo: PropTypes.bool,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
displayCovers: PropTypes.bool,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
playbackRate: PropTypes.number,
|
||||||
|
setPlaybackRate: PropTypes.func,
|
||||||
|
allowPlaybackRate: PropTypes.bool,
|
||||||
|
buffering: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tracklist;
|
@ -0,0 +1,95 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Scrollbars } from 'react-custom-scrollbars';
|
||||||
|
import Tracklist from './Tracklist';
|
||||||
|
|
||||||
|
export default class TracklistWrap extends React.Component {
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { activeTrackIndex, limitTracklistHeight } = this.props;
|
||||||
|
|
||||||
|
if (
|
||||||
|
activeTrackIndex !== nextProps.activeTrackIndex &&
|
||||||
|
limitTracklistHeight
|
||||||
|
) {
|
||||||
|
this.scrollToTrack(nextProps.activeTrackIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToTrack(trackIndex) {
|
||||||
|
const { tracks } = this.props;
|
||||||
|
const trackHeight = this.scrollbarsRef.getScrollHeight() / tracks.length;
|
||||||
|
|
||||||
|
if (!this.isTrackVisible(trackIndex)) {
|
||||||
|
this.scrollbarsRef.scrollTop(trackHeight * trackIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isTrackVisible(trackIndex) {
|
||||||
|
const { tracks } = this.props;
|
||||||
|
const trackHeight = this.scrollbarsRef.getScrollHeight() / tracks.length;
|
||||||
|
const trackPosition = trackHeight * trackIndex;
|
||||||
|
const scrollTop = this.scrollbarsRef.getScrollTop();
|
||||||
|
const scrollBottom = scrollTop + this.scrollbarsRef.getClientHeight();
|
||||||
|
|
||||||
|
return !(trackPosition < scrollTop || trackPosition > scrollBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTracklist() {
|
||||||
|
return (
|
||||||
|
<Tracklist
|
||||||
|
tracks={this.props.tracks}
|
||||||
|
activeTrackIndex={this.props.activeTrackIndex}
|
||||||
|
onTrackClick={this.props.onTrackClick}
|
||||||
|
className={this.props.className}
|
||||||
|
trackClassName={this.props.trackClassName}
|
||||||
|
reverseTrackOrder={this.props.reverseTrackOrder}
|
||||||
|
displayTrackNo={this.props.displayTrackNo}
|
||||||
|
displayBuyButtons={this.props.displayBuyButtons}
|
||||||
|
buyButtonsTarget={this.props.buyButtonsTarget}
|
||||||
|
displayCovers={this.props.displayCovers}
|
||||||
|
displayArtistNames={this.props.displayArtistNames}
|
||||||
|
onTrackLoop={this.props.onTrackLoop}
|
||||||
|
repeatingTrackIndex={this.props.repeatingTrackIndex}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isOpen, limitTracklistHeight, tracklistHeight } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="tracklisting" style={{ display: isOpen ? 'block' : 'none' }}>
|
||||||
|
{limitTracklistHeight ? (
|
||||||
|
<Scrollbars
|
||||||
|
className="ai-scroll-wrap"
|
||||||
|
ref={ref => (this.scrollbarsRef = ref)} // eslint-disable-line no-return-assign
|
||||||
|
style={{ height: tracklistHeight }}
|
||||||
|
>
|
||||||
|
{this.renderTracklist()}
|
||||||
|
</Scrollbars>
|
||||||
|
) : (
|
||||||
|
this.renderTracklist()
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TracklistWrap.propTypes = {
|
||||||
|
tracks: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
activeTrackIndex: PropTypes.number.isRequired,
|
||||||
|
onTrackClick: PropTypes.func.isRequired,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
|
trackClassName: PropTypes.string,
|
||||||
|
reverseTrackOrder: PropTypes.bool,
|
||||||
|
displayTrackNo: PropTypes.bool,
|
||||||
|
limitTracklistHeight: PropTypes.bool,
|
||||||
|
tracklistHeight: PropTypes.number,
|
||||||
|
displayBuyButtons: PropTypes.bool,
|
||||||
|
buyButtonsTarget: PropTypes.bool,
|
||||||
|
displayCovers: PropTypes.bool,
|
||||||
|
displayArtistNames: PropTypes.bool,
|
||||||
|
onTrackLoop: PropTypes.func,
|
||||||
|
repeatingTrackIndex: PropTypes.number,
|
||||||
|
};
|
@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Button from './Button';
|
||||||
|
import { VolumeUpIcon, VolumeDownIcon } from './Icons';
|
||||||
|
|
||||||
|
export default class VolumeControl extends React.Component {
|
||||||
|
renderVolumeBars() {
|
||||||
|
const { volume, setVolume } = this.props;
|
||||||
|
|
||||||
|
return Array(...Array(11)).map((bar, i) => (
|
||||||
|
<span
|
||||||
|
key={i} // eslint-disable-line react/no-array-index-key
|
||||||
|
className={`ai-volume-bar ${
|
||||||
|
i <= volume / 10 ? 'ai-volume-bar-active' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => setVolume(i * 10)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { volume, setVolume } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ai-audio-volume-control">
|
||||||
|
<div className="ai-audio-volume-bars">{this.renderVolumeBars()}</div>
|
||||||
|
|
||||||
|
<div className="ai-audio-volume-control-btns">
|
||||||
|
<Button
|
||||||
|
className="ai-btn"
|
||||||
|
onClick={() => setVolume(volume >= 100 ? volume : volume + 10)}
|
||||||
|
aria-label={aiStrings.volume_up}
|
||||||
|
>
|
||||||
|
<VolumeUpIcon />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="ai-btn"
|
||||||
|
onClick={() => setVolume(volume <= 0 ? volume : volume - 10)}
|
||||||
|
aria-label={aiStrings.volume_down}
|
||||||
|
>
|
||||||
|
<VolumeDownIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeControl.propTypes = {
|
||||||
|
volume: PropTypes.number.isRequired,
|
||||||
|
setVolume: PropTypes.func.isRequired,
|
||||||
|
};
|
@ -0,0 +1,82 @@
|
|||||||
|
import React, { useRef, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import WaveSurfer from 'wavesurfer.js';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
position: PropTypes.number.isRequired,
|
||||||
|
duration: PropTypes.number.isRequired,
|
||||||
|
audio: PropTypes.string,
|
||||||
|
setPosition: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const WaveformProgressBar = ({ audio, position, duration, setPosition }) => {
|
||||||
|
const waveFormDomRef = useRef(null);
|
||||||
|
const wavesurfer = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (waveFormDomRef.current && audio) {
|
||||||
|
wavesurfer.current = WaveSurfer.create({
|
||||||
|
container: waveFormDomRef.current,
|
||||||
|
mediaControls: false,
|
||||||
|
height: 40,
|
||||||
|
barWidth: 2,
|
||||||
|
barGap: 2,
|
||||||
|
barRadius: 3,
|
||||||
|
responsive: true,
|
||||||
|
cursorWidth: 0,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
progressColor: '#f70f5d',
|
||||||
|
waveColor: '#fff',
|
||||||
|
xhr: {
|
||||||
|
mode: 'no-cors',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
wavesurfer.current.load(audio);
|
||||||
|
wavesurfer.current.on('ready', () => {
|
||||||
|
console.log('wavesurfer loaded');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (wavesurfer.current) {
|
||||||
|
wavesurfer.current.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [audio, waveFormDomRef.current]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Sync wavesurfer with current playing position
|
||||||
|
const progress = position / duration;
|
||||||
|
|
||||||
|
if (wavesurfer.current && !Number.isNaN(progress)) {
|
||||||
|
wavesurfer.current.seekTo(progress || 0);
|
||||||
|
}
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
|
const handleClick = event => {
|
||||||
|
if (setPosition == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsetX =
|
||||||
|
event.pageX - event.currentTarget.getBoundingClientRect().left;
|
||||||
|
const posX = offsetX / event.currentTarget.offsetWidth;
|
||||||
|
|
||||||
|
setPosition(posX * duration);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!audio) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ai-waveform-bar" onClick={handleClick}>
|
||||||
|
<div className="ai-waveform" ref={waveFormDomRef} />
|
||||||
|
<div className="ai-waveform-progress" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WaveformProgressBar.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WaveformProgressBar;
|
@ -0,0 +1,418 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Sound from 'react-sound';
|
||||||
|
|
||||||
|
import SoundCloud from '../utils/soundcloud';
|
||||||
|
import multiSoundDisabled from '../utils/multi-sound-disabled';
|
||||||
|
import { getInitialTrackQueueAndIndex } from '../utils/getInitialTrackIndex';
|
||||||
|
|
||||||
|
const PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
|
||||||
|
|
||||||
|
const soundProvider = (Player, events) => {
|
||||||
|
class EnhancedPlayer extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const {
|
||||||
|
volume,
|
||||||
|
cycleTracks,
|
||||||
|
defaultShuffle,
|
||||||
|
shuffleEnabled,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
tracks: [],
|
||||||
|
activeIndex: 0, // Determine active track by index
|
||||||
|
|
||||||
|
// trackQueue: List of track indexes that represents the order of the playlist
|
||||||
|
// i.e. [0, 1, 2, 3, 4] will play the 1st, 2nd, 3rd, etc track.
|
||||||
|
// [5, 4, 3, 2, 1] will play the tracks reversed.
|
||||||
|
// [4, 2, 0, ...] will play the 5th track first, 3rd second, then the 1st, etc.
|
||||||
|
trackQueue: [],
|
||||||
|
playStatus: Sound.status.STOPPED,
|
||||||
|
position: 0,
|
||||||
|
duration: 0,
|
||||||
|
playbackRate: 1,
|
||||||
|
volume: volume == null ? 100 : volume,
|
||||||
|
cycleTracks,
|
||||||
|
repeatingTrackIndex: null,
|
||||||
|
isMultiSoundDisabled: multiSoundDisabled(),
|
||||||
|
buffering: false,
|
||||||
|
shuffle: shuffleEnabled && defaultShuffle,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.playTrack = this.playTrack.bind(this);
|
||||||
|
this.pauseTrack = this.pauseTrack.bind(this);
|
||||||
|
this.togglePlay = this.togglePlay.bind(this);
|
||||||
|
this.nextTrack = this.nextTrack.bind(this);
|
||||||
|
this.prevTrack = this.prevTrack.bind(this);
|
||||||
|
this.setPosition = this.setPosition.bind(this);
|
||||||
|
this.setVolume = this.setVolume.bind(this);
|
||||||
|
this.skipPosition = this.skipPosition.bind(this);
|
||||||
|
this.setPlaybackRate = this.setPlaybackRate.bind(this);
|
||||||
|
this.toggleTracklistCycling = this.toggleTracklistCycling.bind(this);
|
||||||
|
this.toggleShuffle = this.toggleShuffle.bind(this);
|
||||||
|
this.setTrackCycling = this.setTrackCycling.bind(this);
|
||||||
|
this.reverseTracks = this.reverseTracks.bind(this);
|
||||||
|
this.getFinalProps = this.getFinalProps.bind(this);
|
||||||
|
this.onPlaying = this.onPlaying.bind(this);
|
||||||
|
this.onFinishedPlaying = this.onFinishedPlaying.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const {
|
||||||
|
tracksUrl,
|
||||||
|
soundcloudClientId,
|
||||||
|
reverseTrackOrder,
|
||||||
|
initialTrack,
|
||||||
|
} = this.props;
|
||||||
|
const { shuffle } = this.state;
|
||||||
|
const tracksPromised = fetch(tracksUrl).then(res => res.json());
|
||||||
|
|
||||||
|
if (!soundcloudClientId) {
|
||||||
|
tracksPromised.then(tracks => {
|
||||||
|
const { trackQueue, activeIndex } = getInitialTrackQueueAndIndex({
|
||||||
|
tracks,
|
||||||
|
initialTrack,
|
||||||
|
reverseTrackOrder,
|
||||||
|
shuffle,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
tracks,
|
||||||
|
activeIndex,
|
||||||
|
trackQueue,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (reverseTrackOrder) {
|
||||||
|
this.reverseTracks();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sc = new SoundCloud(soundcloudClientId);
|
||||||
|
const scTracks = tracksPromised
|
||||||
|
.then(tracks => sc.fetchSoundCloudStreams(tracks))
|
||||||
|
.catch(err => console.error(err)); // eslint-disable-line no-console
|
||||||
|
|
||||||
|
// Make sure if SoundCloud fetching fails
|
||||||
|
// we delegate and load our tracks anyway
|
||||||
|
const promiseArray = [tracksPromised, scTracks].map(p =>
|
||||||
|
p.catch(error => ({
|
||||||
|
status: 'error',
|
||||||
|
error,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
Promise.all(promiseArray).then(res => {
|
||||||
|
if (res[1].status === 'error') {
|
||||||
|
return this.setState({ tracks: res[0] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const tracks = sc.mapStreamsToTracks(...res);
|
||||||
|
const { trackQueue, activeIndex } = getInitialTrackQueueAndIndex({
|
||||||
|
tracks,
|
||||||
|
initialTrack,
|
||||||
|
reverseTrackOrder,
|
||||||
|
shuffle,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.setState(
|
||||||
|
() => ({
|
||||||
|
tracks,
|
||||||
|
activeIndex,
|
||||||
|
trackQueue,
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
if (reverseTrackOrder) {
|
||||||
|
this.reverseTracks();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
onPlaying({ duration, position }) {
|
||||||
|
this.setState(
|
||||||
|
() => ({ duration, position }),
|
||||||
|
() => {
|
||||||
|
if (events && events.onPlaying) {
|
||||||
|
events.onPlaying(this.getFinalProps());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFinishedPlaying() {
|
||||||
|
const { stopOnTrackFinish, delayBetweenTracks = 0 } = this.props;
|
||||||
|
const delayBetweenTracksMs = delayBetweenTracks * 1000;
|
||||||
|
this.setState(() => ({ playStatus: Sound.status.STOPPED }));
|
||||||
|
|
||||||
|
if (stopOnTrackFinish) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events && events.onFinishedPlaying) {
|
||||||
|
setTimeout(() => {
|
||||||
|
events.onFinishedPlaying(this.getFinalProps());
|
||||||
|
}, delayBetweenTracksMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFinalProps() {
|
||||||
|
const { tracks, activeIndex } = this.state;
|
||||||
|
const currentTrack = tracks[activeIndex] || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
playTrack: this.playTrack,
|
||||||
|
pauseTrack: this.pauseTrack,
|
||||||
|
togglePlay: this.togglePlay,
|
||||||
|
nextTrack: this.nextTrack,
|
||||||
|
prevTrack: this.prevTrack,
|
||||||
|
setPosition: this.setPosition,
|
||||||
|
skipPosition: this.skipPosition,
|
||||||
|
setPlaybackRate: this.setPlaybackRate,
|
||||||
|
setVolume: this.setVolume,
|
||||||
|
toggleTracklistCycling: this.toggleTracklistCycling,
|
||||||
|
setTrackCycling: this.setTrackCycling,
|
||||||
|
toggleShuffle: this.toggleShuffle,
|
||||||
|
currentTrack,
|
||||||
|
...this.props,
|
||||||
|
...this.state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setVolume(volume) {
|
||||||
|
this.setState(() => ({ volume }));
|
||||||
|
}
|
||||||
|
|
||||||
|
setPosition(position) {
|
||||||
|
this.setState(() => ({ position }));
|
||||||
|
}
|
||||||
|
|
||||||
|
setTrackCycling(index, event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { activeIndex, cycleTracks } = this.state;
|
||||||
|
|
||||||
|
if (cycleTracks && index != null) {
|
||||||
|
this.toggleTracklistCycling();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
({ repeatingTrackIndex }) => ({
|
||||||
|
repeatingTrackIndex: repeatingTrackIndex === index ? null : index,
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
if (index != null && activeIndex !== index) {
|
||||||
|
this.playTrack(index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlaybackRate() {
|
||||||
|
this.setState(({ playbackRate }) => {
|
||||||
|
const currentIndex = PLAYBACK_RATES.findIndex(
|
||||||
|
rate => rate === playbackRate,
|
||||||
|
);
|
||||||
|
const nextIndex =
|
||||||
|
(PLAYBACK_RATES.length + (currentIndex + 1)) % PLAYBACK_RATES.length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
playbackRate: PLAYBACK_RATES[nextIndex],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShuffle() {
|
||||||
|
const { initialTrack, reverseTrackOrder } = this.props;
|
||||||
|
const { tracks } = this.state;
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
prev => ({
|
||||||
|
shuffle: !prev.shuffle,
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
this.setState(() => {
|
||||||
|
const { trackQueue } = getInitialTrackQueueAndIndex({
|
||||||
|
tracks,
|
||||||
|
initialTrack,
|
||||||
|
reverseTrackOrder,
|
||||||
|
shuffle: this.state.shuffle,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
trackQueue,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.state.shuffle) {
|
||||||
|
// Shuffle track queue
|
||||||
|
} else {
|
||||||
|
// Unshuffle track queue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipPosition(direction = 1) {
|
||||||
|
const { position } = this.state;
|
||||||
|
const { skipAmount } = this.props;
|
||||||
|
const amount = parseInt(skipAmount, 10) * 1000;
|
||||||
|
|
||||||
|
this.setPosition(position + amount * direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
playTrack(index, event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { repeatingTrackIndex, isMultiSoundDisabled } = this.state;
|
||||||
|
|
||||||
|
if (isMultiSoundDisabled) {
|
||||||
|
window.soundManager.pauseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(() => ({
|
||||||
|
activeIndex: index,
|
||||||
|
position: 0,
|
||||||
|
playStatus: Sound.status.PLAYING,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Reset repating track index if the track is not the active one.
|
||||||
|
if (index !== repeatingTrackIndex && repeatingTrackIndex != null) {
|
||||||
|
this.setTrackCycling(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseTrack(event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { playStatus } = this.state;
|
||||||
|
|
||||||
|
if (playStatus === Sound.status.PLAYING) {
|
||||||
|
this.setState(() => ({ playStatus: Sound.status.PAUSED }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePlay(index, event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { activeIndex } = this.state;
|
||||||
|
|
||||||
|
if (typeof index === 'number' && index !== activeIndex) {
|
||||||
|
this.playTrack(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(({ playStatus, isMultiSoundDisabled }) => {
|
||||||
|
if (playStatus !== Sound.status.PLAYING && isMultiSoundDisabled) {
|
||||||
|
window.soundManager.pauseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
playStatus:
|
||||||
|
playStatus === Sound.status.PLAYING
|
||||||
|
? Sound.status.PAUSED
|
||||||
|
: Sound.status.PLAYING,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTrack() {
|
||||||
|
const { trackQueue, activeIndex } = this.state;
|
||||||
|
const currentQueueIndex = trackQueue.indexOf(activeIndex);
|
||||||
|
const nextQueueIndex = (currentQueueIndex + 1) % trackQueue.length;
|
||||||
|
const nextTrackIndex = trackQueue[nextQueueIndex];
|
||||||
|
|
||||||
|
this.playTrack(nextTrackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevTrack() {
|
||||||
|
const { trackQueue, activeIndex } = this.state;
|
||||||
|
const currentQueueIndex = trackQueue.indexOf(activeIndex);
|
||||||
|
const prevQueueIndex =
|
||||||
|
(currentQueueIndex + trackQueue.length - 1) % trackQueue.length;
|
||||||
|
const prevTrackIndex = trackQueue[prevQueueIndex];
|
||||||
|
|
||||||
|
this.playTrack(prevTrackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTracklistCycling() {
|
||||||
|
const { repeatingTrackIndex } = this.state;
|
||||||
|
|
||||||
|
if (repeatingTrackIndex !== null) {
|
||||||
|
this.setTrackCycling(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(state => ({
|
||||||
|
cycleTracks: !state.cycleTracks,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
reverseTracks() {
|
||||||
|
this.setState(state => ({
|
||||||
|
tracks: state.tracks.slice().reverse(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { tracks, playStatus, position, volume, playbackRate } = this.state;
|
||||||
|
const finalProps = this.getFinalProps();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ai-audioigniter">
|
||||||
|
<Player {...finalProps} />
|
||||||
|
|
||||||
|
{tracks.length > 0 && (
|
||||||
|
<Sound
|
||||||
|
url={finalProps.currentTrack.audio}
|
||||||
|
playStatus={playStatus}
|
||||||
|
position={position}
|
||||||
|
volume={volume}
|
||||||
|
onPlaying={this.onPlaying}
|
||||||
|
onFinishedPlaying={this.onFinishedPlaying}
|
||||||
|
onPause={() => this.pauseTrack()}
|
||||||
|
playbackRate={playbackRate}
|
||||||
|
onBufferChange={buffering => {
|
||||||
|
this.setState({ buffering });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnhancedPlayer.propTypes = {
|
||||||
|
volume: PropTypes.number,
|
||||||
|
cycleTracks: PropTypes.bool,
|
||||||
|
tracksUrl: PropTypes.string,
|
||||||
|
soundcloudClientId: PropTypes.string,
|
||||||
|
reverseTrackOrder: PropTypes.bool,
|
||||||
|
skipAmount: PropTypes.number,
|
||||||
|
stopOnTrackFinish: PropTypes.bool,
|
||||||
|
delayBetweenTracks: PropTypes.number,
|
||||||
|
initialTrack: PropTypes.number,
|
||||||
|
shuffleEnabled: PropTypes.bool,
|
||||||
|
defaultShuffle: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
return EnhancedPlayer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default soundProvider;
|
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Shifts an array to right / left by n positions.
|
||||||
|
*
|
||||||
|
* @param {Array} arr The array.
|
||||||
|
* @param {number} direction The direction - 0 for left 1 for right.
|
||||||
|
* @param {number} n Number of positions to shift by.
|
||||||
|
* @returns {any[]}
|
||||||
|
*/
|
||||||
|
const arrayShift = (arr, direction, n) => {
|
||||||
|
const times = n > arr.length ? n % arr.length : n;
|
||||||
|
return arr.concat(arr.splice(0, direction > 0 ? arr.length - times : times));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default arrayShift;
|
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Shuffles an array.
|
||||||
|
* Copied from https://github.com/sindresorhus/array-shuffle
|
||||||
|
*
|
||||||
|
* @param {Array} array The array to be shuffled.
|
||||||
|
* @returns {*[]|*}
|
||||||
|
*/
|
||||||
|
const arrayShuffle = array => {
|
||||||
|
if (!Array.isArray(array)) {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clone = [...array];
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-plusplus
|
||||||
|
for (let index = clone.length - 1; index > 0; index--) {
|
||||||
|
const newIndex = Math.floor(Math.random() * (index + 1));
|
||||||
|
[clone[index], clone[newIndex]] = [clone[newIndex], clone[index]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default arrayShuffle;
|
@ -0,0 +1,76 @@
|
|||||||
|
import arrayShuffle from './array-shuffle';
|
||||||
|
import arrayShift from './array-shift';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the initial track index.
|
||||||
|
*
|
||||||
|
* @param {Object} options The options.
|
||||||
|
* @param {Array} options.tracks The tracks.
|
||||||
|
* @param {number} [options.initialTrack] The initial track index.
|
||||||
|
* @param {boolean} options.reverseTrackOrder Whether the track order is reversed.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const getInitialTrackIndex = ({
|
||||||
|
tracks = [],
|
||||||
|
initialTrack = 1,
|
||||||
|
reverseTrackOrder = false,
|
||||||
|
}) => {
|
||||||
|
// The user provides a 1-index value.
|
||||||
|
const initialTrackIndex = initialTrack - 1;
|
||||||
|
|
||||||
|
if (!tracks.length || !initialTrack || initialTrack > tracks.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reverseTrackOrder) {
|
||||||
|
return Math.max(tracks.length - initialTrack, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialTrackIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the initial track index and the initial track queue.
|
||||||
|
*
|
||||||
|
* @param {Object} options The options.
|
||||||
|
* @param {Array} options.tracks The tracks.
|
||||||
|
* @param {Number} options.initialTrack The initial track number (1-indexed).
|
||||||
|
* @param {Boolean} reverseTrackOrder Whether the track order is reversed.
|
||||||
|
* @param {Boolean} shuffle Whether the track queue is shuffled.
|
||||||
|
* @returns {{activeIndex: number, trackQueue: (*[]|*)}|{activeIndex: number, trackQueue: *}}
|
||||||
|
*/
|
||||||
|
export const getInitialTrackQueueAndIndex = ({
|
||||||
|
tracks = [],
|
||||||
|
initialTrack = 1,
|
||||||
|
reverseTrackOrder = false,
|
||||||
|
shuffle = false,
|
||||||
|
}) => {
|
||||||
|
const activeIndex = getInitialTrackIndex({
|
||||||
|
tracks,
|
||||||
|
initialTrack,
|
||||||
|
reverseTrackOrder,
|
||||||
|
});
|
||||||
|
|
||||||
|
const orderedTrackIndexes = tracks.map((_, index) => index);
|
||||||
|
|
||||||
|
if (!shuffle) {
|
||||||
|
const shiftAmount = orderedTrackIndexes.indexOf(activeIndex);
|
||||||
|
return {
|
||||||
|
activeIndex,
|
||||||
|
trackQueue: arrayShift(orderedTrackIndexes, 0, shiftAmount),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const shuffledQueue = arrayShuffle(orderedTrackIndexes);
|
||||||
|
|
||||||
|
// Always bring the initial track (activeIndex) to the front of the queue.
|
||||||
|
shuffledQueue.splice(shuffledQueue.indexOf(activeIndex), 1);
|
||||||
|
shuffledQueue.unshift(activeIndex);
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeIndex,
|
||||||
|
trackQueue: shuffledQueue,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getInitialTrackIndex;
|
@ -0,0 +1,8 @@
|
|||||||
|
const multiSoundDisabled = () => {
|
||||||
|
return (
|
||||||
|
window.ai_pro_front_scripts &&
|
||||||
|
!!window.ai_pro_front_scripts.multi_sound_disabled
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default multiSoundDisabled;
|
@ -0,0 +1,88 @@
|
|||||||
|
export default class SoundCloud {
|
||||||
|
constructor(clientId) {
|
||||||
|
if (!clientId) {
|
||||||
|
throw new Error('SoundCloud client ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.baseUrl = 'https://api.soundcloud.com';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a URL is from SoundCloud
|
||||||
|
*
|
||||||
|
* @param {string} url - URL to be checked
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
static isSoundCloudUrl(url) {
|
||||||
|
return url.indexOf('soundcloud.com') > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a SoundCloud URL into a track object
|
||||||
|
*
|
||||||
|
* @param {string} url - URL to be resolved
|
||||||
|
*
|
||||||
|
* @returns {Promise.<*>}
|
||||||
|
*/
|
||||||
|
resolve(url) {
|
||||||
|
/*
|
||||||
|
* Tell the SoundCloud API not to serve a redirect. This is to get around
|
||||||
|
* CORS issues on Safari 7+, which likes to send pre-flight requests
|
||||||
|
* before following redirects, which has problems.
|
||||||
|
*
|
||||||
|
* https://github.com/soundcloud/soundcloud-javascript/issues/27
|
||||||
|
*/
|
||||||
|
const statusCodeMap = encodeURIComponent('_status_code_map[302]=200');
|
||||||
|
|
||||||
|
return fetch(
|
||||||
|
`${this.baseUrl}/resolve?url=${url}&client_id=${
|
||||||
|
this.clientId
|
||||||
|
}&${statusCodeMap}`,
|
||||||
|
)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => fetch(res.location))
|
||||||
|
.then(res => res.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves and fetches SoundCloud track objects
|
||||||
|
*
|
||||||
|
* @param {Object[]} tracks - Tracks object
|
||||||
|
*
|
||||||
|
* @returns {Promise.<*>}
|
||||||
|
*/
|
||||||
|
fetchSoundCloudStreams(tracks) {
|
||||||
|
const scTracks = tracks
|
||||||
|
.filter(track => SoundCloud.isSoundCloudUrl(track.audio))
|
||||||
|
.map(track => this.resolve(track.audio));
|
||||||
|
|
||||||
|
return Promise.all(scTracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a SoundCloud tracks object into an AudioIgniter one
|
||||||
|
* by replacing `track.audio` with `sctrack.stream_url`.
|
||||||
|
*
|
||||||
|
* Works *in order* of appearance in the `tracks` object.
|
||||||
|
*
|
||||||
|
* @param {Object[]} tracks - AudioIgniter tracks object
|
||||||
|
* @param {Object[]} scTracks - SoundCloud tracks object
|
||||||
|
*
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
mapStreamsToTracks(tracks, scTracks) {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
return tracks.map(track => {
|
||||||
|
if (SoundCloud.isSoundCloudUrl(track.audio)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
track.audio = `${scTracks[i].stream_url}?client_id=${this.clientId}`;
|
||||||
|
i++; // eslint-disable-line no-plusplus
|
||||||
|
}
|
||||||
|
|
||||||
|
return track;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
const typographyDisabled = () => {
|
||||||
|
return (
|
||||||
|
window.ai_pro_front_scripts &&
|
||||||
|
!!window.ai_pro_front_scripts.typography_disabled
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default typographyDisabled;
|
1023
wp-content/plugins/audioigniter/player/styles/style.scss
Normal file
1023
wp-content/plugins/audioigniter/player/styles/style.scss
Normal file
File diff suppressed because it is too large
Load Diff
80
wp-content/plugins/audioigniter/player/webpack.config.js
Normal file
80
wp-content/plugins/audioigniter/player/webpack.config.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const parts = require('./webpack.parts');
|
||||||
|
|
||||||
|
const TARGET = process.env.npm_lifecycle_event;
|
||||||
|
process.env.BABEL_ENV = TARGET;
|
||||||
|
|
||||||
|
const PATHS = {
|
||||||
|
app: path.join(__dirname, 'src'),
|
||||||
|
build: path.join(__dirname, 'build'),
|
||||||
|
style: path.join(__dirname, 'styles', 'style.scss'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const common = {
|
||||||
|
entry: {
|
||||||
|
style: PATHS.style,
|
||||||
|
app: PATHS.app,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: PATHS.build,
|
||||||
|
filename: '[name].js',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
title: 'AudioIgniter',
|
||||||
|
template: `${PATHS.app}/index.ejs`,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
devServer: {
|
||||||
|
contentBase: path.resolve('assets'),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.jsx'],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.jsx?$/,
|
||||||
|
use: ['babel-loader?cacheDirectory'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let config;
|
||||||
|
|
||||||
|
// Detect how npm is run and branch based on that
|
||||||
|
switch (TARGET) {
|
||||||
|
case 'build': {
|
||||||
|
config = merge(
|
||||||
|
common,
|
||||||
|
{
|
||||||
|
resolve: {
|
||||||
|
modules: [path.resolve(__dirname), 'node_modules'],
|
||||||
|
extensions: ['.js', '.jsx'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parts.minify(),
|
||||||
|
parts.extractCSS(PATHS.style),
|
||||||
|
parts.setFreeVariable('process.env.NODE_ENV', 'production'),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
config = merge(
|
||||||
|
common,
|
||||||
|
{
|
||||||
|
devtool: 'eval-source-map',
|
||||||
|
},
|
||||||
|
parts.setupSass(PATHS.style),
|
||||||
|
parts.devServer({
|
||||||
|
host: process.env.HOST,
|
||||||
|
port: process.env.PORT,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = config;
|
107
wp-content/plugins/audioigniter/player/webpack.parts.js
Normal file
107
wp-content/plugins/audioigniter/player/webpack.parts.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
const webpack = require('webpack');
|
||||||
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
const autoprefixer = require('autoprefixer');
|
||||||
|
|
||||||
|
exports.setupSass = paths => ({
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: ['style-loader', 'css-loader', 'sass-loader'],
|
||||||
|
include: paths,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.extractCSS = paths => ({
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: ExtractTextPlugin.extract({
|
||||||
|
fallback: 'style-loader',
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
minimize: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
plugins: () => [
|
||||||
|
autoprefixer({
|
||||||
|
browsers: [
|
||||||
|
'Chrome >= 46',
|
||||||
|
'Firefox ESR',
|
||||||
|
'Edge >= 12',
|
||||||
|
'Explorer >= 9',
|
||||||
|
'iOS >= 8',
|
||||||
|
'Safari >= 8',
|
||||||
|
'Android >= 4',
|
||||||
|
],
|
||||||
|
cascade: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'sass-loader',
|
||||||
|
options: {
|
||||||
|
outputStyle: 'expanded',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
include: paths,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ExtractTextPlugin({
|
||||||
|
filename: '[name].css',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.devServer = options => ({
|
||||||
|
devServer: {
|
||||||
|
contentBase: __dirname,
|
||||||
|
historyApiFallback: true,
|
||||||
|
hot: false,
|
||||||
|
inline: true,
|
||||||
|
stats: 'errors-only',
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
overlay: {
|
||||||
|
warnings: true,
|
||||||
|
errors: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.minify = () => ({
|
||||||
|
plugins: [
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
compress: {
|
||||||
|
warnings: false,
|
||||||
|
drop_console: true,
|
||||||
|
screw_ie8: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
comments: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.setFreeVariable = (key, value) => {
|
||||||
|
const env = {};
|
||||||
|
env[key] = JSON.stringify(value);
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [new webpack.DefinePlugin(env)],
|
||||||
|
};
|
||||||
|
};
|
5831
wp-content/plugins/audioigniter/player/yarn.lock
Normal file
5831
wp-content/plugins/audioigniter/player/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
185
wp-content/plugins/audioigniter/readme.txt
Normal file
185
wp-content/plugins/audioigniter/readme.txt
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
=== AudioIgniter Music Player ===
|
||||||
|
Contributors: cssigniterteam, anastis, silencerius, tsiger
|
||||||
|
Tags: audio, podcast, audio player, html5 player, mp3 player, music player, music, radio stream, radio player, sound player, player, podcast player
|
||||||
|
Requires at least: 5.0
|
||||||
|
Tested up to: 5.9
|
||||||
|
Stable tag: 1.7.3
|
||||||
|
License: GPLv2 or later
|
||||||
|
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
|
||||||
|
AudioIgniter lets you create music playlists and embed them in your WordPress posts, pages or custom post types and serve your audio content in style!
|
||||||
|
|
||||||
|
== Description ==
|
||||||
|
Looking for an MP3 music player? AudioIgniter lets you create music playlists and embed them in your WordPress posts, pages or custom post types. By using the standard WordPress media upload functionality, you can create new audio playlists in minutes. Oh, you can use AudioIgniter to stream your radio show too!
|
||||||
|
|
||||||
|
https://www.youtube.com/watch?v=AmRDYlVW_3M
|
||||||
|
|
||||||
|
Check out [the demo](https://www.cssigniter.com/preview/audioigniter/) now!
|
||||||
|
|
||||||
|
**Selling digital music?**
|
||||||
|
|
||||||
|
You can combine [AudioIgniter with WooCommerce to easily sell individual tracks](https://www.cssigniter.com/sell-individual-tracks-using-audioigniter-with-woocommerce/).
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
* Supports audio and radio streaming
|
||||||
|
* Unlimited playlists
|
||||||
|
* Unlimited tracks
|
||||||
|
* 100% Compatible with Elementor
|
||||||
|
* 100% Compatible with Visual Composer
|
||||||
|
* 100% Compatible with Gutenberg Block Editor
|
||||||
|
* Responsive layout
|
||||||
|
* Embed through shortcode
|
||||||
|
* Flexible settings per playlist
|
||||||
|
* Show/Hide track listing
|
||||||
|
* Show/Hide track numbers in tracklist
|
||||||
|
* Show numbers in reverse order
|
||||||
|
* Show/Hide track covers in playlist
|
||||||
|
* Show/Hide active track’s cover
|
||||||
|
* Show/Hide artist name
|
||||||
|
* Custom "Buy track" URL field
|
||||||
|
* Custom "Download" URL field
|
||||||
|
* "Full" or "Simple" player mode (Great for podcasts)
|
||||||
|
* Limit track listing height
|
||||||
|
* Repeat track listing option
|
||||||
|
* Maximum player width
|
||||||
|
* Automatic ID3 Tag extraction from MP3 files
|
||||||
|
* Heavily tested on the 150 most popular free themes on WordPress.org
|
||||||
|
|
||||||
|
**Supported Services:**
|
||||||
|
|
||||||
|
* Acast
|
||||||
|
* Amazon S3
|
||||||
|
* Anchor
|
||||||
|
* Art19
|
||||||
|
* AudioBoom
|
||||||
|
* Castbox
|
||||||
|
* Captivate
|
||||||
|
* Icecast
|
||||||
|
* Podbean
|
||||||
|
* Radiojar
|
||||||
|
* Shoutcast
|
||||||
|
* Speaker
|
||||||
|
* Stitcher
|
||||||
|
* Libsyn
|
||||||
|
|
||||||
|
**But wait, there's more!**
|
||||||
|
|
||||||
|
A [Pro version](https://www.cssigniter.com/plugins/audioigniter) is also available! Here's what you get if you decide to upgrade:
|
||||||
|
|
||||||
|
* Bulk upload functionality
|
||||||
|
* Rearrange tracks functionality
|
||||||
|
* Stop Tracks From Other Players (Multiple Players In One Page)
|
||||||
|
* Track skipping functionality (You can adjust the number of seconds)
|
||||||
|
* Playback rate (1x, 1.5x, 2x)
|
||||||
|
* Lyrics per track
|
||||||
|
* Individual Track Repeat Mode
|
||||||
|
* Non-continuous playback (Stop each track after playing)
|
||||||
|
* Optional customizable delay between tracks
|
||||||
|
* Shuffle playlist mode
|
||||||
|
* Starting track option
|
||||||
|
* Default track timer to countdown mode
|
||||||
|
* Fixed position player (Continuous play must be supported by your theme)
|
||||||
|
* Internal taxonomy for archiving purposes
|
||||||
|
* Customize the colors through the Customizer
|
||||||
|
* Custom block for the Gutenberg Block Editor (With the ability to change colors per player)
|
||||||
|
* Widget & Shortcode available
|
||||||
|
|
||||||
|
**PREMIUM SUPPORT**
|
||||||
|
You can expect the same level of support for both the free and pro version of our plugin. Average response time: 24 hours.
|
||||||
|
|
||||||
|
|
||||||
|
== Installation ==
|
||||||
|
1. Upload the plugin files to the `/wp-content/plugins/audioigniter` directory, or install the plugin through the WordPress plugins screen directly.
|
||||||
|
2. Activate the plugin through the "Plugins" screen in WordPress
|
||||||
|
3. In the WordPress admin dashboard you should see a new post type named "Playlists"
|
||||||
|
4. Navigate to the new Playlists post type and add your tracks!
|
||||||
|
|
||||||
|
== Screenshots ==
|
||||||
|
1. The AudioIgniter player
|
||||||
|
2. Managing your playlists via an intuitive and user friendly interface
|
||||||
|
3. Advanced player customization
|
||||||
|
|
||||||
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.7.3 =
|
||||||
|
* Top "Add Track" prepends a track, and bottom "Add Track" appends a track.
|
||||||
|
|
||||||
|
= 1.7.2 =
|
||||||
|
* Provide minimized and optimized stylesheet.
|
||||||
|
* Updated shortcode to support HTML classes via the class="" parameter.
|
||||||
|
|
||||||
|
= 1.7.1 =
|
||||||
|
* Added support for x3 playback rate.
|
||||||
|
* Added base support for user controlled shuffle button, in playlists that shuffle mode is enabled (Pro feature).
|
||||||
|
* Fixed a deprecation warning "Required parameter follows optional parameter" that would appear in PHP 8.
|
||||||
|
* Fixed spinner positioning on simple track listing.
|
||||||
|
* Introduced method AudioIgniter_Sanitizer::rgba_color()
|
||||||
|
|
||||||
|
= 1.7.0 =
|
||||||
|
* Added a loading spinner while the track is buffering.
|
||||||
|
* Developer note - Changed: Static property AudioIgniter::$version is now non-static and should be accessed as such.
|
||||||
|
|
||||||
|
= 1.6.3 =
|
||||||
|
* Fixed SVG appearance in TwentyTwenty theme.
|
||||||
|
|
||||||
|
= 1.6.2 =
|
||||||
|
* Fixed an issue where the download buttons would suggest an ugly filename consisting of the URL in a sanitized form.
|
||||||
|
|
||||||
|
= 1.6.1 =
|
||||||
|
* Fixed an issue where reverse track order would not work correctly under some particular option configurations.
|
||||||
|
|
||||||
|
= 1.6.0 =
|
||||||
|
* Change the layout of volume controls to be a bit more spacey.
|
||||||
|
* Hidden volume controls in mobile devices (improves player appearance in mobile).
|
||||||
|
|
||||||
|
= 1.5.1 =
|
||||||
|
* Minor performance improvements.
|
||||||
|
|
||||||
|
= 1.5.0 =
|
||||||
|
* Added new filters: 'aiStrings', 'audioigniter_get_playlist_data_attributes_array'.
|
||||||
|
* Added new actions: 'audioigniter_metabox_tracks_repeatable_track_fields_column_1', 'audioigniter_metabox_tracks_repeatable_track_fields_column_2', 'audioigniter_metabox_settings_group_player_track_listing_fields', 'audioigniter_metabox_settings_group_tracks_fields', 'audioigniter_metabox_settings_group_player_track_track_listing_repeat_fields'.
|
||||||
|
* Upgraded React / ReactDOM and dependencies to latest versions.
|
||||||
|
* Fixed issue with viewing buy/download buttons vs track repeat button.
|
||||||
|
* Fixed some untranslatable strings.
|
||||||
|
* Added base support for per-track Lyrics (requires Pro version).
|
||||||
|
* Added base support for single track looping (requires Pro version).
|
||||||
|
* Rearranged track listing settings layout.
|
||||||
|
|
||||||
|
= 1.4.2 =
|
||||||
|
* Accessibility enhancements.
|
||||||
|
|
||||||
|
= 1.4.1 =
|
||||||
|
* Developer enhancements.
|
||||||
|
|
||||||
|
= 1.4.0 =
|
||||||
|
* Code changes to accommodate a new player type, Global Footer Player, available in AudioIgniter Pro.
|
||||||
|
* Introduced AudioIgniter::is_playlist() for easier playlist ID validation.
|
||||||
|
* Added some translators comments.
|
||||||
|
|
||||||
|
= 1.3.0 =
|
||||||
|
* Added a new player type! From now on you can use a simpler playlist type if you don't need the full fledged player.
|
||||||
|
* Player type can now be selected via a simple dropdown.
|
||||||
|
* Updated some settings' labels to reflect the setting's function more accurately.
|
||||||
|
* Fixed an issue which prevented the player from working in IE11 sometimes.
|
||||||
|
* Fixed an issue where reversing a playlist would result in playing the incorrect tracks.
|
||||||
|
* Dropped IE9 support.
|
||||||
|
|
||||||
|
= 1.2.0 =
|
||||||
|
* Added support for initial volume setting.
|
||||||
|
* Show the tracklist toggle button when the tracklist is hidden by default.
|
||||||
|
* Added support for downloading tracks.
|
||||||
|
* Fixed issue where tracklist wouldn't display when there was only one track.
|
||||||
|
|
||||||
|
= 1.1.0 =
|
||||||
|
* Updated CSSIgniter links to https
|
||||||
|
* Added a button to enable repeating the playlist. Added admin option for the default state of the repeat button.
|
||||||
|
* Fixed a bug where the playlist would not get shown if it contained only one track.
|
||||||
|
* Added option to choose whether track links should open in a new window or not.
|
||||||
|
|
||||||
|
= 1.0.1 =
|
||||||
|
* Stop looping over the tracklist when the player finishes playing the last track.
|
||||||
|
* A couple of strings could not be translated.
|
||||||
|
|
||||||
|
= 1.0.0 =
|
||||||
|
* Initial release.
|
11
wp-content/plugins/audioigniter/uninstall.php
Normal file
11
wp-content/plugins/audioigniter/uninstall.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ||
|
||||||
|
! WP_UNINSTALL_PLUGIN ||
|
||||||
|
dirname( WP_UNINSTALL_PLUGIN ) != dirname( plugin_basename( __FILE__ ) )
|
||||||
|
) {
|
||||||
|
status_header( 404 );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
flush_rewrite_rules();
|
Loading…
Reference in New Issue
Block a user