diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php
index e273ff9a7..ce8a2bc3f 100644
--- a/e107_handlers/e107_class.php
+++ b/e107_handlers/e107_class.php
@@ -2290,10 +2290,15 @@ class e107
* - In case of 'info': An associative array containing registered information for all libraries, the registered
* information for the library specified by $name, or FALSE if the library $name is not registered.
*/
- public static function library($action = '', $library = null, $variant = null)
+ public static function library($action = '', $library = null, $variant = null, $types = null)
{
$libraryHandler = self::getLibrary();
+ if(empty($types))
+ {
+ $types = array('js', 'css');
+ }
+
switch($action)
{
case 'detect':
@@ -2317,7 +2322,7 @@ class e107
if(!empty($variant) && !empty($lib['variants'][$variant]['installed']))
{
// Load CDN version with the variant.
- return $libraryHandler->load('cdn.' . $library, $variant);
+ return $libraryHandler->load('cdn.' . $library, $variant, $types);
}
// If CDN version is available, but no variant is specified,
@@ -2325,11 +2330,11 @@ class e107
if(empty($variant) && $debug && !empty($lib['variants']['dev']['installed']))
{
// Load CDN version with 'debug' variant.
- return $libraryHandler->load('cdn.' . $library, 'dev');
+ return $libraryHandler->load('cdn.' . $library, 'dev', $types);
}
// Load CDN version without variant.
- return $libraryHandler->load('cdn.' . $library, $variant);
+ return $libraryHandler->load('cdn.' . $library, $variant, $types);
}
}
@@ -2342,11 +2347,11 @@ class e107
if($lib && !empty($lib['variants']['dev']['installed']))
{
// Load library with 'debug' variant.
- return $libraryHandler->load($library, 'dev');
+ return $libraryHandler->load($library, 'dev', $types);
}
}
- return $libraryHandler->load($library, $variant);
+ return $libraryHandler->load($library, $variant, $types);
break;
case 'info':
@@ -2356,27 +2361,22 @@ class e107
case 'files':
$info = $libraryHandler->info($library);
$ret = [];
- if(!empty($info['files']['css']))
+
+ foreach($types as $t)
{
- foreach($info['files']['css'] as $path => $other)
+ if(!empty($info['files'][$t]))
{
- $file = $info['library_path'].'/';
- $file .= !empty($info['path']) ? $info['path'].'/' : '';
- $file .= $path;
- $ret['css'][] = $file;
- }
- }
- if(!empty($info['files']['js']))
- {
- foreach($info['files']['js'] as $path => $other)
- {
- $file = $info['library_path'].'/';
- $file .= !empty($info['path']) ? $info['path'].'/' : '';
- $file .= $path;
- $ret['js'][] = $file;
+ foreach($info['files'][$t] as $path => $other)
+ {
+ $file = $info['library_path'].'/';
+ $file .= !empty($info['path']) ? $info['path'].'/' : '';
+ $file .= $path;
+ $ret[$t][] = $file;
+ }
}
}
+
return $ret;
break;
@@ -2480,6 +2480,7 @@ class e107
{
$jshandler->requireCoreLib($data);
}
+
break;
case 'bootstrap': //TODO Eventually add own method and render for bootstrap.
@@ -2539,6 +2540,7 @@ class e107
{
$jshandler->headerFile($data, 5, $pre, $post);
}
+
break;
case 'footer':
diff --git a/e107_handlers/e_parse_class.php b/e107_handlers/e_parse_class.php
index f93a1a69b..a79fc12f8 100644
--- a/e107_handlers/e_parse_class.php
+++ b/e107_handlers/e_parse_class.php
@@ -3888,7 +3888,7 @@ class e_parse
$idAtt = (!empty($parm['id'])) ? "id='" . $parm['id'] . "' " : '';
$style = (!empty($parm['style'])) ? "style='" . $parm['style'] . "' " : '';
$class = (!empty($parm['class'])) ? $parm['class'] . ' ' : '';
- $placeholder = isset($parm['placeholder']) ? $parm['placeholder'] : '';
+ $placeholder = isset($parm['placeholder']) ? $parm['placeholder'] : '';
$title = (!empty($parm['title'])) ? " title='" . $this->toAttribute($parm['title']) . "' " : '';
$text = '<' . $tag . " {$idAtt}class='" . $class . $prefix . $id . $size . $spin . $rotate . $fixedW . "' " . $style . $title . '>' . $placeholder . '' . $tag . '>';
diff --git a/e107_handlers/library_manager.php b/e107_handlers/library_manager.php
index e610d4dd4..612981ae0 100755
--- a/e107_handlers/library_manager.php
+++ b/e107_handlers/library_manager.php
@@ -936,15 +936,25 @@ class core_library
'vendor_url' => 'https://fontawesome.com/',
'version_arguments' => array(
'file' => 'css/all.css',
- 'pattern' => '/(\d\.\d\.\d+)/',
- 'lines' => 10,
+ 'pattern' => '/(\d\.\d*\.\d+)/',
+ 'lines' => 2,
),
'files' => array(
+ 'js' => array(
+ 'js/all.min.js' => array(
+ 'zone' => 2,
+ 'type' => 'footer',
+ ),
+ 'js/v4-shims.min.js' => array(
+ 'zone' => 2,
+ 'type' => 'footer',
+ ),
+ ),
'css' => array(
- 'css/all.css' => array(
+ 'css/all.min.css' => array(
'zone' => 2,
),
- 'css/v4-shims.css' => array(
+ 'css/v4-shims.min.css' => array(
'zone' => 2,
),
),
@@ -962,9 +972,9 @@ class core_library
),
),*/
// Override library path to CDN.
- 'library_path' => 'https://use.fontawesome.com/releases',
- 'path' => 'v5.8.1',
- 'version' => '5.8.1',
+ 'library_path' => 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0',
+ 'path' => '',
+ 'version' => '5.14.0',
);
// Font-Awesome (local).
@@ -977,6 +987,16 @@ class core_library
'lines' => 3,
),
'files' => array(
+ 'js' => array(
+ 'js/all.min.js' => array(
+ 'zone' => 2,
+ 'type' => 'footer',
+ ),
+ 'js/v4-shims.min.js' => array(
+ 'zone' => 2,
+ 'type' => 'footer',
+ ),
+ ),
'css' => array(
'css/all.min.css' => array(
'zone' => 2,
@@ -1445,7 +1465,7 @@ class e_library_manager
* - loaded: Either the amount of library files that have been loaded, or FALSE if the library could not be
* loaded. See MYPLUGIN_library::config() for more information.
*/
- public function load($name, $variant = null)
+ public function load($name, $variant = null, $types = ['js', 'css'])
{
// Re-use the statically cached value to save memory.
@@ -1511,7 +1531,7 @@ class e_library_manager
$this->invoke('pre_load', $library);
// Load all the files associated with the library.
- $library['loaded'] = $this->loadFiles($library);
+ $library['loaded'] = $this->loadFiles($library, $types);
// TODO:
// Invoke callbacks in the 'post_load' group.
@@ -1996,7 +2016,7 @@ class e_library_manager
* @return int
* The number of loaded files.
*/
- private function loadFiles($library)
+ private function loadFiles($library, $types = array('js', 'css'))
{
$siteTheme = e107::getPref('sitetheme');
$adminTheme = e107::getPref('admintheme');
@@ -2048,7 +2068,7 @@ class e_library_manager
$count = 0;
// Load both the JavaScript and the CSS files.
- foreach(array('js', 'css') as $type)
+ foreach($types as $type)
{
if(!empty($library['files'][$type]))
{
diff --git a/e107_handlers/theme_handler.php b/e107_handlers/theme_handler.php
index de938ec11..c9c4be2b5 100644
--- a/e107_handlers/theme_handler.php
+++ b/e107_handlers/theme_handler.php
@@ -235,6 +235,13 @@ class e_theme
{
foreach($data as $name => $var)
{
+ $files = !empty($var['files']) ? array($var['files']) : null;
+
+ if(strpos($name,'fontawesome')!==false && ($files === null))
+ {
+ $files = array('css');
+ }
+
if($name === 'bootstrap' && ((int) $var['version'] > 3)) // quick fix.
{
$name .= (string) $var['version'];
@@ -244,7 +251,8 @@ class e_theme
$name .= (string) $var['version'];
}
- $ret[] = e107::library('files', $name);
+
+ $ret[] = e107::library('files', $name, null, $files);
}
}
elseif($type === 'css')
@@ -315,9 +323,19 @@ class e_theme
{
define('FONTAWESOME', (int) $library['version']);
}
+
+ if(empty($library['files'])) // force CSS only for backward compatibility.
+ {
+ $library['files'] = 'css';
+ }
}
- e107::library('load', $name);
+ // support for 'files' attribute in theme.xml library tag. Specific which part of library to load. js || css or leave empty for both.
+ /* @see theme.xml */
+
+ $files = !empty($library['files']) ? array($library['files']) : ['js', 'css'];
+
+ e107::library('load', $name, null, $files);
e107::library('preload', $name);
$loaded[] = $name;
@@ -1097,13 +1115,17 @@ class e_theme
{
$vars['css'] = array();
- foreach($vars['libraries']['library'] as $val)
+ foreach($vars['libraries']['library'] as $c=>$val)
{
- $vars['library'][] = array(
+ foreach($val['@attributes'] as $k=>$v)
+ {
+ $vars['library'][$c][$k] = $v;
+ }
+ /* $vars['library'][] = array(
'name' => $val['@attributes']['name'],
'version' => varset($val['@attributes']['version']),
'scope' => varset($val['@attributes']['scope'], 'front'),
- );
+ );*/
}
unset($vars['libraries']);
diff --git a/e107_tests/tests/unit/e107Test.php b/e107_tests/tests/unit/e107Test.php
index fdcd224a8..4776b95f3 100644
--- a/e107_tests/tests/unit/e107Test.php
+++ b/e107_tests/tests/unit/e107Test.php
@@ -735,25 +735,48 @@ class e107Test extends \Codeception\Test\Unit
$e107 = $this->e107;
$expected = array (
+ 'js' =>
+ array (
+ 0 => '{e_WEB}lib/font-awesome/5/js/all.min.js',
+ 1 => '{e_WEB}lib/font-awesome/5/js/v4-shims.min.js',
+ ),
'css' =>
array (
0 => '{e_WEB}lib/font-awesome/5/css/all.min.css',
1 => '{e_WEB}lib/font-awesome/5/css/v4-shims.min.css',
),
+
);
$result = $e107::library('files', 'fontawesome5');
$this->assertSame($expected, $result);
+
+ // -------------------
+
+ // Expecting only the JS portion of the library.
$expected = array (
- 'css' =>
+ 'js' =>
array (
- 0 => '{e_WEB}lib/bootstrap/5/css/bootstrap.min.css',
+ 0 => '{e_WEB}lib/font-awesome/5/js/all.min.js',
+ 1 => '{e_WEB}lib/font-awesome/5/js/v4-shims.min.js',
),
+ );
+
+ $result = $e107::library('files', 'fontawesome5', null, ['js']);
+ $this->assertSame($expected, $result);
+
+ // -------------------
+ $expected = array (
'js' =>
array (
0 => '{e_WEB}lib/bootstrap/5/js/bootstrap.bundle.min.js',
),
+ 'css' =>
+ array (
+ 0 => '{e_WEB}lib/bootstrap/5/css/bootstrap.min.css',
+ ),
+
);
$result = $e107::library('files', 'bootstrap5');
diff --git a/e107_tests/tests/unit/e_formTest.php b/e107_tests/tests/unit/e_formTest.php
index e94c0cd56..4bf28a446 100644
--- a/e107_tests/tests/unit/e_formTest.php
+++ b/e107_tests/tests/unit/e_formTest.php
@@ -1126,7 +1126,7 @@ class e_formTest extends \Codeception\Test\Unit
'hidden_001' => "",
// 'method_001' => 'custom-value',
'language_001' => "",
- 'media_001' => "
",
+ 'media_001' => "",
// 'lanlist_001' => 'German', // only works with multiple languages installed.
);
diff --git a/e107_tests/tests/unit/e_parseTest.php b/e107_tests/tests/unit/e_parseTest.php
index 76f5b9e68..579466459 100644
--- a/e107_tests/tests/unit/e_parseTest.php
+++ b/e107_tests/tests/unit/e_parseTest.php
@@ -2035,24 +2035,24 @@ while($row = $sql->fetch())
$this->tp->setFontAwesome(4);
$result = $this->tp->toGlyph('fa-envelope.glyph');
- $expected = " ";
+ $expected = " ";
$this->assertEquals($expected,$result);
$this->tp->setFontAwesome(5);
$result = $this->tp->toGlyph('fa-mailchimp');
- $expected = " ";
+ $expected = " ";
$this->assertEquals($expected, $result);
$this->tp->setFontAwesome(4);
$result = $this->tp->toGlyph('fab-mailchimp'); // spefific call
- $expected = " ";
+ $expected = " ";
$this->assertEquals($expected, $result);
$result = $this->tp->toGlyph('fas-camera'); // spefific call
- $this->assertSame( " ", $result);
+ $this->assertSame( " ", $result);
// test core, shims and old identifiers with FontAwesome 5 installed.
$this->tp->setFontAwesome(5);
@@ -2060,17 +2060,17 @@ while($row = $sql->fetch())
$tests = array(
'e-database-16' => "",
'e-database-32' => "",
- 'fa-sun-o' => " ",
- 'fa-comments-o' => " ",
- 'fa-file-text-o' => " ",
- 'fa-bank' => " ",
- 'fa-warning' => " ",
- 'glyphicon-star' => " ",
- 'icon-star' => " ",
- 'floppy-disk' => " ",
- 'icon-user' => " ",
- 'user' => " ",
- 'flag' => " ",
+ 'fa-sun-o' => " ",
+ 'fa-comments-o' => " ",
+ 'fa-file-text-o' => " ",
+ 'fa-bank' => " ",
+ 'fa-warning' => " ",
+ 'glyphicon-star' => " ",
+ 'icon-star' => " ",
+ 'floppy-disk' => " ",
+ 'icon-user' => " ",
+ 'user' => " ",
+ 'flag' => " ",
'fa-' => null,
);
@@ -2088,17 +2088,17 @@ while($row = $sql->fetch())
$tests = array(
'e-database-16' => "",
'e-database-32' => "",
- 'fa-sun-o' => " ",
- 'fa-comments-o' => " ",
- 'fa-file-text-o' => " ",
- 'fa-bank' => " ",
- 'fa-warning' => " ",
- 'glyphicon-star' => " ",
- 'icon-star' => " ",
- 'floppy-disk' => " ",
- 'icon-user' => " ",
- 'user' => " ",
- 'flag' => " ",
+ 'fa-sun-o' => " ",
+ 'fa-comments-o' => " ",
+ 'fa-file-text-o' => " ",
+ 'fa-bank' => " ",
+ 'fa-warning' => " ",
+ 'glyphicon-star' => " ",
+ 'icon-star' => " ",
+ 'floppy-disk' => " ",
+ 'icon-user' => " ",
+ 'user' => " ",
+ 'flag' => " ",
'fa-' => null,
);
@@ -2118,7 +2118,7 @@ while($row = $sql->fetch())
{
$this->tp->setFontAwesome(5);
$result = $this->tp->toGlyph('fa-paypal.glyph');
- $this->assertSame(" ", $result);
+ $this->assertSame(" ", $result);
}
/*
public function testToBadge()
@@ -2448,7 +2448,7 @@ Your browser does not support the audio tag.
// -----
$result = $tp->makeClickable($email, 'email', array('sub' => 'fa-envelope.glyph'));
- $this->assertStringContainsString("fa-envelope' >", $result);
+ $this->assertStringContainsString("fa-envelope' >", $result);
// links standard.
$tests = array(
diff --git a/e107_tests/tests/unit/e_parse_shortcodeTest.php b/e107_tests/tests/unit/e_parse_shortcodeTest.php
index b19373b8c..d9ea56d36 100644
--- a/e107_tests/tests/unit/e_parse_shortcodeTest.php
+++ b/e107_tests/tests/unit/e_parse_shortcodeTest.php
@@ -513,7 +513,7 @@ class e_parse_shortcodeTest extends \Codeception\Test\Unit
'=extended' => 'Extended Body
',
),
'newscommentlink' => array(
- ': class=me' => "1, 'news_sef'=>'welcome-to-e107-me-again-x'])."'>"
+ ': class=me' => "1, 'news_sef'=>'welcome-to-e107-me-again-x'])."'>"
),
@@ -1726,7 +1726,7 @@ class e_parse_shortcodeTest extends \Codeception\Test\Unit
{
$tp = e107::getParser();
$result = $tp->parseTemplate('{GLYPH=fa-user}');
- $this->assertSame("", $result);
+ $this->assertSame("", $result);
}
diff --git a/e107_tests/tests/unit/e_themeTest.php b/e107_tests/tests/unit/e_themeTest.php
index 95671de03..43bfd3579 100644
--- a/e107_tests/tests/unit/e_themeTest.php
+++ b/e107_tests/tests/unit/e_themeTest.php
@@ -86,7 +86,7 @@ class e_themeTest extends \Codeception\Test\Unit
'bootstrap.editable' =>
array(
'name' => 'bootstrap.editable',
- 'version' => '',
+ // 'version' => '',
),
)
),
@@ -108,7 +108,7 @@ class e_themeTest extends \Codeception\Test\Unit
'bootstrap.editable' =>
array(
'name' => 'bootstrap.editable',
- 'version' => '',
+ // 'version' => '',
),
)
),
@@ -256,14 +256,15 @@ class e_themeTest extends \Codeception\Test\Unit
$expected = array(
0 =>
array(
+ 'js' =>
+ array(
+ 0 => '{e_WEB}lib/bootstrap/3/js/bootstrap.min.js',
+ ),
'css' =>
array(
0 => '{e_WEB}lib/bootstrap/3/css/bootstrap.min.css',
),
- 'js' =>
- array(
- 0 => '{e_WEB}lib/bootstrap/3/js/bootstrap.min.js',
- ),
+
),
1 =>
array(
@@ -272,6 +273,7 @@ class e_themeTest extends \Codeception\Test\Unit
0 => '{e_WEB}lib/font-awesome/5/css/all.min.css',
1 => '{e_WEB}lib/font-awesome/5/css/v4-shims.min.css',
),
+
),
);
@@ -283,14 +285,15 @@ class e_themeTest extends \Codeception\Test\Unit
$expected = array(
0 =>
array(
+ 'js' =>
+ array(
+ 0 => '{e_WEB}lib/bootstrap/3/js/bootstrap.min.js',
+ ),
'css' =>
array(
0 => '{e_WEB}lib/bootstrap/3/css/bootstrap.min.css',
),
- 'js' =>
- array(
- 0 => '{e_WEB}lib/bootstrap/3/js/bootstrap.min.js',
- ),
+
),
1 =>
array(
@@ -299,6 +302,7 @@ class e_themeTest extends \Codeception\Test\Unit
0 => '{e_WEB}lib/font-awesome/5/css/all.min.css',
1 => '{e_WEB}lib/font-awesome/5/css/v4-shims.min.css',
),
+
),
);
@@ -315,6 +319,42 @@ class e_themeTest extends \Codeception\Test\Unit
// $result = e107::getTheme('bootstrap5')->getThemeFiles('css', 'wysiwyg');
+ /** Expecting bootstrap 5 files fontawesome 5 (js only) */
+
+ $expected = array (
+ 0 =>
+ array (
+ 'js' =>
+ array (
+ 0 => '{e_WEB}lib/bootstrap/5/js/bootstrap.bundle.min.js',
+ ),
+ 'css' =>
+ array (
+ 0 => '{e_WEB}lib/bootstrap/5/css/bootstrap.min.css',
+ ),
+
+ ),
+ 1 =>
+ array (
+ 'js' =>
+ array (
+ 0 => '{e_WEB}lib/font-awesome/5/js/all.min.js',
+ 1 => '{e_WEB}lib/font-awesome/5/js/v4-shims.min.js',
+ ),
+ ),
+ 2 =>
+ array (
+ 'css' =>
+ array (
+ 0 => '{e_WEB}lib/animate.css/animate.min.css',
+ ),
+ ),
+ );
+
+
+ $result = e107::getTheme('bootstrap5')->getThemeFiles('library', 'front');
+ $this->assertSame($expected, $result);
+
}
@@ -337,6 +377,11 @@ class e_themeTest extends \Codeception\Test\Unit
'scope' => 'front',
'expected' => ['bootstrap', 'fontawesome']
),
+ 3 => array(
+ 'theme' => 'bootstrap5',
+ 'scope' => 'front',
+ 'expected' => ['bootstrap5', 'fontawesome5', 'animate.css']
+ ),
);
@@ -374,7 +419,7 @@ class e_themeTest extends \Codeception\Test\Unit
2 =>
array(
'name' => 'bootstrap.editable',
- 'version' => '',
+ // 'version' => '',
'scope' => 'admin',
),
)
@@ -398,7 +443,7 @@ class e_themeTest extends \Codeception\Test\Unit
2 =>
array(
'name' => 'bootstrap.editable',
- 'version' => '',
+ // 'version' => '',
'scope' => 'admin',
),
)
@@ -579,7 +624,7 @@ class e_themeTest extends \Codeception\Test\Unit
2 =>
array (
'name' => 'bootstrap.editable',
- 'version' => '',
+ // 'version' => '',
'scope' => 'admin',
),
)
@@ -598,10 +643,11 @@ class e_themeTest extends \Codeception\Test\Unit
'name' => 'fontawesome',
'version' => '5',
'scope' => 'front',
+ 'files' => 'js',
),
2 => array (
'name' => 'animate.css',
- 'version' => '',
+ // 'version' => '',
'scope' => 'front',
)
),
diff --git a/e107_tests/tests/unit/themeHandlerTest.php b/e107_tests/tests/unit/themeHandlerTest.php
index 60a84309f..24dcb8eb1 100644
--- a/e107_tests/tests/unit/themeHandlerTest.php
+++ b/e107_tests/tests/unit/themeHandlerTest.php
@@ -132,12 +132,9 @@
{
}
+*/
- public function testParse_theme_xml()
- {
-
- }
-
+/*
public function testThemeUpload()
{
diff --git a/e107_themes/bootstrap5/theme.xml b/e107_themes/bootstrap5/theme.xml
index e54ec4ec9..a0254c9bf 100644
--- a/e107_themes/bootstrap5/theme.xml
+++ b/e107_themes/bootstrap5/theme.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/e107_web/lib/font-awesome/5/css/svg-with-js.min.css b/e107_web/lib/font-awesome/5/css/svg-with-js.min.css
new file mode 100644
index 000000000..8eea9648d
--- /dev/null
+++ b/e107_web/lib/font-awesome/5/css/svg-with-js.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
+.svg-inline--fa,svg:not(:root).svg-inline--fa{overflow:visible}.svg-inline--fa{display:inline-block;font-size:inherit;height:1em;vertical-align:-.125em}.svg-inline--fa.fa-lg{vertical-align:-.225em}.svg-inline--fa.fa-w-1{width:.0625em}.svg-inline--fa.fa-w-2{width:.125em}.svg-inline--fa.fa-w-3{width:.1875em}.svg-inline--fa.fa-w-4{width:.25em}.svg-inline--fa.fa-w-5{width:.3125em}.svg-inline--fa.fa-w-6{width:.375em}.svg-inline--fa.fa-w-7{width:.4375em}.svg-inline--fa.fa-w-8{width:.5em}.svg-inline--fa.fa-w-9{width:.5625em}.svg-inline--fa.fa-w-10{width:.625em}.svg-inline--fa.fa-w-11{width:.6875em}.svg-inline--fa.fa-w-12{width:.75em}.svg-inline--fa.fa-w-13{width:.8125em}.svg-inline--fa.fa-w-14{width:.875em}.svg-inline--fa.fa-w-15{width:.9375em}.svg-inline--fa.fa-w-16{width:1em}.svg-inline--fa.fa-w-17{width:1.0625em}.svg-inline--fa.fa-w-18{width:1.125em}.svg-inline--fa.fa-w-19{width:1.1875em}.svg-inline--fa.fa-w-20{width:1.25em}.svg-inline--fa.fa-pull-left{margin-right:.3em;width:auto}.svg-inline--fa.fa-pull-right{margin-left:.3em;width:auto}.svg-inline--fa.fa-border{height:1.5em}.svg-inline--fa.fa-li{width:2em}.svg-inline--fa.fa-fw{width:1.25em}.fa-layers svg.svg-inline--fa{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:1em}.fa-layers svg.svg-inline--fa{-webkit-transform-origin:center center;transform-origin:center center}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers-text{left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);-webkit-transform-origin:center center;transform-origin:center center}.fa-layers-counter{background-color:#ff253a;border-radius:1em;-webkit-box-sizing:border-box;box-sizing:border-box;color:#fff;height:1.5em;line-height:1;max-width:5em;min-width:1.5em;overflow:hidden;padding:.25em;right:0;text-overflow:ellipsis;top:0;-webkit-transform:scale(.25);transform:scale(.25);-webkit-transform-origin:top right;transform-origin:top right}.fa-layers-bottom-right{bottom:0;right:0;top:auto;-webkit-transform:scale(.25);transform:scale(.25);-webkit-transform-origin:bottom right;transform-origin:bottom right}.fa-layers-bottom-left{bottom:0;left:0;right:auto;top:auto;-webkit-transform:scale(.25);transform:scale(.25);-webkit-transform-origin:bottom left;transform-origin:bottom left}.fa-layers-top-right{right:0;top:0;-webkit-transform:scale(.25);transform:scale(.25);-webkit-transform-origin:top right;transform-origin:top right}.fa-layers-top-left{left:0;right:auto;top:0;-webkit-transform:scale(.25);transform:scale(.25);-webkit-transform-origin:top left;transform-origin:top left}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2.5em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.svg-inline--fa.fa-stack-1x{height:1em;width:1.25em}.svg-inline--fa.fa-stack-2x{height:2em;width:2.5em}.fa-inverse{color:#fff}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.svg-inline--fa .fa-primary{fill:var(--fa-primary-color,currentColor);opacity:1;opacity:var(--fa-primary-opacity,1)}.svg-inline--fa .fa-secondary{fill:var(--fa-secondary-color,currentColor)}.svg-inline--fa .fa-secondary,.svg-inline--fa.fa-swap-opacity .fa-primary{opacity:.4;opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-secondary{opacity:1;opacity:var(--fa-primary-opacity,1)}.svg-inline--fa mask .fa-primary,.svg-inline--fa mask .fa-secondary{fill:#000}.fad.fa-inverse{color:#fff}
\ No newline at end of file
diff --git a/e107_web/lib/font-awesome/5/js/all.min.js b/e107_web/lib/font-awesome/5/js/all.min.js
new file mode 100644
index 000000000..b9bb8db97
--- /dev/null
+++ b/e107_web/lib/font-awesome/5/js/all.min.js
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
+!function(){"use strict";var c={},l={};try{"undefined"!=typeof window&&(c=window),"undefined"!=typeof document&&(l=document)}catch(c){}var h=(c.navigator||{}).userAgent,z=void 0===h?"":h,a=c,v=l,m=(a.document,!!v.documentElement&&!!v.head&&"function"==typeof v.addEventListener&&v.createElement,~z.indexOf("MSIE")||z.indexOf("Trident/"),"___FONT_AWESOME___"),e=function(){try{return!0}catch(c){return!1}}();var s=a||{};s[m]||(s[m]={}),s[m].styles||(s[m].styles={}),s[m].hooks||(s[m].hooks={}),s[m].shims||(s[m].shims=[]);var t=s[m];function M(c,z){var l=(2>>0;h--;)l[h]=c[h];return l}function Ac(c){return c.classList?bc(c.classList):(c.getAttribute("class")||"").split(" ").filter(function(c){return c})}function gc(c,l){var h,z=l.split("-"),a=z[0],v=z.slice(1).join("-");return a!==c||""===v||(h=v,~T.indexOf(h))?null:v}function Sc(c){return"".concat(c).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function yc(h){return Object.keys(h||{}).reduce(function(c,l){return c+"".concat(l,": ").concat(h[l],";")},"")}function wc(c){return c.size!==Lc.size||c.x!==Lc.x||c.y!==Lc.y||c.rotate!==Lc.rotate||c.flipX||c.flipY}function Zc(c){var l=c.transform,h=c.containerWidth,z=c.iconWidth,a={transform:"translate(".concat(h/2," 256)")},v="translate(".concat(32*l.x,", ").concat(32*l.y,") "),m="scale(".concat(l.size/16*(l.flipX?-1:1),", ").concat(l.size/16*(l.flipY?-1:1),") "),e="rotate(".concat(l.rotate," 0 0)");return{outer:a,inner:{transform:"".concat(v," ").concat(m," ").concat(e)},path:{transform:"translate(".concat(z/2*-1," -256)")}}}var kc={x:0,y:0,width:"100%",height:"100%"};function xc(c){var l=!(1").concat(m.map(Jc).join(""),"").concat(l,">")}var $c=function(){};function cl(c){return"string"==typeof(c.getAttribute?c.getAttribute(cc):null)}var ll={replace:function(c){var l=c[0],h=c[1].map(function(c){return Jc(c)}).join("\n");if(l.parentNode&&l.outerHTML)l.outerHTML=h+(lc.keepOriginalSource&&"svg"!==l.tagName.toLowerCase()?"\x3c!-- ".concat(l.outerHTML," Font Awesome fontawesome.com --\x3e"):"");else if(l.parentNode){var z=document.createElement("span");l.parentNode.replaceChild(z,l),z.outerHTML=h}},nest:function(c){var l=c[0],h=c[1];if(~Ac(l).indexOf(lc.replacementClass))return ll.replace(c);var z=new RegExp("".concat(lc.familyPrefix,"-.*"));delete h[0].attributes.style,delete h[0].attributes.id;var a=h[0].attributes.class.split(" ").reduce(function(c,l){return l===lc.replacementClass||l.match(z)?c.toSvg.push(l):c.toNode.push(l),c},{toNode:[],toSvg:[]});h[0].attributes.class=a.toSvg.join(" ");var v=h.map(function(c){return Jc(c)}).join("\n");l.setAttribute("class",a.toNode.join(" ")),l.setAttribute(cc,""),l.innerHTML=v}};function hl(c){c()}function zl(h,c){var z="function"==typeof c?c:$c;if(0===h.length)z();else{var l=hl;lc.mutateApproach===y&&(l=o.requestAnimationFrame||hl),l(function(){var c=!0===lc.autoReplaceSvg?ll.replace:ll[lc.autoReplaceSvg]||ll.replace,l=_c.begin("mutate");h.map(c),l(),z()})}}var al=!1;function vl(){al=!1}var ml=null;function el(c){if(t&&lc.observeMutations){var a=c.treeCallback,v=c.nodeCallback,m=c.pseudoElementsCallback,l=c.observeMutationsRoot,h=void 0===l?C:l;ml=new t(function(c){al||bc(c).forEach(function(c){if("childList"===c.type&&0