Improved loading time on themes page
Now lazy loading theme updater with Ajax
This commit is contained in:
parent
452d94c578
commit
e81a3940f3
|
@ -0,0 +1,333 @@
|
||||||
|
@if(auth()->user()->role == 'admin')
|
||||||
|
@if(env('ENABLE_THEME_UPDATER') == 'true')
|
||||||
|
<style>
|
||||||
|
details {
|
||||||
|
width: 65%;
|
||||||
|
margin-left: 15px;
|
||||||
|
|
||||||
|
{
|
||||||
|
{
|
||||||
|
-- max-width: calc(100% - 20rem);
|
||||||
|
--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #78909C;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #ECEFF1;
|
||||||
|
color: #263238;
|
||||||
|
transition: background-color .15s;
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
opacity: .15;
|
||||||
|
box-shadow: 0 .25em .5em #263238;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity .2s;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[open] {
|
||||||
|
background-color: #FFF;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
font-size: 1.33em;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
width: .75em;
|
||||||
|
height: 2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
content: '';
|
||||||
|
background-color: currentColor;
|
||||||
|
text-align: right;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
transition: transform .2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
transform: translateY(-50%) rotate(90deg);
|
||||||
|
|
||||||
|
[open] & {
|
||||||
|
transform: translateY(-50%) rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table,
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.updatespin {
|
||||||
|
animation: upspin 1s linear infinite;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes upspin {
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<br><br><br>
|
||||||
|
<details>
|
||||||
|
<summary><i class="bi bi-caret-down-fill"></i> Theme updater </summary>
|
||||||
|
<div class="content" style="padding:10px;">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th style="width:85%;">Theme name:</th>
|
||||||
|
<th style="width: 15%;">Update status:</th>
|
||||||
|
<th>Version: </th>
|
||||||
|
</tr>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if ($handle = opendir('themes')) {
|
||||||
|
while (false !== ($entry = readdir($handle))) {
|
||||||
|
|
||||||
|
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
|
||||||
|
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
|
||||||
|
$pattern = '/Theme Version:.*/';
|
||||||
|
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
||||||
|
if(sizeof($matches) > 0) {
|
||||||
|
$verNr = substr($matches[0][0],15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$themeVe = NULL;
|
||||||
|
if(!isset($verNr)){$verNr = "error";};
|
||||||
|
|
||||||
|
if ($entry != "." && $entry != "..") {
|
||||||
|
echo '<tr>';
|
||||||
|
echo '<th>'; print_r(ucfirst($entry));
|
||||||
|
echo '</th>';
|
||||||
|
echo '<th><center>';
|
||||||
|
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
|
||||||
|
if(!strpos(file_get_contents(base_path('themes') . '/' . $entry . '/readme.md'), 'Source code:')){$hasSource = false;}else{
|
||||||
|
$hasSource = true;
|
||||||
|
|
||||||
|
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
|
||||||
|
$pattern = '/Source code:.*/';
|
||||||
|
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
||||||
|
$sourceURL = substr($matches[0][0],13);
|
||||||
|
|
||||||
|
$replaced = str_replace("https://github.com/", "https://raw.githubusercontent.com/", trim($sourceURL));
|
||||||
|
$replaced = $replaced . "/main/readme.md";
|
||||||
|
|
||||||
|
if (strpos($sourceURL, 'github.com')){
|
||||||
|
|
||||||
|
ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0)');
|
||||||
|
try{
|
||||||
|
$textGit = file_get_contents($replaced);
|
||||||
|
$patternGit = '/Theme Version:.*/';
|
||||||
|
preg_match($patternGit, $textGit, $matches, PREG_OFFSET_CAPTURE);
|
||||||
|
$sourceURLGit = substr($matches[0][0],15);
|
||||||
|
$Vgitt = 'v' . $sourceURLGit;
|
||||||
|
$verNrv = 'v' . $verNr;
|
||||||
|
}catch(Exception $ex){
|
||||||
|
$themeVe = "error";
|
||||||
|
$Vgitt = NULL;
|
||||||
|
$verNrv = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(trim($Vgitt) > trim($verNrv)){
|
||||||
|
$updateAv = true;
|
||||||
|
$GLOBALS['updateAv'] = true;
|
||||||
|
} else {
|
||||||
|
$updateAv = false;
|
||||||
|
}
|
||||||
|
} else {$themeVe = "error";}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($themeVe == "error") {
|
||||||
|
echo '<img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Error!&color=red">';
|
||||||
|
} elseif ($hasSource == false) {
|
||||||
|
echo '<a href="https://littlelink-custom.com/themes.php" target="_blank"><img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Update manually&color=red"></a>';
|
||||||
|
} elseif($updateAv == true) {
|
||||||
|
echo '<img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Update available&color=yellow">';
|
||||||
|
} else {
|
||||||
|
echo '<img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Up to date&color=green">';
|
||||||
|
}
|
||||||
|
echo '</center></th>';
|
||||||
|
echo '<th>' . $verNr . '</th>';
|
||||||
|
echo '</tr>';}
|
||||||
|
}} ?>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<a href="{{url('update/theme')}}" onclick="updateicon()" class="mt-3 ml-3 btn btn-info row"><span id="updateicon" class=""><i class="bi bi-arrow-repeat"></i></span> Update all themes</a><br><br>
|
||||||
|
<script>
|
||||||
|
function updateicon() {
|
||||||
|
var element = document.getElementById("updateicon");
|
||||||
|
element.classList.add("updatespin");
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
try{ if($GLOBALS['updateAv'] == true) echo '<img style="padding-left:40px; padding-top:15px; scale: 1.5;" src="https://img.llc.ovh/static/v1?label=&message=A theme needs updating&color=brightgreen">';
|
||||||
|
}catch(Exception $ex){}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$('select[name=theme]').on('change', function() {
|
||||||
|
var s = $(this).data('base-url') + "?t=" + $(this).val();
|
||||||
|
$("#frPreview").prop('src', s);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
class Accordion {
|
||||||
|
constructor(el) {
|
||||||
|
// Store the <details> element
|
||||||
|
this.el = el;
|
||||||
|
// Store the <summary> element
|
||||||
|
this.summary = el.querySelector('summary');
|
||||||
|
// Store the <div class="content"> element
|
||||||
|
this.content = el.querySelector('.content');
|
||||||
|
|
||||||
|
// Store the animation object (so we can cancel it if needed)
|
||||||
|
this.animation = null;
|
||||||
|
// Store if the element is closing
|
||||||
|
this.isClosing = false;
|
||||||
|
// Store if the element is expanding
|
||||||
|
this.isExpanding = false;
|
||||||
|
// Detect user clicks on the summary element
|
||||||
|
this.summary.addEventListener('click', (e) => this.onClick(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(e) {
|
||||||
|
// Stop default behaviour from the browser
|
||||||
|
e.preventDefault();
|
||||||
|
// Add an overflow on the <details> to avoid content overflowing
|
||||||
|
this.el.style.overflow = 'hidden';
|
||||||
|
// Check if the element is being closed or is already closed
|
||||||
|
if (this.isClosing || !this.el.open) {
|
||||||
|
this.open();
|
||||||
|
// Check if the element is being openned or is already open
|
||||||
|
} else if (this.isExpanding || this.el.open) {
|
||||||
|
this.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shrink() {
|
||||||
|
// Set the element as "being closed"
|
||||||
|
this.isClosing = true;
|
||||||
|
|
||||||
|
// Store the current height of the element
|
||||||
|
const startHeight = `${this.el.offsetHeight}px`;
|
||||||
|
// Calculate the height of the summary
|
||||||
|
const endHeight = `${this.summary.offsetHeight}px`;
|
||||||
|
|
||||||
|
// If there is already an animation running
|
||||||
|
if (this.animation) {
|
||||||
|
// Cancel the current animation
|
||||||
|
this.animation.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a WAAPI animation
|
||||||
|
this.animation = this.el.animate({
|
||||||
|
// Set the keyframes from the startHeight to endHeight
|
||||||
|
height: [startHeight, endHeight]
|
||||||
|
}, {
|
||||||
|
duration: 400
|
||||||
|
, easing: 'ease-out'
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the animation is complete, call onAnimationFinish()
|
||||||
|
this.animation.onfinish = () => this.onAnimationFinish(false);
|
||||||
|
// If the animation is cancelled, isClosing variable is set to false
|
||||||
|
this.animation.oncancel = () => this.isClosing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
// Apply a fixed height on the element
|
||||||
|
this.el.style.height = `${this.el.offsetHeight}px`;
|
||||||
|
// Force the [open] attribute on the details element
|
||||||
|
this.el.open = true;
|
||||||
|
// Wait for the next frame to call the expand function
|
||||||
|
window.requestAnimationFrame(() => this.expand());
|
||||||
|
}
|
||||||
|
|
||||||
|
expand() {
|
||||||
|
// Set the element as "being expanding"
|
||||||
|
this.isExpanding = true;
|
||||||
|
// Get the current fixed height of the element
|
||||||
|
const startHeight = `${this.el.offsetHeight}px`;
|
||||||
|
// Calculate the open height of the element (summary height + content height)
|
||||||
|
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
|
||||||
|
|
||||||
|
// If there is already an animation running
|
||||||
|
if (this.animation) {
|
||||||
|
// Cancel the current animation
|
||||||
|
this.animation.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a WAAPI animation
|
||||||
|
this.animation = this.el.animate({
|
||||||
|
// Set the keyframes from the startHeight to endHeight
|
||||||
|
height: [startHeight, endHeight]
|
||||||
|
}, {
|
||||||
|
duration: 400
|
||||||
|
, easing: 'ease-out'
|
||||||
|
});
|
||||||
|
// When the animation is complete, call onAnimationFinish()
|
||||||
|
this.animation.onfinish = () => this.onAnimationFinish(true);
|
||||||
|
// If the animation is cancelled, isExpanding variable is set to false
|
||||||
|
this.animation.oncancel = () => this.isExpanding = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onAnimationFinish(open) {
|
||||||
|
// Set the open attribute based on the parameter
|
||||||
|
this.el.open = open;
|
||||||
|
// Clear the stored animation
|
||||||
|
this.animation = null;
|
||||||
|
// Reset isClosing & isExpanding
|
||||||
|
this.isClosing = false;
|
||||||
|
this.isExpanding = false;
|
||||||
|
// Remove the overflow hidden and the fixed height
|
||||||
|
this.el.style.height = this.el.style.overflow = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('details').forEach((el) => {
|
||||||
|
new Accordion(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
@endif
|
|
@ -15,29 +15,42 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
|
||||||
<div class="col-8 col-md-4">
|
<div class="col-8 col-md-4">
|
||||||
<select style="margin-left: 15px; margin-bottom: 20px;" class="form-control" name="theme" data-base-url="{{ url('') }}/@<?= Auth::user()->littlelink_name ?>">
|
<select id="theme-select" style="margin-left: 15px; margin-bottom: 20px;" class="form-control" name="theme" data-base-url="{{ url('') }}/@<?= Auth::user()->littlelink_name ?>">
|
||||||
|
<?php
|
||||||
<?php if ($handle = opendir('themes')) {
|
if ($handle = opendir('themes')) {
|
||||||
while (false !== ($entry = readdir($handle))) {
|
while (false !== ($entry = readdir($handle))) {
|
||||||
if ($entry != "." && $entry != "..") {
|
if ($entry != "." && $entry != "..") {
|
||||||
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
|
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
|
||||||
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
|
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
|
||||||
$pattern = '/Theme Name:.*/';
|
$pattern = '/Theme Name:.*/';
|
||||||
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
||||||
if(sizeof($matches) > 0) {
|
if(sizeof($matches) > 0) {
|
||||||
$themeName = substr($matches[0][0],12);
|
$themeName = substr($matches[0][0],12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($page->theme != $entry and isset($themeName)){
|
||||||
|
echo '<option value="'.$entry.'" data-image="'.url('themes/'.$entry.'/screenshot.png').'">'.$themeName.'</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if($page->theme != $entry and isset($themeName)){echo '<option value="'; print_r($entry); echo '">'; echo $themeName; echo '</option>'; }}}} ?>
|
|
||||||
<?php
|
|
||||||
if($page->theme != "default" and $page->theme != ""){
|
if($page->theme != "default" and $page->theme != ""){
|
||||||
if(file_exists(base_path('themes') . '/' . $page->theme . '/readme.md')){
|
if(file_exists(base_path('themes') . '/' . $page->theme . '/readme.md')){
|
||||||
$text = file_get_contents(base_path('themes') . '/' . $page->theme . '/readme.md');
|
$text = file_get_contents(base_path('themes') . '/' . $page->theme . '/readme.md');
|
||||||
$pattern = '/Theme Name:.*/';
|
$pattern = '/Theme Name:.*/';
|
||||||
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
||||||
$themeName = substr($matches[0][0],12);}
|
$themeName = substr($matches[0][0],12);
|
||||||
if(isset($themeName)){echo '<option value="'.$page->theme.'" selected>'.$themeName.'</option>';}} ?>
|
}
|
||||||
<?php echo '<option value="default"'; if($page->theme == "default" or $page->theme == ""){echo 'selected';} echo '>Default</option>'; ?>
|
echo '<option value="'.$page->theme.'" data-image="'.url('themes/'.$page->theme.'/screenshot.png').'" selected>'.$themeName.'</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<option value="default" data-image="'.url('themes/default/screenshot.png').'"';
|
||||||
|
if($page->theme == "default" or $page->theme == ""){
|
||||||
|
echo ' selected';
|
||||||
|
}
|
||||||
|
echo '>Default</option>';
|
||||||
|
?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -74,208 +87,8 @@
|
||||||
|
|
||||||
@if(auth()->user()->role == 'admin')
|
@if(auth()->user()->role == 'admin')
|
||||||
@if(env('ENABLE_THEME_UPDATER') == 'true')
|
@if(env('ENABLE_THEME_UPDATER') == 'true')
|
||||||
<style>
|
<div id="ajax-container" style="height: 123px;"></div>
|
||||||
details {
|
<div id="my-lazy-element"></div>
|
||||||
width: 65%;
|
|
||||||
margin-left: 15px;
|
|
||||||
|
|
||||||
{
|
|
||||||
{
|
|
||||||
-- max-width: calc(100% - 20rem);
|
|
||||||
--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
border: 1px solid #78909C;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #ECEFF1;
|
|
||||||
color: #263238;
|
|
||||||
transition: background-color .15s;
|
|
||||||
|
|
||||||
> :last-child {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
border-radius: inherit;
|
|
||||||
opacity: .15;
|
|
||||||
box-shadow: 0 .25em .5em #263238;
|
|
||||||
pointer-events: none;
|
|
||||||
transition: opacity .2s;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[open] {
|
|
||||||
background-color: #FFF;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
opacity: .6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
padding: 0.375rem 0.75rem;
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
font-size: 1.33em;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
width: .75em;
|
|
||||||
height: 2px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 0;
|
|
||||||
content: '';
|
|
||||||
background-color: currentColor;
|
|
||||||
text-align: right;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
transition: transform .2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
transform: translateY(-50%) rotate(90deg);
|
|
||||||
|
|
||||||
[open] & {
|
|
||||||
transform: translateY(-50%) rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table,
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.updatespin {
|
|
||||||
animation: upspin 1s linear infinite;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes upspin {
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<br><br><br>
|
|
||||||
<details>
|
|
||||||
<summary><i class="bi bi-caret-down-fill"></i> Theme updater </summary>
|
|
||||||
<div class="content" style="padding:10px;">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th style="width:85%;">Theme name:</th>
|
|
||||||
<th style="width: 15%;">Update status:</th>
|
|
||||||
<th>Version: </th>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
if ($handle = opendir('themes')) {
|
|
||||||
while (false !== ($entry = readdir($handle))) {
|
|
||||||
|
|
||||||
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
|
|
||||||
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
|
|
||||||
$pattern = '/Theme Version:.*/';
|
|
||||||
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
|
||||||
if(sizeof($matches) > 0) {
|
|
||||||
$verNr = substr($matches[0][0],15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$themeVe = NULL;
|
|
||||||
if(!isset($verNr)){$verNr = "error";};
|
|
||||||
|
|
||||||
if ($entry != "." && $entry != "..") {
|
|
||||||
echo '<tr>';
|
|
||||||
echo '<th>'; print_r(ucfirst($entry));
|
|
||||||
echo '</th>';
|
|
||||||
echo '<th><center>';
|
|
||||||
if(file_exists(base_path('themes') . '/' . $entry . '/readme.md')){
|
|
||||||
if(!strpos(file_get_contents(base_path('themes') . '/' . $entry . '/readme.md'), 'Source code:')){$hasSource = false;}else{
|
|
||||||
$hasSource = true;
|
|
||||||
|
|
||||||
$text = file_get_contents(base_path('themes') . '/' . $entry . '/readme.md');
|
|
||||||
$pattern = '/Source code:.*/';
|
|
||||||
preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
|
|
||||||
$sourceURL = substr($matches[0][0],13);
|
|
||||||
|
|
||||||
$replaced = str_replace("https://github.com/", "https://raw.githubusercontent.com/", trim($sourceURL));
|
|
||||||
$replaced = $replaced . "/main/readme.md";
|
|
||||||
|
|
||||||
if (strpos($sourceURL, 'github.com')){
|
|
||||||
|
|
||||||
ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0)');
|
|
||||||
try{
|
|
||||||
$textGit = file_get_contents($replaced);
|
|
||||||
$patternGit = '/Theme Version:.*/';
|
|
||||||
preg_match($patternGit, $textGit, $matches, PREG_OFFSET_CAPTURE);
|
|
||||||
$sourceURLGit = substr($matches[0][0],15);
|
|
||||||
$Vgitt = 'v' . $sourceURLGit;
|
|
||||||
$verNrv = 'v' . $verNr;
|
|
||||||
}catch(Exception $ex){
|
|
||||||
$themeVe = "error";
|
|
||||||
$Vgitt = NULL;
|
|
||||||
$verNrv = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(trim($Vgitt) > trim($verNrv)){
|
|
||||||
$updateAv = true;
|
|
||||||
$GLOBALS['updateAv'] = true;
|
|
||||||
} else {
|
|
||||||
$updateAv = false;
|
|
||||||
}
|
|
||||||
} else {$themeVe = "error";}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($themeVe == "error") {
|
|
||||||
echo '<img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Error!&color=red">';
|
|
||||||
} elseif ($hasSource == false) {
|
|
||||||
echo '<a href="https://littlelink-custom.com/themes.php" target="_blank"><img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Update manually&color=red"></a>';
|
|
||||||
} elseif($updateAv == true) {
|
|
||||||
echo '<img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Update available&color=yellow">';
|
|
||||||
} else {
|
|
||||||
echo '<img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Up to date&color=green">';
|
|
||||||
}
|
|
||||||
echo '</center></th>';
|
|
||||||
echo '<th>' . $verNr . '</th>';
|
|
||||||
echo '</tr>';}
|
|
||||||
}} ?>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<a href="{{url('update/theme')}}" onclick="updateicon()" class="mt-3 ml-3 btn btn-info row"><span id="updateicon" class=""><i class="bi bi-arrow-repeat"></i></span> Update all themes</a><br><br>
|
|
||||||
<script>
|
|
||||||
function updateicon() {
|
|
||||||
var element = document.getElementById("updateicon");
|
|
||||||
element.classList.add("updatespin");
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
try{ if($GLOBALS['updateAv'] == true) echo '<img style="padding-left:40px; padding-top:15px; scale: 1.5;" src="https://img.llc.ovh/static/v1?label=&message=A theme needs updating&color=brightgreen">';
|
|
||||||
}catch(Exception $ex){}
|
|
||||||
?>
|
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@ -338,141 +151,27 @@ try{ if($GLOBALS['updateAv'] == true) echo '<img style="padding-left:40px; paddi
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@push('sidebar-scripts')
|
|
||||||
<script>
|
|
||||||
$(function() {
|
|
||||||
$('select[name=theme]').on('change', function() {
|
|
||||||
var s = $(this).data('base-url') + "?t=" + $(this).val();
|
|
||||||
$("#frPreview").prop('src', s);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
class Accordion {
|
|
||||||
constructor(el) {
|
|
||||||
// Store the <details> element
|
|
||||||
this.el = el;
|
|
||||||
// Store the <summary> element
|
|
||||||
this.summary = el.querySelector('summary');
|
|
||||||
// Store the <div class="content"> element
|
|
||||||
this.content = el.querySelector('.content');
|
|
||||||
|
|
||||||
// Store the animation object (so we can cancel it if needed)
|
|
||||||
this.animation = null;
|
|
||||||
// Store if the element is closing
|
|
||||||
this.isClosing = false;
|
|
||||||
// Store if the element is expanding
|
|
||||||
this.isExpanding = false;
|
|
||||||
// Detect user clicks on the summary element
|
|
||||||
this.summary.addEventListener('click', (e) => this.onClick(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(e) {
|
|
||||||
// Stop default behaviour from the browser
|
|
||||||
e.preventDefault();
|
|
||||||
// Add an overflow on the <details> to avoid content overflowing
|
|
||||||
this.el.style.overflow = 'hidden';
|
|
||||||
// Check if the element is being closed or is already closed
|
|
||||||
if (this.isClosing || !this.el.open) {
|
|
||||||
this.open();
|
|
||||||
// Check if the element is being openned or is already open
|
|
||||||
} else if (this.isExpanding || this.el.open) {
|
|
||||||
this.shrink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shrink() {
|
|
||||||
// Set the element as "being closed"
|
|
||||||
this.isClosing = true;
|
|
||||||
|
|
||||||
// Store the current height of the element
|
|
||||||
const startHeight = `${this.el.offsetHeight}px`;
|
|
||||||
// Calculate the height of the summary
|
|
||||||
const endHeight = `${this.summary.offsetHeight}px`;
|
|
||||||
|
|
||||||
// If there is already an animation running
|
|
||||||
if (this.animation) {
|
|
||||||
// Cancel the current animation
|
|
||||||
this.animation.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a WAAPI animation
|
|
||||||
this.animation = this.el.animate({
|
|
||||||
// Set the keyframes from the startHeight to endHeight
|
|
||||||
height: [startHeight, endHeight]
|
|
||||||
}, {
|
|
||||||
duration: 400
|
|
||||||
, easing: 'ease-out'
|
|
||||||
});
|
|
||||||
|
|
||||||
// When the animation is complete, call onAnimationFinish()
|
|
||||||
this.animation.onfinish = () => this.onAnimationFinish(false);
|
|
||||||
// If the animation is cancelled, isClosing variable is set to false
|
|
||||||
this.animation.oncancel = () => this.isClosing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
open() {
|
|
||||||
// Apply a fixed height on the element
|
|
||||||
this.el.style.height = `${this.el.offsetHeight}px`;
|
|
||||||
// Force the [open] attribute on the details element
|
|
||||||
this.el.open = true;
|
|
||||||
// Wait for the next frame to call the expand function
|
|
||||||
window.requestAnimationFrame(() => this.expand());
|
|
||||||
}
|
|
||||||
|
|
||||||
expand() {
|
|
||||||
// Set the element as "being expanding"
|
|
||||||
this.isExpanding = true;
|
|
||||||
// Get the current fixed height of the element
|
|
||||||
const startHeight = `${this.el.offsetHeight}px`;
|
|
||||||
// Calculate the open height of the element (summary height + content height)
|
|
||||||
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
|
|
||||||
|
|
||||||
// If there is already an animation running
|
|
||||||
if (this.animation) {
|
|
||||||
// Cancel the current animation
|
|
||||||
this.animation.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a WAAPI animation
|
|
||||||
this.animation = this.el.animate({
|
|
||||||
// Set the keyframes from the startHeight to endHeight
|
|
||||||
height: [startHeight, endHeight]
|
|
||||||
}, {
|
|
||||||
duration: 400
|
|
||||||
, easing: 'ease-out'
|
|
||||||
});
|
|
||||||
// When the animation is complete, call onAnimationFinish()
|
|
||||||
this.animation.onfinish = () => this.onAnimationFinish(true);
|
|
||||||
// If the animation is cancelled, isExpanding variable is set to false
|
|
||||||
this.animation.oncancel = () => this.isExpanding = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onAnimationFinish(open) {
|
|
||||||
// Set the open attribute based on the parameter
|
|
||||||
this.el.open = open;
|
|
||||||
// Clear the stored animation
|
|
||||||
this.animation = null;
|
|
||||||
// Reset isClosing & isExpanding
|
|
||||||
this.isClosing = false;
|
|
||||||
this.isExpanding = false;
|
|
||||||
// Remove the overflow hidden and the fixed height
|
|
||||||
this.el.style.height = this.el.style.overflow = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll('details').forEach((el) => {
|
|
||||||
new Accordion(el);
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
@endpush
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
<script src="{{ asset('studio/external-dependencies/jquery-1.12.4.min.js') }}"></script>
|
<script src="{{ asset('studio/external-dependencies/jquery-1.12.4.min.js') }}"></script>
|
||||||
|
</section>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
var placeholder = $('#ajax-container');
|
||||||
|
var lazyElement = $('#my-lazy-element');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '../theme-updater',
|
||||||
|
success: function(response) {
|
||||||
|
placeholder.replaceWith(lazyElement);
|
||||||
|
|
||||||
|
lazyElement.html(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script type="text/javascript">$("iframe").load(function() { $("iframe").contents().find("a").each(function(index) { $(this).on("click", function(event) { event.preventDefault(); event.stopPropagation(); }); }); });</script>
|
<script type="text/javascript">$("iframe").load(function() { $("iframe").contents().find("a").each(function(index) { $(this).on("click", function(event) { event.preventDefault(); event.stopPropagation(); }); }); });</script>
|
||||||
@endsection
|
@endsection
|
|
@ -166,6 +166,7 @@ Route::group([
|
||||||
Route::get('/update/theme', [AdminController::class, 'updateThemes'])->name('updateThemes');
|
Route::get('/update/theme', [AdminController::class, 'updateThemes'])->name('updateThemes');
|
||||||
Route::get('/panel/config', [AdminController::class, 'showConfig'])->name('showConfig');
|
Route::get('/panel/config', [AdminController::class, 'showConfig'])->name('showConfig');
|
||||||
Route::post('/panel/config', [AdminController::class, 'editConfig'])->name('editConfig');
|
Route::post('/panel/config', [AdminController::class, 'editConfig'])->name('editConfig');
|
||||||
|
Route::get('/theme-updater', function () {return view('studio/theme-updater', []);});
|
||||||
Route::get('/update', function () {return view('update', []);});
|
Route::get('/update', function () {return view('update', []);});
|
||||||
Route::get('/backup', function () {return view('backup', []);});
|
Route::get('/backup', function () {return view('backup', []);});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue