mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-09 21:47:59 +08:00
💋 Sync 2026-10-16 23:55:30
This commit is contained in:
1 parent
76f10c2a74
commit
1849cc6156
254 files changed
+25484
-8840
No files matched your search
@@ -34,10 +34,6 @@
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// thanks for Jo-Philipp Wich <jow@openwrt.org>
|
||||
var luciLocation = {{ ctx.path }};
|
||||
</script>
|
||||
<script type="text/javascript">L.require('menu-argon')</script>
|
||||
{% include('themes/' + theme + '/page_scripts', { is_login: false }) %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -22,17 +22,14 @@
|
||||
const firmware_info = version.distname + ' ' + version.distversion + '-' + version.distrevision;
|
||||
%}
|
||||
<footer>
|
||||
<div title="{{ entityencode(luci_info + ' · ArgonTheme · ' + firmware_info, true) }}">
|
||||
<a class="luci-link" href="https://github.com/openwrt/luci" target="_blank" title="{{ entityencode(luci_info, true) }}"><span class="login-footer-detail">Powered by </span>{{ version.luciname }}<span class="login-footer-detail"> ({{ version.luciversion }})</span></a>
|
||||
<div class="login-footer-links">
|
||||
<a href="https://github.com/openwrt/luci" target="_blank" title="{{ entityencode(luci_info, true) }}">{{ split(version.luciname, ' (')[0] }}</a>
|
||||
<a href="https://github.com/jerrykuku/luci-theme-argon" target="_blank">ArgonTheme {# vPKG_VERSION #}</a>
|
||||
<a class="luci-link" href="{{ version.disturl }}" target="_blank" title="{{ entityencode(firmware_info, true) }}">{{ version.distname }} {{ version.distversion }}<span class="login-footer-detail">-{{ version.distrevision }}</span></a>
|
||||
</div>
|
||||
<a href="{{ version.disturl }}" target="_blank" title="{{ entityencode(firmware_info, true) }}">{{ firmware_info }}</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// thanks for Jo-Philipp Wich <jow@openwrt.org>
|
||||
var luciLocation = {{ ctx.path }};
|
||||
</script>
|
||||
{% include('themes/' + theme + '/page_scripts', { is_login: true }) %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,119 @@
|
||||
{#
|
||||
Argon is a clean HTML5 theme for LuCI. It is based on luci-theme-material Argon Template
|
||||
|
||||
luci-theme-argon
|
||||
Copyright 2020 Jerrykuku <jerrykuku@qq.com>
|
||||
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
https://github.com/jerrykuku/luci-theme-argon/issues
|
||||
|
||||
luci-theme-material:
|
||||
Copyright 2015 Lutty Yang <lutty@wcan.in>
|
||||
|
||||
Argon Theme
|
||||
https://demos.creative-tim.com/argon-dashboard/index.html
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-#}
|
||||
|
||||
{# Shared document head. Callers supply hostname and is_login explicitly. -#}
|
||||
{%
|
||||
import { access, lsdir, lstat } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
import { dark_palette, appearance_modes } from 'luci.argon-palette';
|
||||
|
||||
let cfg = cursor();
|
||||
http.prepare_content('text/html; charset=UTF-8');
|
||||
|
||||
//Custom settings
|
||||
function validColor(value, fallback) {
|
||||
return type(value) == 'string' && match(value, /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/) ? value : fallback;
|
||||
}
|
||||
|
||||
function validNumber(value, fallback, maximum) {
|
||||
return type(value) == 'string' && match(value, /^(0|[1-9][0-9]*)(\.[0-9]+)?$/) && +value <= maximum ? value : fallback;
|
||||
}
|
||||
|
||||
let mode = 'normal';
|
||||
let primary = '#5e72e4';
|
||||
let dark_primary = '#483d8b';
|
||||
let blur_radius = '10';
|
||||
let blur_radius_dark = '10';
|
||||
let blur_opacity = '0.5';
|
||||
let blur_opacity_dark = '0.5';
|
||||
const has_argon_config = access('/etc/config/argon');
|
||||
let dark_settings = {};
|
||||
if (has_argon_config) {
|
||||
for (let key in ['dark_primary_mode', 'dark_primary', 'dark_glass_mode', 'blur', 'blur_dark', 'transparency', 'transparency_dark'])
|
||||
dark_settings[key] = cfg.get_first('argon', 'global', key);
|
||||
primary = validColor(cfg.get_first('argon', 'global', 'primary'), primary);
|
||||
dark_primary = validColor(cfg.get_first('argon', 'global', 'dark_primary'), dark_primary);
|
||||
blur_radius = validNumber(cfg.get_first('argon', 'global', 'blur'), blur_radius, 100);
|
||||
blur_radius_dark = validNumber(cfg.get_first('argon', 'global', 'blur_dark'), blur_radius_dark, 100);
|
||||
blur_opacity = validNumber(cfg.get_first('argon', 'global', 'transparency'), blur_opacity, 1);
|
||||
blur_opacity_dark = validNumber(cfg.get_first('argon', 'global', 'transparency_dark'), blur_opacity_dark, 1);
|
||||
let configured_mode = cfg.get_first('argon', 'global', 'mode');
|
||||
if (configured_mode == 'normal' || configured_mode == 'dark' || configured_mode == 'light')
|
||||
mode = configured_mode;
|
||||
}
|
||||
const dark_modes = appearance_modes(dark_settings);
|
||||
const palette = dark_palette(dark_modes.color == 'custom' ? dark_primary : primary, dark_modes.color == 'custom');
|
||||
dark_primary = palette.primary;
|
||||
if (dark_modes.glass == 'inherit') {
|
||||
blur_radius_dark = blur_radius;
|
||||
blur_opacity_dark = blur_opacity;
|
||||
}
|
||||
const bar_color = mode == 'dark' ? dark_primary : primary;
|
||||
|
||||
-%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ dispatcher.lang }}" data-argon-mode="{{ mode }}" {% if (mode == 'dark'): %} data-darkmode="true"{% elif (mode == 'light'): %} data-darkmode="false"{% endif %}>
|
||||
|
||||
<head>
|
||||
{% if (!is_login): %}
|
||||
{%
|
||||
const icon_dir = '/www/luci-static/argon/icon/';
|
||||
const menu_icons = filter(lsdir(icon_dir) ?? [], name =>
|
||||
match(name, /^[a-zA-Z0-9_-]+\.svg$/) && lstat(icon_dir + name)?.type == 'file');
|
||||
%}
|
||||
<meta name="argon-menu-icons" content="{{ entityencode(join(' ', menu_icons), true) }}">
|
||||
{% endif %}
|
||||
{% include("themes/" + theme + "/head_meta", { hostname: hostname, node: node, bar_color: bar_color, media: media, mode: mode, primary: primary, dark_primary: dark_primary }) %}
|
||||
|
||||
<link rel="stylesheet" href="{{ media }}/css/cascade.css{# ?v=PKG_VERSION #}">
|
||||
<style title="text/css">
|
||||
:root {
|
||||
--light-primary: {{ primary }};
|
||||
--dark-primary: {{ dark_primary }};
|
||||
--dark-on-primary: {{ palette.on_primary }};
|
||||
--dark-link-color: {{ palette.link }};
|
||||
--dark-focus-ring: {{ palette.focus }};
|
||||
--dark-control-focus: {{ palette.control_focus }};
|
||||
--dark-progress-fill: {{ palette.progress }};
|
||||
--dark-control-ring: {{ palette.control_ring }};
|
||||
--dark-selected-bg: {{ palette.selected }};
|
||||
--dark-selected-hover-bg: {{ palette.hover }};
|
||||
--dark-accent-ring: {{ palette.ring }};
|
||||
--dark-accent-shadow: {{ palette.shadow }};
|
||||
--dark-checkmark-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23{{ substr(palette.on_primary, 1) }}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
|
||||
--blur-radius-light:{{ blur_radius }}px;
|
||||
--blur-opacity-light:{{ blur_opacity }};
|
||||
--blur-radius-dark:{{ blur_radius_dark }}px;
|
||||
--blur-opacity-dark:{{ blur_opacity_dark }};
|
||||
}
|
||||
</style>
|
||||
<!-- Stylesheets -->
|
||||
{% if (node?.css): %}
|
||||
<link rel="stylesheet" href="{{ resource }}/{{ node.css}}">
|
||||
{% endif -%}
|
||||
{% if (css): %}
|
||||
<style title="text/css">
|
||||
{{ css }}
|
||||
</style>
|
||||
{% endif -%}
|
||||
{% if (!is_login): %}
|
||||
<script src="{{ dispatcher.build_url('admin/translations', dispatcher.lang) }}?v={{ version.luciversion }}"></script>
|
||||
<script src="{{ resource }}/cbi.js?v={{ version.luciversion }}"></script>
|
||||
<script src="{{ resource }}/luci.js?v={{ version.luciversion }}"></script>
|
||||
{% endif %}
|
||||
</head>
|
||||
@@ -16,7 +16,12 @@
|
||||
<meta name="apple-mobile-web-app-title" content="{{ hostname }} - LuCI">
|
||||
|
||||
<!-- Theme and Colors -->
|
||||
{% if (mode == 'normal'): %}
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="{{ primary }}">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="{{ dark_primary }}">
|
||||
{% else %}
|
||||
<meta name="theme-color" content="{{ bar_color }}">
|
||||
{% endif %}
|
||||
|
||||
<!-- Icons and Manifest -->
|
||||
{% if (branding.favicon): %}
|
||||
|
||||
@@ -17,86 +17,11 @@
|
||||
-#}
|
||||
|
||||
{%
|
||||
import { access } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
import { getuid, getspnam } from 'luci.core';
|
||||
|
||||
const boardinfo = ubus.call("system", "board");
|
||||
const hostname = striptags(boardinfo?.hostname ?? '?');
|
||||
|
||||
let cfg = cursor();
|
||||
|
||||
//send as HTML5
|
||||
http.prepare_content('text/html; charset=UTF-8');
|
||||
|
||||
//Custom settings
|
||||
function validColor(value, fallback) {
|
||||
return type(value) == 'string' && match(value, /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/) ? value : fallback;
|
||||
}
|
||||
|
||||
function validNumber(value, fallback, maximum) {
|
||||
return type(value) == 'string' && match(value, /^(0|[1-9][0-9]*)(\.[0-9]+)?$/) && +value <= maximum ? value : fallback;
|
||||
}
|
||||
|
||||
let mode = 'normal';
|
||||
let primary = '#5e72e4';
|
||||
let dark_primary = '#483d8b';
|
||||
let blur_radius = '10';
|
||||
let blur_radius_dark = '10';
|
||||
let blur_opacity = '0.5';
|
||||
let blur_opacity_dark = '0.5';
|
||||
const has_argon_config = access('/etc/config/argon');
|
||||
if (has_argon_config) {
|
||||
primary = validColor(cfg.get_first('argon', 'global', 'primary'), primary);
|
||||
dark_primary = validColor(cfg.get_first('argon', 'global', 'dark_primary'), dark_primary);
|
||||
blur_radius = validNumber(cfg.get_first('argon', 'global', 'blur'), blur_radius, 100);
|
||||
blur_radius_dark = validNumber(cfg.get_first('argon', 'global', 'blur_dark'), blur_radius_dark, 100);
|
||||
blur_opacity = validNumber(cfg.get_first('argon', 'global', 'transparency'), blur_opacity, 1);
|
||||
blur_opacity_dark = validNumber(cfg.get_first('argon', 'global', 'transparency_dark'), blur_opacity_dark, 1);
|
||||
let configured_mode = cfg.get_first('argon', 'global', 'mode');
|
||||
if (configured_mode == 'normal' || configured_mode == 'dark' || configured_mode == 'light')
|
||||
mode = configured_mode;
|
||||
}
|
||||
const bar_color = mode == 'dark' ? dark_primary : primary;
|
||||
|
||||
-%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ dispatcher.lang }}"{% if (mode == 'dark'): %} data-darkmode="true"{% elif (mode == 'light'): %} data-darkmode="false"{% endif %}>
|
||||
|
||||
<head>
|
||||
{% include("themes/" + theme + "/head_meta", { hostname: hostname, node: node, bar_color: bar_color, media: media }) %}
|
||||
|
||||
<link rel="stylesheet" href="{{ media }}/css/cascade.css{# ?v=PKG_VERSION #}">
|
||||
{% if (mode == 'normal'): %}
|
||||
<link rel="stylesheet" href="{{ media }}/css/dark.css{# ?v=PKG_VERSION #}" media="(prefers-color-scheme: dark)">
|
||||
{% elif (mode == 'dark'): %}
|
||||
<link rel="stylesheet" href="{{ media }}/css/dark.css{# ?v=PKG_VERSION #}">
|
||||
{% endif %}
|
||||
<style title="text/css">
|
||||
{% if (has_argon_config): %}
|
||||
:root {
|
||||
--primary: {{ primary }};
|
||||
--dark-primary: {{ dark_primary }};
|
||||
--blur-radius:{{ blur_radius }}px;
|
||||
--blur-opacity:{{ blur_opacity }};
|
||||
--blur-radius-dark:{{ blur_radius_dark }}px;
|
||||
--blur-opacity-dark:{{ blur_opacity_dark }};
|
||||
}
|
||||
{% endif -%}
|
||||
</style>
|
||||
<!-- Stylesheets -->
|
||||
{% if (node?.css): %}
|
||||
<link rel="stylesheet" href="{{ resource }}/{{ node.css}}">
|
||||
{% endif -%}
|
||||
{% if (css): %}
|
||||
<style title="text/css">
|
||||
{{ css }}
|
||||
</style>
|
||||
{% endif -%}
|
||||
<script src="{{ dispatcher.build_url('admin/translations', dispatcher.lang) }}?v={{ version.luciversion }}"></script>
|
||||
<script src="{{ resource }}/cbi.js?v={{ version.luciversion }}"></script>
|
||||
<script src="{{ resource }}/luci.js?v={{ version.luciversion }}"></script>
|
||||
</head>
|
||||
const hostname = striptags(ubus.call('system', 'board')?.hostname ?? '?');
|
||||
include('themes/' + theme + '/head', { hostname: hostname, is_login: false });
|
||||
%}
|
||||
|
||||
<body
|
||||
class="lang_{{ dispatcher.lang }} {{ node?.title ? ` - ${striptags(node.title)}` : '' }} {% if (ctx.authsession): %}logged-in{% endif %}"
|
||||
@@ -125,9 +50,12 @@
|
||||
<div class="container">
|
||||
<div class="flex1">
|
||||
<button type="button" class="showSide" aria-label="{{ entityencode(_('Toggle navigation'), true) }}" aria-controls="mainmenu" aria-expanded="false"></button>
|
||||
<a class="brand" href="#">{{ hostname }}</a>
|
||||
<a class="brand" href="#" title="{{ entityencode(hostname, true) }}">{{ hostname }}</a>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="status" id="indicators"></div>
|
||||
<div id="argon-appearance" hidden></div>
|
||||
</div>
|
||||
<div class="status" id="indicators"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -17,81 +17,8 @@
|
||||
-#}
|
||||
|
||||
{%
|
||||
import { access } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
|
||||
const boardinfo = ubus.call("system", "board");
|
||||
const hostname = striptags(boardinfo?.hostname ?? '?');
|
||||
|
||||
let cfg = cursor();
|
||||
|
||||
//send as HTML5
|
||||
http.prepare_content('text/html; charset=UTF-8');
|
||||
|
||||
//Custom settings
|
||||
function validColor(value, fallback) {
|
||||
return type(value) == 'string' && match(value, /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/) ? value : fallback;
|
||||
}
|
||||
|
||||
function validNumber(value, fallback, maximum) {
|
||||
return type(value) == 'string' && match(value, /^(0|[1-9][0-9]*)(\.[0-9]+)?$/) && +value <= maximum ? value : fallback;
|
||||
}
|
||||
|
||||
let mode = 'normal';
|
||||
let primary = '#5e72e4';
|
||||
let dark_primary = '#483d8b';
|
||||
let blur_radius = '10';
|
||||
let blur_radius_dark = '10';
|
||||
let blur_opacity = '0.5';
|
||||
let blur_opacity_dark = '0.5';
|
||||
const has_argon_config = access('/etc/config/argon');
|
||||
if (has_argon_config) {
|
||||
primary = validColor(cfg.get_first('argon', 'global', 'primary'), primary);
|
||||
dark_primary = validColor(cfg.get_first('argon', 'global', 'dark_primary'), dark_primary);
|
||||
blur_radius = validNumber(cfg.get_first('argon', 'global', 'blur'), blur_radius, 100);
|
||||
blur_radius_dark = validNumber(cfg.get_first('argon', 'global', 'blur_dark'), blur_radius_dark, 100);
|
||||
blur_opacity = validNumber(cfg.get_first('argon', 'global', 'transparency'), blur_opacity, 1);
|
||||
blur_opacity_dark = validNumber(cfg.get_first('argon', 'global', 'transparency_dark'), blur_opacity_dark, 1);
|
||||
let configured_mode = cfg.get_first('argon', 'global', 'mode');
|
||||
if (configured_mode == 'normal' || configured_mode == 'dark' || configured_mode == 'light')
|
||||
mode = configured_mode;
|
||||
}
|
||||
const bar_color = mode == 'dark' ? dark_primary : primary;
|
||||
|
||||
-%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ dispatcher.lang }}">
|
||||
|
||||
<head>
|
||||
{% include("themes/" + theme + "/head_meta", { hostname: hostname, node: node, bar_color: bar_color, media: media }) %}
|
||||
|
||||
<link rel="stylesheet" href="{{ media }}/css/cascade.css{# ?v=PKG_VERSION #}">
|
||||
{% if (mode == 'normal'): %}
|
||||
<link rel="stylesheet" href="{{ media }}/css/dark.css{# ?v=PKG_VERSION #}" media="(prefers-color-scheme: dark)">
|
||||
{% elif (mode == 'dark'): %}
|
||||
<link rel="stylesheet" href="{{ media }}/css/dark.css{# ?v=PKG_VERSION #}">
|
||||
{% endif %}
|
||||
<style title="text/css">
|
||||
{% if (has_argon_config): %}
|
||||
:root {
|
||||
--primary: {{ primary }};
|
||||
--dark-primary: {{ dark_primary }};
|
||||
--blur-radius:{{ blur_radius }}px;
|
||||
--blur-opacity:{{ blur_opacity }};
|
||||
--blur-radius-dark:{{ blur_radius_dark }}px;
|
||||
--blur-opacity-dark:{{ blur_opacity_dark }};
|
||||
}
|
||||
{% endif -%}
|
||||
</style>
|
||||
{% if (node?.css): %}
|
||||
<link rel="stylesheet" href="{{ resource }}/{{ node.css}}">
|
||||
{% endif -%}
|
||||
{% if (css): %}
|
||||
<style title="text/css">
|
||||
{{ css }}
|
||||
</style>
|
||||
{% endif -%}
|
||||
|
||||
</head>
|
||||
const hostname = striptags(ubus.call('system', 'board')?.hostname ?? '?');
|
||||
include('themes/' + theme + '/head', { hostname: hostname, is_login: true });
|
||||
%}
|
||||
|
||||
<body>
|
||||
@@ -0,0 +1,110 @@
|
||||
{# Background selection and rendering. cfg is the caller's UCI cursor. -#}
|
||||
{%
|
||||
import * as fs from 'fs';
|
||||
|
||||
// Per-request pseudo-random state for selecting local backgrounds.
|
||||
let lcgState = (+time()) % 2147483648;
|
||||
|
||||
function randomIndex(limit) {
|
||||
lcgState = (lcgState * 1103515245 + 12345) % 2147483648;
|
||||
|
||||
return lcgState % limit;
|
||||
}
|
||||
|
||||
//Fetch Local Background Media
|
||||
|
||||
const imageTypes = " jpg jpeg png gif webp ";
|
||||
const videoTypes = " mp4 webm ";
|
||||
const allTypes = imageTypes + videoTypes;
|
||||
|
||||
function fetchMedia(path, themeDir) {
|
||||
let backgroundTable = [];
|
||||
for (f in (fs.lsdir(path))) {
|
||||
let parts = split(f, '.');
|
||||
let ext = lc(parts?.[length(parts) - 1]);
|
||||
if (ext && index(allTypes, " " + ext + " ") != -1) {
|
||||
let bg = {};
|
||||
bg.type = ext;
|
||||
bg.url = themeDir + http.urlencode(f);
|
||||
push(backgroundTable, bg);
|
||||
}
|
||||
}
|
||||
return backgroundTable;
|
||||
}
|
||||
|
||||
function selectBackground(themeDir) {
|
||||
let bgUrl = media + "/img/bg1.webp";
|
||||
let backgroundType = "Image";
|
||||
let mimeType = "";
|
||||
|
||||
if (fs.access("/etc/config/argon")) {
|
||||
let online_wallpaper = cfg.get_first('argon', 'global', 'online_wallpaper') ?? (cfg.get_first('argon', 'global', 'bing_background') == '1' ? 'bing' : null);
|
||||
if (online_wallpaper && online_wallpaper != "none") {
|
||||
const picurl = ubus.call("luci.argon_wallpaper", "get_url") ?? {};
|
||||
if (type(picurl?.url) == 'string' && match(picurl.url, /^https:\/\/[^[:space:]"'<>]+$/)) {
|
||||
bgUrl = picurl.url;
|
||||
return {bgUrl, backgroundType, mimeType};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let background = fetchMedia("/www" + themeDir, themeDir);
|
||||
if ( length(background) > 0 ) {
|
||||
let currentBg = background[randomIndex(length(background))];
|
||||
bgUrl = currentBg.url;
|
||||
if (index(videoTypes, " " + currentBg.type + " ") != -1) {
|
||||
backgroundType = "Video";
|
||||
mimeType = "video/" + currentBg.type;
|
||||
}
|
||||
}
|
||||
|
||||
return {bgUrl, backgroundType, mimeType};
|
||||
}
|
||||
|
||||
const bgArry = selectBackground(media + '/background/');
|
||||
%}
|
||||
{% if ( bgArry.backgroundType == "Video" ): %}
|
||||
<!-- Video Player Start -->
|
||||
<div class="video">
|
||||
<video loop muted playsinline id="video" poster="{{ media }}/img/bg1.webp">
|
||||
<source src="{{ entityencode(bgArry.bgUrl, true) }}" type="{{ bgArry.mimeType }}">
|
||||
</video>
|
||||
</div>
|
||||
<button type="button" class="volume-control mute" aria-label="{{ entityencode(_('Mute background video'), true) }}" aria-pressed="true"></button>
|
||||
<script>
|
||||
var backgroundVideo = document.getElementById("video");
|
||||
var motionPreference = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
function updateBackgroundMotion() {
|
||||
if (motionPreference && motionPreference.matches)
|
||||
backgroundVideo.pause();
|
||||
else {
|
||||
var playResult = backgroundVideo.play();
|
||||
if (playResult && playResult.catch)
|
||||
playResult.catch(function() {});
|
||||
}
|
||||
}
|
||||
updateBackgroundMotion();
|
||||
if (motionPreference) {
|
||||
if (motionPreference.addEventListener)
|
||||
motionPreference.addEventListener('change', updateBackgroundMotion);
|
||||
else if (motionPreference.addListener)
|
||||
motionPreference.addListener(updateBackgroundMotion);
|
||||
}
|
||||
document.querySelector(".volume-control").addEventListener("click", function(){
|
||||
if(this.classList.contains("mute")){
|
||||
this.classList.remove("mute");
|
||||
backgroundVideo.muted = false;
|
||||
this.setAttribute('aria-pressed', 'false');
|
||||
}else{
|
||||
this.classList.add("mute");
|
||||
backgroundVideo.muted = true;
|
||||
this.setAttribute('aria-pressed', 'true');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- Video Player End -->
|
||||
{% else %}
|
||||
<!-- Image Background Start -->
|
||||
<div class="main-bg" id="main-bg"><img src="{{ entityencode(bgArry.bgUrl, true) }}" data-fallback="{{ media }}/img/bg1.webp" onerror="this.onerror=null;this.src=this.dataset.fallback" alt=""></div>
|
||||
<!-- Image Background End -->
|
||||
{% endif %}
|
||||
@@ -0,0 +1,72 @@
|
||||
{# Complete authentication form; auth_* values come from the dispatcher scope. -#}
|
||||
<form class="form-login" method="post" action="{{ entityencode(http.getenv("REQUEST_URI"), true) }}">
|
||||
|
||||
{#-
|
||||
`auth_plugin` is set only on the render that follows an ACCEPTED password
|
||||
(dispatcher.uc), so it is what separates "the password was wrong" from "the
|
||||
password was right and a plugin wants more". Gating the credentials alert on
|
||||
`!auth_plugin` and showing `auth_message` beside it rather than instead of it
|
||||
covers all four states: a wrong password, a wrong password with a challenge
|
||||
already pending, the challenge prompt itself, and a failed challenge.
|
||||
-#}
|
||||
{%- if (fuser && !auth_plugin): %}
|
||||
<div class="errorbox">{{ _('Invalid username and/or password! Please try again.') }}</div>
|
||||
{% endif -%}
|
||||
|
||||
{%- if (auth_message): %}
|
||||
<div class="errorbox">{{ auth_message }}</div>
|
||||
{% endif -%}
|
||||
|
||||
<div class="input-container">
|
||||
<div class="input-group user-icon">
|
||||
<label class="visually-hidden" for="cbi-input-user">{{ _('Username') }}</label>
|
||||
<input class="cbi-input-user" id="cbi-input-user" type="text" name="luci_username" autocomplete="username" autocapitalize="none" spellcheck="false" placeholder="{{ entityencode(_('Username'), true) }}" value="{{ entityencode(duser, true) }}" />
|
||||
<span class="border" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="input-group pass-icon">
|
||||
<label class="visually-hidden" for="cbi-input-password">{{ _('Password') }}</label>
|
||||
<input class="cbi-input-password" id="cbi-input-password" type="password" name="luci_password" autocomplete="current-password" placeholder="{{ entityencode(_('Password'), true) }}" />
|
||||
<span class="border" aria-hidden="true"></span>
|
||||
</div>
|
||||
{%- if (auth_fields): %}
|
||||
{%- for (let field in auth_fields): %}
|
||||
{#- Keep a plugin's format hint as the placeholder, while its label or
|
||||
field name provides an accessible name for the input. -#}
|
||||
<div class="input-group pass-icon">
|
||||
<label class="visually-hidden" for="{{ entityencode(field.name, true) }}">{{ entityencode(_(field.label || field.name)) }}</label>
|
||||
<input class="cbi-input-text" id="{{ entityencode(field.name, true) }}" type="{{ entityencode(field.type ?? 'text', true) }}" name="{{ entityencode(field.name, true) }}"
|
||||
placeholder="{{ entityencode(_(field.label ?? field.placeholder ?? field.name), true) }}"
|
||||
{% if (field.placeholder): %}title="{{ entityencode(field.placeholder, true) }}"{% endif %}
|
||||
{% if (field.inputmode): %}inputmode="{{ entityencode(field.inputmode, true) }}"{% endif %}
|
||||
{% if (field.pattern): %}pattern="{{ entityencode(field.pattern, true) }}"{% endif %}
|
||||
{% if (field.maxlength): %}maxlength="{{ entityencode('' + field.maxlength, true) }}"{% endif %}
|
||||
{% if (field.autocomplete): %}autocomplete="{{ entityencode(field.autocomplete, true) }}"{% endif %}
|
||||
{% if (field.required): %}required{% endif %} />
|
||||
<span class="border" aria-hidden="true"></span>
|
||||
</div>
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- if (auth_html): %}
|
||||
<div class="auth-plugin">{{ auth_html }}</div>
|
||||
{% endif -%}
|
||||
|
||||
<div>
|
||||
<input type="submit" value="{{ _('Log in') }}" class="cbi-button cbi-button-apply" />
|
||||
</div>
|
||||
{#- Loaded last so the markup a plugin emitted through `auth_html` is already
|
||||
in the document when its script runs. -#}
|
||||
{%- if (auth_assets): %}
|
||||
{%- for (let asset in auth_assets): %}
|
||||
{%- if (asset.type == 'script'): %}
|
||||
<script src="{{ asset.src }}"></script>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
</form>
|
||||
<!-- Login Form End -->
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var input = document.getElementsByName('luci_password')[0];
|
||||
if (input)
|
||||
input.focus();
|
||||
//]]></script>
|
||||
@@ -0,0 +1,23 @@
|
||||
{# Shared location bootstrap; LuCI modules are loaded only after the app shell. -#}
|
||||
<script>
|
||||
// thanks for Jo-Philipp Wich <jow@openwrt.org>
|
||||
var luciLocation = {{ ctx.path }};
|
||||
</script>
|
||||
{% if (!is_login): %}
|
||||
<script type="text/javascript">L.require('menu-argon')</script>
|
||||
<script type="text/javascript">
|
||||
L.require('argon-appearance');
|
||||
if ([ 'admin-status', 'admin-status-overview' ].includes(document.body.dataset.page))
|
||||
L.require('argon-overview');
|
||||
if (document.body.dataset.page === 'admin-system-admin-sshkeys')
|
||||
L.require('argon-sshkeys');
|
||||
|
||||
if ([
|
||||
'admin-status-realtime',
|
||||
'admin-status-realtime-load',
|
||||
'admin-status-realtime-bandwidth',
|
||||
'admin-status-realtime-connections'
|
||||
].includes(document.body.dataset.page))
|
||||
L.require('argon-graphs');
|
||||
</script>
|
||||
{% endif %}
|
||||
@@ -36,120 +36,15 @@
|
||||
login_style = configured_style;
|
||||
}
|
||||
|
||||
// Per-request pseudo-random state for selecting local backgrounds.
|
||||
let lcgState = (+time()) % 2147483648;
|
||||
|
||||
function randomIndex(limit) {
|
||||
lcgState = (lcgState * 1103515245 + 12345) % 2147483648;
|
||||
|
||||
return lcgState % limit;
|
||||
}
|
||||
|
||||
//Fetch Local Background Media
|
||||
|
||||
const imageTypes = " jpg jpeg png gif webp ";
|
||||
const videoTypes = " mp4 webm ";
|
||||
const allTypes = imageTypes + videoTypes;
|
||||
|
||||
function fetchMedia(path, themeDir) {
|
||||
let backgroundTable = [];
|
||||
for (f in (fs.lsdir(path))) {
|
||||
let parts = split(f, '.');
|
||||
let ext = lc(parts?.[length(parts) - 1]);
|
||||
if (ext && index(allTypes, " " + ext + " ") != -1) {
|
||||
let bg = {};
|
||||
bg.type = ext;
|
||||
bg.url = themeDir + http.urlencode(f);
|
||||
push(backgroundTable, bg);
|
||||
}
|
||||
}
|
||||
return backgroundTable;
|
||||
}
|
||||
|
||||
function selectBackground(themeDir) {
|
||||
let bgUrl = media + "/img/bg1.jpg";
|
||||
let backgroundType = "Image";
|
||||
let mimeType = "";
|
||||
|
||||
if (fs.access("/etc/config/argon")) {
|
||||
let online_wallpaper = cfg.get_first('argon', 'global', 'online_wallpaper') ?? (cfg.get_first('argon', 'global', 'bing_background') == '1' ? 'bing' : null);
|
||||
if (online_wallpaper && online_wallpaper != "none") {
|
||||
const picurl = ubus.call("luci.argon_wallpaper", "get_url") ?? {};
|
||||
if (type(picurl?.url) == 'string' && match(picurl.url, /^https:\/\/[^[:space:]"'<>]+$/)) {
|
||||
bgUrl = picurl.url;
|
||||
return {bgUrl, backgroundType, mimeType};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let background = fetchMedia("/www" + themeDir, themeDir);
|
||||
if ( length(background) > 0 ) {
|
||||
let currentBg = background[randomIndex(length(background))];
|
||||
bgUrl = currentBg.url;
|
||||
if (index(videoTypes, " " + currentBg.type + " ") != -1) {
|
||||
backgroundType = "Video";
|
||||
mimeType = "video/" + currentBg.type;
|
||||
}
|
||||
}
|
||||
|
||||
return {bgUrl, backgroundType, mimeType};
|
||||
}
|
||||
|
||||
const boardinfo = ubus.call("system", "board");
|
||||
const hostname = striptags(boardinfo?.hostname ?? '?');
|
||||
const themeDir = media + "/background/";
|
||||
const bgArry = selectBackground(themeDir);
|
||||
const branding = get_branding();
|
||||
const logo_url = branding.logo ? media + '/branding/' + branding.logo : media + '/img/argon.svg';
|
||||
|
||||
%}
|
||||
<!-- Login Page Start -->
|
||||
<div class="login-page" data-login-style="{{ login_style }}">
|
||||
{% if ( bgArry.backgroundType == "Video" ): %}
|
||||
<!-- Video Player Start -->
|
||||
<div class="video">
|
||||
<video loop muted playsinline id="video" poster="{{ media }}/img/bg1.jpg">
|
||||
<source src="{{ entityencode(bgArry.bgUrl, true) }}" type="{{ bgArry.mimeType }}">
|
||||
</video>
|
||||
</div>
|
||||
<button type="button" class="volume-control mute" aria-label="{{ entityencode(_('Mute background video'), true) }}" aria-pressed="true"></button>
|
||||
<script>
|
||||
var backgroundVideo = document.getElementById("video");
|
||||
var motionPreference = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
function updateBackgroundMotion() {
|
||||
if (motionPreference && motionPreference.matches)
|
||||
backgroundVideo.pause();
|
||||
else {
|
||||
var playResult = backgroundVideo.play();
|
||||
if (playResult && playResult.catch)
|
||||
playResult.catch(function() {});
|
||||
}
|
||||
}
|
||||
updateBackgroundMotion();
|
||||
if (motionPreference) {
|
||||
if (motionPreference.addEventListener)
|
||||
motionPreference.addEventListener('change', updateBackgroundMotion);
|
||||
else if (motionPreference.addListener)
|
||||
motionPreference.addListener(updateBackgroundMotion);
|
||||
}
|
||||
document.querySelector(".volume-control").addEventListener("click", function(){
|
||||
if(this.classList.contains("mute")){
|
||||
this.classList.remove("mute");
|
||||
backgroundVideo.muted = false;
|
||||
this.setAttribute('aria-pressed', 'false');
|
||||
}else{
|
||||
this.classList.add("mute");
|
||||
backgroundVideo.muted = true;
|
||||
this.setAttribute('aria-pressed', 'true');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- Video Player End -->
|
||||
{% else %}
|
||||
<!-- Image Background Start -->
|
||||
<div class="main-bg" id="main-bg"><img src="{{ entityencode(bgArry.bgUrl, true) }}" data-fallback="{{ media }}/img/bg1.jpg" onerror="this.onerror=null;this.src=this.dataset.fallback" alt=""></div>
|
||||
<!-- Image Background End -->
|
||||
{% endif %}
|
||||
{% include('themes/' + theme + '/login_background', { cfg: cfg }) %}
|
||||
<!-- Login Container Start -->
|
||||
<div class="login-container">
|
||||
<div class="login-form">
|
||||
@@ -159,75 +54,5 @@
|
||||
</a>
|
||||
<!-- Logo End -->
|
||||
<!-- Login Form Start -->
|
||||
<form class="form-login" method="post" action="{{ entityencode(http.getenv("REQUEST_URI"), true) }}">
|
||||
|
||||
{#-
|
||||
`auth_plugin` is set only on the render that follows an ACCEPTED password
|
||||
(dispatcher.uc), so it is what separates "the password was wrong" from "the
|
||||
password was right and a plugin wants more". Gating the credentials alert on
|
||||
`!auth_plugin` and showing `auth_message` beside it rather than instead of it
|
||||
covers all four states: a wrong password, a wrong password with a challenge
|
||||
already pending, the challenge prompt itself, and a failed challenge.
|
||||
-#}
|
||||
{%- if (fuser && !auth_plugin): %}
|
||||
<div class="errorbox">{{ _('Invalid username and/or password! Please try again.') }}</div>
|
||||
{% endif -%}
|
||||
|
||||
{%- if (auth_message): %}
|
||||
<div class="errorbox">{{ auth_message }}</div>
|
||||
{% endif -%}
|
||||
|
||||
<div class="input-container">
|
||||
<div class="input-group user-icon">
|
||||
<label class="visually-hidden" for="cbi-input-user">{{ _('Username') }}</label>
|
||||
<input class="cbi-input-user" id="cbi-input-user" type="text" name="luci_username" value="{{ entityencode(duser, true) }}" />
|
||||
<span class="border" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="input-group pass-icon">
|
||||
<label class="visually-hidden" for="cbi-input-password">{{ _('Password') }}</label>
|
||||
<input class="cbi-input-password" id="cbi-input-password" type="password" name="luci_password" />
|
||||
<span class="border" aria-hidden="true"></span>
|
||||
</div>
|
||||
{%- if (auth_fields): %}
|
||||
{%- for (let field in auth_fields): %}
|
||||
{#- Keep a plugin's format hint as the placeholder, while its label or
|
||||
field name provides an accessible name for the input. -#}
|
||||
<div class="input-group pass-icon">
|
||||
<label class="visually-hidden" for="{{ entityencode(field.name, true) }}">{{ entityencode(_(field.label || field.name)) }}</label>
|
||||
<input class="cbi-input-text" id="{{ entityencode(field.name, true) }}" type="{{ entityencode(field.type ?? 'text', true) }}" name="{{ entityencode(field.name, true) }}"
|
||||
placeholder="{{ entityencode(_(field.label ?? field.placeholder ?? field.name), true) }}"
|
||||
{% if (field.placeholder): %}title="{{ entityencode(field.placeholder, true) }}"{% endif %}
|
||||
{% if (field.inputmode): %}inputmode="{{ entityencode(field.inputmode, true) }}"{% endif %}
|
||||
{% if (field.pattern): %}pattern="{{ entityencode(field.pattern, true) }}"{% endif %}
|
||||
{% if (field.maxlength): %}maxlength="{{ entityencode('' + field.maxlength, true) }}"{% endif %}
|
||||
{% if (field.autocomplete): %}autocomplete="{{ entityencode(field.autocomplete, true) }}"{% endif %}
|
||||
{% if (field.required): %}required{% endif %} />
|
||||
<span class="border" aria-hidden="true"></span>
|
||||
</div>
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- if (auth_html): %}
|
||||
<div class="auth-plugin">{{ auth_html }}</div>
|
||||
{% endif -%}
|
||||
|
||||
<div>
|
||||
<input type="submit" value="{{ _('Log in') }}" class="cbi-button cbi-button-apply" />
|
||||
</div>
|
||||
{#- Loaded last so the markup a plugin emitted through `auth_html` is already
|
||||
in the document when its script runs. -#}
|
||||
{%- if (auth_assets): %}
|
||||
{%- for (let asset in auth_assets): %}
|
||||
{%- if (asset.type == 'script'): %}
|
||||
<script src="{{ asset.src }}"></script>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
</form>
|
||||
<!-- Login Form End -->
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var input = document.getElementsByName('luci_password')[0];
|
||||
if (input)
|
||||
input.focus();
|
||||
//]]></script>
|
||||
{% include('themes/' + theme + '/login_form') %}
|
||||
{% include("themes/" + theme + "/footer_login") %}
|
||||
Reference in new issue
Block a user