更新模板功能
@ -23,7 +23,7 @@ class BannerController extends BaseController
|
||||
try {
|
||||
$banners = Banner::where('delete_time', null)
|
||||
->where('tid', $this->getTenantId())
|
||||
->order('sort', 'asc')
|
||||
->order('sort desc, id desc')
|
||||
->field('id, title, desc, url, image, sort, create_time, update_time')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
@ -34,6 +34,8 @@ class SiteSettingsController extends BaseController
|
||||
'sitename' => '',
|
||||
'logo' => '',
|
||||
'logow' => '',
|
||||
'ico' => '',
|
||||
'companyintroduction' => '',
|
||||
'description' => '',
|
||||
'copyright' => '',
|
||||
'companyname' => '',
|
||||
@ -44,6 +46,8 @@ class SiteSettingsController extends BaseController
|
||||
'sitename' => $siteSetting->sitename ?? '',
|
||||
'logo' => $siteSetting->logo ?? '',
|
||||
'logow' => $siteSetting->logow ?? '',
|
||||
'ico' => $siteSetting->ico ?? '',
|
||||
'companyintroduction' => $siteSetting->companyintroduction ?? '',
|
||||
'description' => $siteSetting->description ?? '',
|
||||
'copyright' => $siteSetting->copyright ?? '',
|
||||
'companyname' => $siteSetting->companyname ?? '',
|
||||
@ -85,12 +89,18 @@ class SiteSettingsController extends BaseController
|
||||
if (isset($rawData['sitename'])) {
|
||||
$siteSetting->sitename = (string)$rawData['sitename'];
|
||||
}
|
||||
if (isset($rawData['companyintroduction'])) {
|
||||
$siteSetting->companyintroduction = (string)$rawData['companyintroduction'];
|
||||
}
|
||||
if (isset($rawData['logo'])) {
|
||||
$siteSetting->logo = (string)$rawData['logo'];
|
||||
}
|
||||
if (isset($rawData['logow'])) {
|
||||
$siteSetting->logow = (string)$rawData['logow'];
|
||||
}
|
||||
if (isset($rawData['ico'])) {
|
||||
$siteSetting->ico = (string)$rawData['ico'];
|
||||
}
|
||||
if (isset($rawData['description'])) {
|
||||
$siteSetting->description = (string)$rawData['description'];
|
||||
}
|
||||
|
||||
@ -6,9 +6,9 @@ namespace app\index\controller;
|
||||
|
||||
use app\model\Banner;
|
||||
use app\index\BaseController;
|
||||
use app\model\FrontMenu;
|
||||
use app\model\OnePage;
|
||||
use app\model\System\SystemSiteSettings;
|
||||
use app\model\Cms\FrontMenu;
|
||||
use app\model\Cms\OnePage;
|
||||
use app\model\System\SystemSiteSetting;
|
||||
use app\service\ThemeService;
|
||||
use think\db\exception\DbException;
|
||||
use think\facade\Env;
|
||||
@ -324,39 +324,13 @@ class Index extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端底部数据
|
||||
* 获取首页综合数据
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getFooterData()
|
||||
public function getHomeData()
|
||||
{
|
||||
try {
|
||||
$footerData = SystemSiteSettings::where('delete_time', null)
|
||||
->field('label, value')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $footerData
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取友情链接列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getFriendlinkList()
|
||||
{
|
||||
try {
|
||||
// 通过域名获取租户ID
|
||||
// 1. 通过域名获取租户ID
|
||||
$tid = BaseController::getTenantIdByDomain();
|
||||
|
||||
if (empty($tid)) {
|
||||
@ -367,23 +341,34 @@ class Index extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
// 2. 获取站点基础信息 (normalinfos)
|
||||
$normalInfos = SystemSiteSetting::where('tid', $tid)
|
||||
->field('sitename,logo,logow,ico,description,copyright,companyname,icp,companyintroduction')
|
||||
->find();
|
||||
|
||||
// 3. 获取友情链接列表
|
||||
$friendlinkList = Friendlink::where('delete_time', null)
|
||||
->where('tid', $tid)
|
||||
->where('status', 1)
|
||||
->order('sort', 'asc')
|
||||
->field('id,link_name,link_url,link_logo,description')
|
||||
->field('id,link_name,link_url,link_logo')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 4. 合并返回
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $friendlinkList
|
||||
'data' => [
|
||||
'normal' => $normalInfos ?: (object) [],
|
||||
'links' => $friendlinkList
|
||||
]
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'msg' => '服务器错误:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ Route::get('init', 'app\index\controller\Index@init');
|
||||
Route::get('footerdata', 'app\index\controller\Index@getFooterData');
|
||||
Route::get('companyInfos', 'app\index\controller\Index@getCompanyInfos');
|
||||
Route::post('requirement', 'app\index\controller\Index@requirement');
|
||||
Route::get('friendlinks', 'app\index\controller\Index@getFriendlinkList');
|
||||
Route::get('homeData', 'app\index\controller\Index@getHomeData');
|
||||
|
||||
// --- 客户需求路由 ---
|
||||
|
||||
|
||||
@ -32,6 +32,8 @@ class SystemSiteSetting extends Model
|
||||
'sitename' => 'string',
|
||||
'logo' => 'string',
|
||||
'logow' => 'string',
|
||||
'ico' => 'string',
|
||||
'companyintroduction' => 'string',
|
||||
'description' => 'string',
|
||||
'copyright' => 'string',
|
||||
'companyname' => 'string',
|
||||
|
||||
@ -6,6 +6,16 @@
|
||||
RewriteCond %{REQUEST_METHOD} OPTIONS
|
||||
RewriteRule ^(.*)$ $1 [R=204,L]
|
||||
|
||||
# 排除静态文件和特定PHP文件
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !^/themes/
|
||||
RewriteCond %{REQUEST_URI} !^/static/
|
||||
RewriteCond %{REQUEST_URI} !^/storage/
|
||||
RewriteCond %{REQUEST_URI} !^/favicon.ico$
|
||||
RewriteCond %{REQUEST_URI} !^/robots.txt$
|
||||
RewriteRule ^(.*)$ index.php [L]
|
||||
|
||||
# 添加 CORS 头
|
||||
Header set Access-Control-Allow-Origin "http://localhost:5173"
|
||||
Header set Access-Control-Allow-Credentials "true"
|
||||
|
||||
5
public/news/article_detail.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// 短链接入口:/news/article_detail.php?id=xx
|
||||
// 直接复用主题详情页模板(保持同一份业务逻辑)
|
||||
require __DIR__ . '/../themes/default/article_detail.php';
|
||||
|
||||
4
public/news/index.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
// /news 文章列表短入口
|
||||
require __DIR__ . '/../themes/default/article_list.php';
|
||||
|
||||
BIN
public/storage/uploads/2026/03/12/69b21a885ce6d.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/storage/uploads/2026/03/12/69b21aa3d9d56.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/storage/uploads/2026/03/12/69b21ab693ebc.jpg
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
public/storage/uploads/2026/03/12/69b21ac964870.jpg
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
public/storage/uploads/2026/03/12/69b21ad9ea01f.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/storage/uploads/2026/03/12/69b21b53ea2eb.jpg
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
public/storage/uploads/2026/03/12/69b21b64a4540.jpg
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/storage/uploads/2026/03/12/69b21b7b35083.jpg
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
public/storage/uploads/2026/03/12/69b21d01dc0df.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
427
public/themes/default/article_detail.php
Normal file
@ -0,0 +1,427 @@
|
||||
<?php
|
||||
// 1. 引入 ThinkPHP 环境
|
||||
$rootPath = dirname(__DIR__, 3) . DIRECTORY_SEPARATOR;
|
||||
require_once $rootPath . 'vendor/autoload.php';
|
||||
|
||||
use think\facade\Db;
|
||||
use think\App;
|
||||
|
||||
// 初始化应用
|
||||
$app = new App($rootPath);
|
||||
$app->initialize();
|
||||
|
||||
// 获取基础环境信息
|
||||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||||
$scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
|
||||
$baseUrl = $scheme . '://' . $host;
|
||||
$themeBase = '/themes/default';
|
||||
|
||||
// 获取文章 ID
|
||||
$articleId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
|
||||
// 辅助处理函数
|
||||
function formatImageUrl($path, $baseUrl)
|
||||
{
|
||||
if (empty($path)) return '';
|
||||
if (preg_match('/^https?:\/\//', $path)) return $path;
|
||||
return $baseUrl . (strpos($path, '/') === 0 ? '' : '/') . $path;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 获取租户 ID
|
||||
$tenantDomain = Db::name('mete_tenant_domain')
|
||||
->where('full_domain', $host)
|
||||
->where('status', 1)
|
||||
->whereNull('delete_time')
|
||||
->find();
|
||||
$tid = $tenantDomain ? (int)$tenantDomain['tid'] : 0;
|
||||
|
||||
if ($tid === 0 || $articleId === 0) {
|
||||
die("未找到相关内容");
|
||||
}
|
||||
|
||||
// 2. 获取站点基础信息
|
||||
$setting = Db::name('mete_system_site_setting')->where('tid', $tid)->whereNull('delete_time')->find();
|
||||
$tenant = Db::name('mete_tenant')->where('id', $tid)->whereNull('delete_time')->find();
|
||||
$siteInfo = array_merge($tenant ?: [], $setting ?: []);
|
||||
|
||||
// 3. 获取文章详细数据
|
||||
$article = Db::name('mete_articles')
|
||||
->where('id', $articleId)
|
||||
->where('tid', $tid)
|
||||
->whereNull('delete_time')
|
||||
->find();
|
||||
|
||||
if (!$article) {
|
||||
die("文章不存在或已删除");
|
||||
}
|
||||
|
||||
// 更新阅读数 (可选)
|
||||
Db::name('mete_articles')->where('id', $articleId)->inc('views')->update();
|
||||
} catch (\Exception $e) {
|
||||
die("系统错误: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title><?php echo htmlspecialchars($article['title']); ?> - <?php echo $siteInfo['sitename']; ?></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/bootstrap-5.0.0-beta1.min.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/LineIcons.2.0.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/animate.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/lindy-uikit.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/all.min.css" />
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--ink: #0f172a;
|
||||
--muted: #475569;
|
||||
--line: rgba(15, 23, 42, 0.10);
|
||||
--card: rgba(255, 255, 255, 0.86);
|
||||
--accent: #5864FF;
|
||||
--accent2: #22c55e;
|
||||
--shadow: 0 16px 50px rgba(2, 6, 23, 0.12);
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--ink);
|
||||
background:
|
||||
radial-gradient(1100px 420px at 12% 0%, rgba(88, 100, 255, 0.20), transparent 55%),
|
||||
radial-gradient(900px 380px at 86% 10%, rgba(34, 197, 94, 0.12), transparent 55%),
|
||||
linear-gradient(180deg, #fbfdff 0%, #f6f8ff 100%);
|
||||
}
|
||||
|
||||
/* 顶部简洁导航 */
|
||||
.navbar-brand img {
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.article-topbar {
|
||||
margin-top: 18px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 16px;
|
||||
background: var(--card);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 10px 30px rgba(2, 6, 23, 0.06);
|
||||
}
|
||||
|
||||
.article-topbar .navbar {
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.article-topbar .back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--ink);
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
text-decoration: none;
|
||||
transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
|
||||
}
|
||||
|
||||
.article-topbar .back-link:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 10px 25px rgba(2, 6, 23, 0.10);
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.article-topbar .back-link i {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.breadcrumb-area {
|
||||
padding: 18px 0;
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 14px;
|
||||
background: var(--card);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 10px 30px rgba(2, 6, 23, 0.06);
|
||||
}
|
||||
|
||||
.breadcrumb-area a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.breadcrumb-area a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
padding: 46px 0 18px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-size: 34px;
|
||||
font-weight: 800;
|
||||
color: var(--ink);
|
||||
line-height: 1.25;
|
||||
letter-spacing: -0.02em;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.article-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.70);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.article-meta i {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.article-content {
|
||||
padding: 22px 0 70px;
|
||||
line-height: 1.85;
|
||||
font-size: 16px;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.article-content .col-lg-10 {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
box-shadow: var(--shadow);
|
||||
padding: 26px 22px;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.article-content img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
border-radius: 14px;
|
||||
margin: 18px 0;
|
||||
box-shadow: 0 14px 40px rgba(2, 6, 23, 0.10);
|
||||
}
|
||||
|
||||
/* 富文本排版:标题/段落/链接/列表/引用/代码 */
|
||||
.content-text {
|
||||
color: var(--ink);
|
||||
/* border-top: 1px solid #efefef; */
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
.content-text p {
|
||||
margin: 0 0 14px;
|
||||
color: #1f2937;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content-text a {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.content-text a:hover {
|
||||
color: #3b49ff;
|
||||
}
|
||||
|
||||
.content-text h1,
|
||||
.content-text h2,
|
||||
.content-text h3,
|
||||
.content-text h4 {
|
||||
color: var(--ink);
|
||||
font-weight: 800;
|
||||
margin: 26px 0 12px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.content-text h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.content-text h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.content-text ul,
|
||||
.content-text ol {
|
||||
margin: 10px 0 18px 1.1rem;
|
||||
}
|
||||
|
||||
.content-text li {
|
||||
margin: 6px 0;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.content-text blockquote {
|
||||
margin: 18px 0;
|
||||
padding: 14px 16px;
|
||||
border-left: 4px solid var(--accent);
|
||||
background: rgba(88, 100, 255, 0.07);
|
||||
border-radius: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.content-text hr {
|
||||
border: 0;
|
||||
border-top: 1px solid var(--line);
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.content-text code {
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
background: rgba(15, 23, 42, 0.06);
|
||||
color: #0b1220;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
|
||||
.content-text pre {
|
||||
margin: 16px 0 22px;
|
||||
padding: 16px 16px;
|
||||
border-radius: 14px;
|
||||
background: #0b1220;
|
||||
color: #e5e7eb;
|
||||
overflow: auto;
|
||||
box-shadow: 0 18px 60px rgba(2, 6, 23, 0.25);
|
||||
}
|
||||
|
||||
.content-text pre code {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
|
||||
.content-text p img {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: rgba(2, 6, 23, 0.92);
|
||||
padding: 44px 0;
|
||||
color: rgba(226, 232, 240, 0.86);
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.article-title {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.article-content .col-lg-10 {
|
||||
padding: 18px 14px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="article-topbar">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="<?php echo formatImageUrl($siteInfo['logo'] ?? '', $baseUrl); ?>" alt="Logo" />
|
||||
</a>
|
||||
<div class="ms-auto">
|
||||
<a class="back-link" href="/">
|
||||
<i class="lni lni-arrow-left"></i>
|
||||
返回首页
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="breadcrumb-area">
|
||||
<div class="container">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/">首页</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">文章详情</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="article-header">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10 text-center">
|
||||
<h1 class="article-title"><?php echo htmlspecialchars($article['title']); ?></h1>
|
||||
<div class="article-meta">
|
||||
<span><i class="lni lni-calendar"></i> <?php echo date('Y-m-d', strtotime($article['publish_date'])); ?></span>
|
||||
<span><i class="lni lni-eye"></i> 阅读: <?php echo $article['views']; ?></span>
|
||||
<span><i class="lni lni-user"></i> 管理员</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="article-content">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<!-- <?php if (!empty($article['image'])): ?>
|
||||
<div class="text-center mb-40">
|
||||
<img src="<?php echo formatImageUrl($article['image'], $baseUrl); ?>" style="width: 100%;">
|
||||
</div>
|
||||
<?php endif; ?> -->
|
||||
|
||||
<div class="content-text">
|
||||
<?php
|
||||
// 替换内容中的相对图片路径为绝对路径
|
||||
$content = $article['content'];
|
||||
$content = str_replace('src="/uploads/', 'src="' . $baseUrl . '/uploads/', $content);
|
||||
echo $content;
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<p>Copyright © 2026 <?php echo $siteInfo['companyname']; ?> | <?php echo $siteInfo['icp']; ?></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="<?php echo $themeBase; ?>/assets/js/bootstrap-5.0.0-beta1.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
406
public/themes/default/article_list.php
Normal file
@ -0,0 +1,406 @@
|
||||
<?php
|
||||
// 文章列表(每页 10 条,支持翻页)
|
||||
$rootPath = dirname(__DIR__, 3) . DIRECTORY_SEPARATOR;
|
||||
require_once $rootPath . 'vendor/autoload.php';
|
||||
|
||||
use think\facade\Db;
|
||||
use think\App;
|
||||
|
||||
$app = new App($rootPath);
|
||||
$app->initialize();
|
||||
|
||||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||||
$scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
|
||||
$baseUrl = $scheme . '://' . $host;
|
||||
$themeBase = '/themes/default';
|
||||
|
||||
function formatImageUrl($path, $baseUrl) {
|
||||
if (empty($path)) return '';
|
||||
if (preg_match('/^https?:\/\//', $path)) return $path;
|
||||
return $baseUrl . (strpos($path, '/') === 0 ? '' : '/') . $path;
|
||||
}
|
||||
|
||||
function stripHtmlText($html) {
|
||||
if (empty($html)) return '';
|
||||
$text = preg_replace('/<[^>]+>/', ' ', $html);
|
||||
$text = preg_replace('/\s+/', ' ', $text);
|
||||
return trim($text);
|
||||
}
|
||||
|
||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
if ($page < 1) $page = 1;
|
||||
$pageSize = 10;
|
||||
|
||||
try {
|
||||
$tenantDomain = Db::name('mete_tenant_domain')
|
||||
->where('full_domain', $host)
|
||||
->where('status', 1)
|
||||
->whereNull('delete_time')
|
||||
->find();
|
||||
$tid = $tenantDomain ? (int)$tenantDomain['tid'] : 0;
|
||||
|
||||
if ($tid === 0) {
|
||||
die("未找到相关内容");
|
||||
}
|
||||
|
||||
$setting = Db::name('mete_system_site_setting')->where('tid', $tid)->whereNull('delete_time')->find();
|
||||
$tenant = Db::name('mete_tenant')->where('id', $tid)->whereNull('delete_time')->find();
|
||||
$siteInfo = array_merge($tenant ?: [], $setting ?: []);
|
||||
|
||||
$baseQuery = Db::name('mete_articles')
|
||||
->where('tid', $tid)
|
||||
->whereNull('delete_time');
|
||||
|
||||
// 如果存在 status 字段,通常用 2 表示已发布;不存在也不影响(交给数据库报错会影响页面)
|
||||
// 为避免因不同库结构导致报错,这里不强依赖 status 过滤。
|
||||
|
||||
$total = (int)$baseQuery->count();
|
||||
$totalPages = (int)max(1, ceil($total / $pageSize));
|
||||
if ($page > $totalPages) $page = $totalPages;
|
||||
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$articles = $baseQuery
|
||||
->order('publish_date', 'desc')
|
||||
->limit($offset, $pageSize)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
die("系统错误: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title><?php echo htmlspecialchars(($siteInfo['sitename'] ?? '') ?: '新闻中心'); ?></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/bootstrap-5.0.0-beta1.min.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/LineIcons.2.0.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/animate.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/lindy-uikit.css" />
|
||||
<link rel="stylesheet" href="<?php echo $themeBase; ?>/assets/css/all.min.css" />
|
||||
|
||||
<style>
|
||||
:root{
|
||||
--ink:#0f172a;
|
||||
--muted:#475569;
|
||||
--line:rgba(15,23,42,.10);
|
||||
--card:rgba(255,255,255,.86);
|
||||
--accent:#5864FF;
|
||||
--shadow:0 16px 50px rgba(2,6,23,.12);
|
||||
}
|
||||
body{
|
||||
color:var(--ink);
|
||||
background:
|
||||
radial-gradient(1100px 420px at 12% 0%, rgba(88,100,255,.20), transparent 55%),
|
||||
radial-gradient(900px 380px at 86% 10%, rgba(34,197,94,.12), transparent 55%),
|
||||
linear-gradient(180deg, #fbfdff 0%, #f6f8ff 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.topbar{
|
||||
margin-top:18px;
|
||||
border:1px solid var(--line);
|
||||
border-radius:16px;
|
||||
background:var(--card);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 10px 30px rgba(2,6,23,.06);
|
||||
}
|
||||
.topbar .navbar{ padding: 12px 14px; }
|
||||
.navbar-brand img{ max-width: 260px; }
|
||||
.back-link{
|
||||
display:inline-flex;
|
||||
align-items:center;
|
||||
gap:8px;
|
||||
padding:10px 12px;
|
||||
border-radius:999px;
|
||||
border:1px solid var(--line);
|
||||
color:var(--ink);
|
||||
background:rgba(255,255,255,.55);
|
||||
text-decoration:none;
|
||||
transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
|
||||
}
|
||||
.back-link:hover{
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 10px 25px rgba(2,6,23,.10);
|
||||
background: rgba(255,255,255,.75);
|
||||
}
|
||||
.back-link i{ color: var(--accent); }
|
||||
|
||||
.page-head{
|
||||
padding: 34px 0 10px;
|
||||
text-align:center;
|
||||
}
|
||||
.page-title{
|
||||
font-size: 34px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
.page-subtitle{
|
||||
color: var(--muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.list-wrap{
|
||||
padding: 10px 0 70px;
|
||||
flex: 1;
|
||||
}
|
||||
.list-card{
|
||||
background: var(--card);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
box-shadow: var(--shadow);
|
||||
padding: 18px;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.news-grid{
|
||||
display:grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
@media (max-width: 992px){
|
||||
.news-grid{ grid-template-columns: 1fr; }
|
||||
.page-title{ font-size: 28px; }
|
||||
}
|
||||
|
||||
.news-item{
|
||||
display:flex;
|
||||
gap: 14px;
|
||||
padding: 14px;
|
||||
border: 1px solid rgba(15,23,42,.08);
|
||||
border-radius: 16px;
|
||||
background: rgba(255,255,255,.70);
|
||||
text-decoration:none;
|
||||
color: inherit;
|
||||
transition: transform .14s ease, box-shadow .14s ease, background .14s ease;
|
||||
overflow:hidden;
|
||||
}
|
||||
.news-item:hover{
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 16px 45px rgba(2,6,23,.12);
|
||||
background: rgba(255,255,255,.82);
|
||||
}
|
||||
.thumb{
|
||||
width: 160px;
|
||||
height: 108px;
|
||||
border-radius: 14px;
|
||||
overflow:hidden;
|
||||
flex: 0 0 auto;
|
||||
background: rgba(15,23,42,.06);
|
||||
border: 1px solid rgba(15,23,42,.08);
|
||||
}
|
||||
.thumb img{
|
||||
width:100%;
|
||||
height:100%;
|
||||
object-fit: cover;
|
||||
display:block;
|
||||
}
|
||||
@media (max-width: 576px){
|
||||
.thumb{ width: 120px; height: 86px; }
|
||||
}
|
||||
|
||||
.news-body{ min-width: 0; flex: 1; }
|
||||
.news-title{
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
margin: 0 0 8px;
|
||||
display:-webkit-box;
|
||||
line-clamp: 2;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow:hidden;
|
||||
}
|
||||
.news-desc{
|
||||
margin: 0 0 10px;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
display:-webkit-box;
|
||||
line-clamp: 2;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow:hidden;
|
||||
}
|
||||
.news-meta{
|
||||
display:flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
align-items:center;
|
||||
}
|
||||
.news-meta i{ color: var(--accent); }
|
||||
|
||||
.pager{
|
||||
display:flex;
|
||||
justify-content:center;
|
||||
margin-top: 22px;
|
||||
}
|
||||
.pagination{
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.page-link{
|
||||
border-radius: 12px !important;
|
||||
border: 1px solid rgba(15,23,42,.10);
|
||||
color: var(--ink);
|
||||
background: rgba(255,255,255,.65);
|
||||
box-shadow: 0 10px 25px rgba(2,6,23,.06);
|
||||
}
|
||||
.page-item.active .page-link{
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #fff;
|
||||
box-shadow: 0 18px 50px rgba(88,100,255,.30);
|
||||
}
|
||||
.page-item.disabled .page-link{
|
||||
background: rgba(255,255,255,.45);
|
||||
color: rgba(71,85,105,.6);
|
||||
}
|
||||
footer{
|
||||
background: rgba(2,6,23,.92);
|
||||
padding: 44px 0;
|
||||
color: rgba(226,232,240,.86);
|
||||
}
|
||||
footer p{ margin: 0; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="topbar">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="<?php echo formatImageUrl($siteInfo['logo'] ?? '', $baseUrl); ?>" alt="Logo" />
|
||||
</a>
|
||||
<div class="ms-auto">
|
||||
<a class="back-link" href="/#newsCenter">
|
||||
<i class="lni lni-arrow-left"></i>
|
||||
返回首页
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="page-head">
|
||||
<div class="container">
|
||||
<h1 class="page-title">新闻中心</h1>
|
||||
<p class="page-subtitle">最新动态与行业资讯,持续更新。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="list-wrap">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="list-card">
|
||||
<?php if (empty($articles)): ?>
|
||||
<div class="text-center py-5" style="color: var(--muted);">
|
||||
暂无文章
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="news-grid">
|
||||
<?php foreach ($articles as $a): ?>
|
||||
<?php
|
||||
$title = $a['title'] ?? '未命名';
|
||||
$publishDate = !empty($a['publish_date']) ? date('Y-m-d', strtotime($a['publish_date'])) : '';
|
||||
$views = $a['views'] ?? ($a['pv'] ?? 0);
|
||||
$desc = $a['summary'] ?? ($a['description'] ?? '');
|
||||
if (empty($desc) && !empty($a['content'])) $desc = stripHtmlText($a['content']);
|
||||
if (mb_strlen($desc) > 120) $desc = mb_substr($desc, 0, 120) . '…';
|
||||
$thumb = $a['thumb'] ?? ($a['image'] ?? '');
|
||||
$thumbUrl = $thumb ? formatImageUrl($thumb, $baseUrl) : '';
|
||||
$id = (int)($a['id'] ?? 0);
|
||||
?>
|
||||
<a class="news-item" href="/news/article_detail.php?id=<?php echo $id; ?>">
|
||||
<div class="thumb" aria-hidden="true">
|
||||
<?php if ($thumbUrl): ?>
|
||||
<img src="<?php echo htmlspecialchars($thumbUrl); ?>" alt="<?php echo htmlspecialchars($title); ?>">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="news-body">
|
||||
<h3 class="news-title"><?php echo htmlspecialchars($title); ?></h3>
|
||||
<p class="news-desc"><?php echo htmlspecialchars($desc ?: '暂无摘要'); ?></p>
|
||||
<div class="news-meta">
|
||||
<?php if ($publishDate): ?>
|
||||
<span><i class="lni lni-calendar"></i> <?php echo htmlspecialchars($publishDate); ?></span>
|
||||
<?php endif; ?>
|
||||
<span><i class="lni lni-eye"></i> <?php echo (int)$views; ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="pager">
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<?php
|
||||
$mkUrl = function($p){
|
||||
return '/news?page=' . (int)$p;
|
||||
};
|
||||
$prev = max(1, $page - 1);
|
||||
$next = min($totalPages, $page + 1);
|
||||
$window = 2;
|
||||
$start = max(1, $page - $window);
|
||||
$end = min($totalPages, $page + $window);
|
||||
?>
|
||||
|
||||
<li class="page-item <?php echo $page <= 1 ? 'disabled' : ''; ?>">
|
||||
<a class="page-link" href="<?php echo $page <= 1 ? '#' : $mkUrl($prev); ?>" tabindex="-1">上一页</a>
|
||||
</li>
|
||||
|
||||
<?php if ($start > 1): ?>
|
||||
<li class="page-item"><a class="page-link" href="<?php echo $mkUrl(1); ?>">1</a></li>
|
||||
<?php if ($start > 2): ?>
|
||||
<li class="page-item disabled"><span class="page-link">…</span></li>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php for ($p = $start; $p <= $end; $p++): ?>
|
||||
<li class="page-item <?php echo $p === $page ? 'active' : ''; ?>">
|
||||
<a class="page-link" href="<?php echo $mkUrl($p); ?>"><?php echo (int)$p; ?></a>
|
||||
</li>
|
||||
<?php endfor; ?>
|
||||
|
||||
<?php if ($end < $totalPages): ?>
|
||||
<?php if ($end < $totalPages - 1): ?>
|
||||
<li class="page-item disabled"><span class="page-link">…</span></li>
|
||||
<?php endif; ?>
|
||||
<li class="page-item"><a class="page-link" href="<?php echo $mkUrl($totalPages); ?>"><?php echo (int)$totalPages; ?></a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
|
||||
<a class="page-link" href="<?php echo $page >= $totalPages ? '#' : $mkUrl($next); ?>">下一页</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<p>Copyright © 2026 <?php echo htmlspecialchars($siteInfo['companyname'] ?? ''); ?> | <?php echo htmlspecialchars($siteInfo['icp'] ?? ''); ?></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="<?php echo $themeBase; ?>/assets/js/bootstrap-5.0.0-beta1.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@ -1129,7 +1129,7 @@ p {
|
||||
FOOTER-4 CSS
|
||||
================================ */
|
||||
.footer-style-4 {
|
||||
background: #F3F3F3;
|
||||
background: #000;
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
@ -1164,7 +1164,7 @@ p {
|
||||
|
||||
.footer-style-4 .widget-wrapper .footer-widget h6 {
|
||||
font-weight: 600;
|
||||
color: #585978;
|
||||
color: #fff;
|
||||
margin-bottom: 35px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@ -1172,7 +1172,7 @@ p {
|
||||
.footer-style-4 .widget-wrapper .footer-widget .links li a {
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
color: #585978;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer-style-4 .widget-wrapper .footer-widget .links li a:hover {
|
||||
@ -1199,7 +1199,7 @@ p {
|
||||
.footer-style-4 .widget-wrapper .footer-widget .download-app li a .text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #585978;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer-style-4 .widget-wrapper .footer-widget .download-app li a .text b {
|
||||
|
||||
@ -82,15 +82,31 @@ if ($tid > 0) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取企业联系信息
|
||||
// 获取企业联系信息和站点设置
|
||||
$companyInfo = [];
|
||||
if ($tid > 0) {
|
||||
$companyInfo = Tenant::where('id', $tid)
|
||||
// 获取租户信息
|
||||
$tenant = Tenant::where('id', $tid)
|
||||
->where('delete_time', null)
|
||||
->field('contact_phone, contact_email, address, worktime')
|
||||
->find();
|
||||
|
||||
// 获取站点设置
|
||||
$siteSetting = \app\model\System\SystemSiteSetting::where('tid', $tid)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
$companyInfo = array_merge(
|
||||
$tenant ? $tenant->toArray() : [],
|
||||
$siteSetting ? $siteSetting->toArray() : []
|
||||
);
|
||||
} else {
|
||||
$companyInfo = [
|
||||
'sitename' => '',
|
||||
'logo' => '',
|
||||
'logow' => '',
|
||||
'ico' => '',
|
||||
'description' => '',
|
||||
'contact_phone' => '-',
|
||||
'contact_email' => '-',
|
||||
'address' => '-',
|
||||
@ -98,9 +114,28 @@ if ($tid > 0) {
|
||||
];
|
||||
}
|
||||
|
||||
// 处理图片路径
|
||||
if (!empty($companyInfo['logo']) && !preg_match('/^https?:\/\//', $companyInfo['logo'])) {
|
||||
$scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
||||
$companyInfo['logo'] = $scheme . '://' . $host .
|
||||
(strpos($companyInfo['logo'], '/') === 0 ? '' : '/') . $companyInfo['logo'];
|
||||
}
|
||||
if (!empty($companyInfo['logow']) && !preg_match('/^https?:\/\//', $companyInfo['logow'])) {
|
||||
$scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
||||
$companyInfo['logow'] = $scheme . '://' . $host .
|
||||
(strpos($companyInfo['logow'], '/') === 0 ? '' : '/') . $companyInfo['logow'];
|
||||
}
|
||||
if (!empty($companyInfo['ico']) && !preg_match('/^https?:\/\//', $companyInfo['ico'])) {
|
||||
$scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
||||
$companyInfo['ico'] = $scheme . '://' . $host .
|
||||
(strpos($companyInfo['ico'], '/') === 0 ? '' : '/') . $companyInfo['ico'];
|
||||
}
|
||||
|
||||
// 辅助函数:去除 HTML 标签
|
||||
function stripHtml($html) {
|
||||
if (empty($html)) return '';
|
||||
function stripHtml($html)
|
||||
{
|
||||
if (empty($html))
|
||||
return '';
|
||||
$text = preg_replace('/<[^>]+>/', ' ', $html);
|
||||
$text = preg_replace('/\s+/', ' ', $text);
|
||||
return trim($text);
|
||||
@ -112,8 +147,11 @@ function stripHtml($html) {
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Nova - Bootstrap 5 Template</title>
|
||||
<meta name="description" content="" />
|
||||
<title><?php echo !empty($companyInfo['sitename']) ? $companyInfo['sitename'] : '实例 - 云泽网模板'; ?></title>
|
||||
<?php if (!empty($companyInfo['ico'])): ?>
|
||||
<link rel="icon" href="<?php echo $companyInfo['ico']; ?>" type="image/x-icon" />
|
||||
<?php endif; ?>
|
||||
<meta name="description" content="<?php echo !empty($companyInfo['description']) ? $companyInfo['description'] : ''; ?>" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="assets/css/bootstrap-5.0.0-beta1.min.css" />
|
||||
<link rel="stylesheet" href="assets/css/LineIcons.2.0.css" />
|
||||
@ -157,10 +195,12 @@ function stripHtml($html) {
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-12">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<a class="navbar-brand" href="index.html">
|
||||
<img src="assets/img/logo/logo.svg" alt="Logo" />
|
||||
<a class="navbar-brand" href="index.php">
|
||||
<img src="<?php echo !empty($companyInfo['logo']) ? $companyInfo['logo'] : 'assets/img/logo/logo.svg'; ?>" alt="Logo" />
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent6" aria-controls="navbarSupportedContent6" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarSupportedContent6" aria-controls="navbarSupportedContent6"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="toggler-icon"></span>
|
||||
<span class="toggler-icon"></span>
|
||||
<span class="toggler-icon"></span>
|
||||
@ -172,17 +212,17 @@ function stripHtml($html) {
|
||||
<a class="page-scroll active" href="#home">主页</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="page-scroll" href="#feature">新闻中心</a>
|
||||
<a class="page-scroll" href="#newsCenter">新闻中心</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="page-scroll" href="#about">关于我们</a>
|
||||
<a class="page-scroll" href="#aboutUs">关于我们</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="page-scroll" href="#pricing">Pricing</a>
|
||||
<a class="page-scroll" href="#services">特色业务</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="page-scroll" href="#contact">联系我们</a>
|
||||
<a class="page-scroll" href="#contactUs">联系我们</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -202,12 +242,15 @@ function stripHtml($html) {
|
||||
<div class="swiper-button-prev"></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="feature" class="feature-section feature-style-5">
|
||||
<section id="newsCenter" class="feature-section feature-style-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-xxl-5 col-xl-5 col-lg-7 col-md-8">
|
||||
<div class="section-title text-center mb-60">
|
||||
<div class="section-title text-center mb-60" style="display: flex;align-items: center;justify-content: center;">
|
||||
<h3 class="mb-15 wow fadeInUp" data-wow-delay=".2s">新闻中心</h3>
|
||||
<a href="/news/index.php" target="_blank">
|
||||
<span style="font-size: 14px;margin-left: 50px;">Read More</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -218,30 +261,18 @@ function stripHtml($html) {
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<section id="about" class="about-section about-style-4">
|
||||
<section id="aboutUs" class="about-section about-style-4">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-xl-5 col-lg-6">
|
||||
<div class="about-content-wrapper">
|
||||
<div class="section-title mb-30">
|
||||
<h3 class="mb-25 wow fadeInUp" data-wow-delay=".2s">The future of designing starts here</h3>
|
||||
<p class="wow fadeInUp" data-wow-delay=".3s">Stop wasting time and money designing and managing a website that doesn’t get results. Happiness guaranteed,</p>
|
||||
<h3 class="mb-25 wow fadeInUp" data-wow-delay=".2s">关于我们</h3>
|
||||
<p class="wow fadeInUp" data-wow-delay=".3s">
|
||||
<?php echo !empty($companyInfo['companyintroduction']) ? $companyInfo['companyintroduction'] : ''; ?>
|
||||
</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="wow fadeInUp" data-wow-delay=".35s">
|
||||
<i class="lni lni-checkmark-circle"></i>
|
||||
Stop wasting time and money designing and managing a website that doesn’t get results.
|
||||
</li>
|
||||
<li class="wow fadeInUp" data-wow-delay=".4s">
|
||||
<i class="lni lni-checkmark-circle"></i>
|
||||
Stop wasting time and money designing and managing.
|
||||
</li>
|
||||
<li class="wow fadeInUp" data-wow-delay=".45s">
|
||||
<i class="lni lni-checkmark-circle"></i>
|
||||
Stop wasting time and money designing and managing a website that doesn’t get results.
|
||||
</li>
|
||||
</ul>
|
||||
<a href="#0" class="button button-lg radius-10 wow fadeInUp" data-wow-delay=".5s">Learn More</a>
|
||||
<!-- <a href="#0" class="button button-lg radius-10 wow fadeInUp" data-wow-delay=".5s">Learn More</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-7 col-lg-6">
|
||||
@ -252,13 +283,14 @@ function stripHtml($html) {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="pricing" class="pricing-section pricing-style-4 bg-light">
|
||||
<section id="services" class="pricing-section pricing-style-4 bg-light">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-xl-5 col-lg-6">
|
||||
<div class="section-title mb-60">
|
||||
<h3 class="mb-15 wow fadeInUp" data-wow-delay=".2s">特色业务</h3>
|
||||
<p class="wow fadeInUp" data-wow-delay=".4s">我们始终以专业、高效、创新为核心,深耕行业多年,形成了独具优势的特色业务体系。凭借成熟的服务流程、过硬的技术实力与丰富的实战经验,为客户提供一站式、定制化解决方案。</p>
|
||||
<p class="wow fadeInUp" data-wow-delay=".4s">
|
||||
我们始终以专业、高效、创新为核心,深耕行业多年,形成了独具优势的特色业务体系。凭借成熟的服务流程、过硬的技术实力与丰富的实战经验,为客户提供一站式、定制化解决方案。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-7 col-lg-6">
|
||||
@ -313,7 +345,7 @@ function stripHtml($html) {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="contact" class="contact-section contact-style-3">
|
||||
<section id="contactUs" class="contact-section contact-style-3">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-xxl-5 col-xl-5 col-lg-7 col-md-10">
|
||||
@ -360,7 +392,8 @@ function stripHtml($html) {
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-button">
|
||||
<button type="submit" class="button" id="submitBtn"> <i class="lni lni-telegram-original"></i> 提交</button>
|
||||
<button type="submit" class="button" id="submitBtn"> <i class="lni lni-telegram-original"></i>
|
||||
提交</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -492,9 +525,11 @@ function stripHtml($html) {
|
||||
<div class="col-xl-3 col-lg-4 col-md-6">
|
||||
<div class="footer-widget wow fadeInUp" data-wow-delay=".2s">
|
||||
<div class="logo">
|
||||
<a href="#0"> <img src="assets/img/logo/logo.svg" alt=""> </a>
|
||||
<a href="#0"> <img src="<?php echo !empty($companyInfo['logow']) ? $companyInfo['logow'] : 'assets/img/logo/logo.svg'; ?>" alt=""> </a>
|
||||
</div>
|
||||
<p class="desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Facilisis nulla placerat amet amet congue.</p>
|
||||
<p class="desc">
|
||||
<?php echo !empty($companyInfo['description']) ? $companyInfo['description'] : ''; ?>
|
||||
</p>
|
||||
<ul class="socials">
|
||||
<li> <a href="#0"> <i class="fa-brands fa-qq"></i> </a> </li>
|
||||
<li> <a href="#0"> <i class="fa-brands fa-weixin"></i> </a> </li>
|
||||
@ -506,11 +541,11 @@ function stripHtml($html) {
|
||||
<div class="footer-widget wow fadeInUp" data-wow-delay=".3s">
|
||||
<h6>快捷链接</h6>
|
||||
<ul class="links">
|
||||
<li> <a href="#0">主页</a> </li>
|
||||
<li> <a href="#0">关于我们</a> </li>
|
||||
<li> <a href="#0">服务</a> </li>
|
||||
<li> <a href="#0">客户评价</a> </li>
|
||||
<li> <a href="#0">联系</a> </li>
|
||||
<li> <a href="#home">主页</a> </li>
|
||||
<li> <a href="#newsCenter">新闻中心</a> </li>
|
||||
<li> <a href="#aboutUs">关于我们</a> </li>
|
||||
<li> <a href="#services">特色业务</a> </li>
|
||||
<li> <a href="#contactUs">联系我们</a> </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -564,6 +599,16 @@ function stripHtml($html) {
|
||||
<script>
|
||||
// API 接口地址配置
|
||||
const API_BASE_URL = 'https://api.yunzer.cn';
|
||||
// 主题 URL 前缀:优先从当前脚本路径推断;若脚本被路由到 /news 等,则回退到 /themes/default
|
||||
const THEME_BASE_PATH = <?php
|
||||
$scriptName = str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '');
|
||||
if ($scriptName && strpos($scriptName, '/themes/') !== false) {
|
||||
$basePath = rtrim(dirname($scriptName), '/');
|
||||
} else {
|
||||
$basePath = '/themes/default';
|
||||
}
|
||||
echo json_encode($basePath);
|
||||
?>;
|
||||
|
||||
// 加载 Banner 轮播图
|
||||
function loadBanners() {
|
||||
@ -660,21 +705,39 @@ function stripHtml($html) {
|
||||
return API_BASE_URL + (imagePath.startsWith('/') ? imagePath : '/' + imagePath);
|
||||
}
|
||||
|
||||
// 加载友情链接
|
||||
function loadFriendlinks() {
|
||||
// 加载首页数据
|
||||
function loadHomeData() {
|
||||
const container = document.getElementById('friendlink-container');
|
||||
if (!container) return;
|
||||
|
||||
fetch(API_BASE_URL + '/friendlinks')
|
||||
fetch(API_BASE_URL + '/homeData')
|
||||
.then(res => res.json())
|
||||
.then(result => {
|
||||
if (result.code !== 200 || !result.data || result.data.length === 0) {
|
||||
if (result.code !== 200 || !result.data || !result.data.links || result.data.links.length === 0) {
|
||||
container.innerHTML = '<div class="col-12 text-center"><p>暂无友情链接</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新站点信息
|
||||
if (result.data.normal) {
|
||||
const n = result.data.normal;
|
||||
if (n.sitename) document.title = n.sitename;
|
||||
if (n.ico) {
|
||||
const favicon = document.querySelector('link[rel="icon"]');
|
||||
if (favicon) {
|
||||
favicon.href = API_BASE_URL + (n.ico.startsWith('/') ? n.ico : '/' + n.ico);
|
||||
} else {
|
||||
const newFavicon = document.createElement('link');
|
||||
newFavicon.rel = 'icon';
|
||||
newFavicon.href = API_BASE_URL + (n.ico.startsWith('/') ? n.ico : '/' + n.ico);
|
||||
newFavicon.type = 'image/x-icon';
|
||||
document.head.appendChild(newFavicon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let html = '';
|
||||
result.data.forEach((item) => {
|
||||
result.data.links.forEach((item) => {
|
||||
const logoUrl = item.link_logo ? (item.link_logo.startsWith('http') ? item.link_logo : API_BASE_URL + item.link_logo) : '';
|
||||
const linkName = item.link_name || '';
|
||||
const linkUrl = item.link_url || '#';
|
||||
@ -697,13 +760,15 @@ function stripHtml($html) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 加载 Banner 轮播图
|
||||
loadBanners();
|
||||
|
||||
// 加载友情链接
|
||||
loadFriendlinks();
|
||||
// 加载首页数据
|
||||
loadHomeData();
|
||||
|
||||
// 加载新闻数据
|
||||
const container = document.getElementById('news-container');
|
||||
@ -718,9 +783,16 @@ function stripHtml($html) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newsList = result.list;
|
||||
const showCount = 8; // 初始显示数量
|
||||
const hasMore = newsList.length > showCount;
|
||||
|
||||
let html = '';
|
||||
|
||||
result.list.forEach((item, i) => {
|
||||
// 只显示前 showCount 个新闻
|
||||
const displayNews = hasMore ? newsList.slice(0, showCount) : newsList;
|
||||
|
||||
displayNews.forEach((item, i) => {
|
||||
const title = item.title || '无标题';
|
||||
let desc = item.summary || item.description || '';
|
||||
// 去除 HTML 标签后截取
|
||||
@ -736,6 +808,7 @@ function stripHtml($html) {
|
||||
'<div class="news-thumb-placeholder">暂无图片</div>';
|
||||
|
||||
html += '<div class="col-lg-3 col-md-6 mb-4">' +
|
||||
'<a href="/news/article_detail.php?id=' + item.id + '" target="_blank" class="news-item-link">' +
|
||||
'<div class="news-card wow fadeInUp" data-wow-delay=".2s">' +
|
||||
'<div class="news-thumb-wrap">' +
|
||||
thumbHtml +
|
||||
@ -745,9 +818,17 @@ function stripHtml($html) {
|
||||
'<p class="news-desc">' + desc + '</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</a>' +
|
||||
'</div>';
|
||||
});
|
||||
|
||||
// 如果有更多新闻,添加查看更多按钮
|
||||
if (hasMore) {
|
||||
html += '<div class="col-12 text-center mt-4">' +
|
||||
'<a href="/news" class="btn btn-primary">查看更多</a>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
})
|
||||
.catch(err => {
|
||||
@ -759,6 +840,14 @@ function stripHtml($html) {
|
||||
|
||||
<!-- Banner 轮播图样式 -->
|
||||
<style>
|
||||
.desc {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.banner-swiper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
403
public/themes/template3/about.html
Normal file
@ -0,0 +1,403 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<base href="/themes/template3/">
|
||||
<title>About - Nova Bootstrap Template</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="keywords" content="">
|
||||
|
||||
<!-- Favicons -->
|
||||
<link href="assets/img/favicon.png" rel="icon">
|
||||
<link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
|
||||
|
||||
<!-- Vendor CSS Files -->
|
||||
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="assets/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="assets/vendor/aos/aos.css" rel="stylesheet">
|
||||
<link href="assets/vendor/glightbox/css/glightbox.min.css" rel="stylesheet">
|
||||
<link href="assets/vendor/swiper/swiper-bundle.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Main CSS File -->
|
||||
<link href="assets/css/main.css" rel="stylesheet">
|
||||
|
||||
<!-- =======================================================
|
||||
* Template Name: Nova
|
||||
* Template URL: https://bootstrapmade.com/nova-bootstrap-business-template/
|
||||
* Updated: Aug 07 2024 with Bootstrap v5.3.3
|
||||
* Author: BootstrapMade.com
|
||||
* License: https://bootstrapmade.com/license/
|
||||
======================================================== -->
|
||||
</head>
|
||||
|
||||
<body class="about-page">
|
||||
|
||||
<header id="header" class="header d-flex align-items-center fixed-top">
|
||||
<div class="container-fluid container-xl position-relative d-flex align-items-center justify-content-between">
|
||||
|
||||
<a href="index.html" class="logo d-flex align-items-center">
|
||||
<!-- Uncomment the line below if you also wish to use an image logo -->
|
||||
<!-- <img src="assets/img/logo.png" alt=""> -->
|
||||
<h1 class="sitename">Nova</h1>
|
||||
</a>
|
||||
|
||||
<nav id="navmenu" class="navmenu">
|
||||
<ul>
|
||||
<li><a href="/index.html" class="active">Home<br></a></li>
|
||||
<li><a href="/about.html">About</a></li>
|
||||
<li><a href="/services.html">Services</a></li>
|
||||
<li><a href="/portfolio.html">Portfolio</a></li>
|
||||
<li><a href="/team.html">Team</a></li>
|
||||
<li><a href="/blog.html">Blog</a></li>
|
||||
<li class="dropdown"><a href="#"><span>Dropdown</span> <i class="bi bi-chevron-down toggle-dropdown"></i></a>
|
||||
<ul>
|
||||
<li><a href="#">Dropdown 1</a></li>
|
||||
<li class="dropdown"><a href="#"><span>Deep Dropdown</span> <i class="bi bi-chevron-down toggle-dropdown"></i></a>
|
||||
<ul>
|
||||
<li><a href="#">Deep Dropdown 1</a></li>
|
||||
<li><a href="#">Deep Dropdown 2</a></li>
|
||||
<li><a href="#">Deep Dropdown 3</a></li>
|
||||
<li><a href="#">Deep Dropdown 4</a></li>
|
||||
<li><a href="#">Deep Dropdown 5</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#">Dropdown 2</a></li>
|
||||
<li><a href="#">Dropdown 3</a></li>
|
||||
<li><a href="#">Dropdown 4</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/contact.html">Contact</a></li>
|
||||
</ul>
|
||||
<i class="mobile-nav-toggle d-xl-none bi bi-list"></i>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
|
||||
<!-- Page Title -->
|
||||
<div class="page-title dark-background" data-aos="fade" style="background-image: url(assets/img/about-page-title-bg.jpg);">
|
||||
<div class="container">
|
||||
<h1>About</h1>
|
||||
<nav class="breadcrumbs">
|
||||
<ol>
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li class="current">About</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div><!-- End Page Title -->
|
||||
|
||||
<!-- About Section -->
|
||||
<section id="about" class="about section">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row gy-4" data-aos="fade-up" data-aos-delay="100">
|
||||
<div class="col-lg-5">
|
||||
<img src="assets/img/about.jpg" class="img-fluid" alt="">
|
||||
</div>
|
||||
<div class="col-lg-7" data-aos="fade-up" data-aos-delay="200">
|
||||
<div class="content">
|
||||
<h3>Voluptatem dignissimos provident quasi</h3>
|
||||
<p>
|
||||
Ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident
|
||||
</p>
|
||||
<ul>
|
||||
<li><i class="bi bi-check-circle-fill"></i> <span>Ullamco laboris nisi ut aliquip ex ea commodo consequat.</span></li>
|
||||
<li><i class="bi bi-check-circle-fill"></i> <span>Duis aute irure dolor in reprehenderit in voluptate velit.</span></li>
|
||||
<li><i class="bi bi-check-circle-fill"></i> <span>Ullamco laboris nisi ut aliquip ex ea commodo consequat.</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section><!-- /About Section -->
|
||||
|
||||
<!-- Why Us Section -->
|
||||
<section id="why-us" class="why-us section">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row g-0">
|
||||
|
||||
<div class="col-xl-5 img-bg" data-aos="fade-up" data-aos-delay="100">
|
||||
<img src="assets/img/why-us-bg.jpg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="col-xl-7 slides position-relative" data-aos="fade-up" data-aos-delay="200">
|
||||
|
||||
<div class="swiper init-swiper">
|
||||
<script type="application/json" class="swiper-config">
|
||||
{
|
||||
"loop": true,
|
||||
"speed": 600,
|
||||
"autoplay": {
|
||||
"delay": 5000
|
||||
},
|
||||
"slidesPerView": "auto",
|
||||
"centeredSlides": true,
|
||||
"pagination": {
|
||||
"el": ".swiper-pagination",
|
||||
"type": "bullets",
|
||||
"clickable": true
|
||||
},
|
||||
"navigation": {
|
||||
"nextEl": ".swiper-button-next",
|
||||
"prevEl": ".swiper-button-prev"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="swiper-wrapper">
|
||||
|
||||
<div class="swiper-slide">
|
||||
<div class="item">
|
||||
<h3 class="mb-3">Let's grow your business together</h3>
|
||||
<h4 class="mb-3">Optio reiciendis accusantium iusto architecto at quia minima maiores quidem, dolorum.</h4>
|
||||
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus, ipsam perferendis asperiores explicabo vel tempore velit totam, natus nesciunt accusantium dicta quod quibusdam ipsum maiores nobis non, eum. Ullam reiciendis dignissimos laborum aut, magni voluptatem velit doloribus quas sapiente optio.</p>
|
||||
</div>
|
||||
</div><!-- End slide item -->
|
||||
|
||||
<div class="swiper-slide">
|
||||
<div class="item">
|
||||
<h3 class="mb-3">Unde perspiciatis ut repellat dolorem</h3>
|
||||
<h4 class="mb-3">Amet cumque nam sed voluptas doloribus iusto. Dolorem eos aliquam quis.</h4>
|
||||
<p>Dolorem quia fuga consectetur voluptatem. Earum consequatur nulla maxime necessitatibus cum accusamus. Voluptatem dolorem ut numquam dolorum delectus autem veritatis facilis. Et ea ut repellat ea. Facere est dolores fugiat dolor.</p>
|
||||
</div>
|
||||
</div><!-- End slide item -->
|
||||
|
||||
<div class="swiper-slide">
|
||||
<div class="item">
|
||||
<h3 class="mb-3">Aliquid non alias minus</h3>
|
||||
<h4 class="mb-3">Necessitatibus voluptatibus explicabo dolores a vitae voluptatum.</h4>
|
||||
<p>Neque voluptates aut. Soluta aut perspiciatis porro deserunt. Voluptate ut itaque velit. Aut consectetur voluptatem aspernatur sequi sit laborum. Voluptas enim dolorum fugiat aut.</p>
|
||||
</div>
|
||||
</div><!-- End slide item -->
|
||||
|
||||
<div class="swiper-slide">
|
||||
<div class="item">
|
||||
<h3 class="mb-3">Necessitatibus suscipit non voluptatem quibusdam</h3>
|
||||
<h4 class="mb-3">Tempora quos est ut quia adipisci ut voluptas. Deleniti laborum soluta nihil est. Eum similique neque autem ut.</h4>
|
||||
<p>Ut rerum et autem vel. Et rerum molestiae aut sit vel incidunt sit at voluptatem. Saepe dolorem et sed voluptate impedit. Ad et qui sint at qui animi animi rerum.</p>
|
||||
</div>
|
||||
</div><!-- End slide item -->
|
||||
|
||||
</div>
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
|
||||
<div class="swiper-button-prev"></div>
|
||||
<div class="swiper-button-next"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section><!-- /Why Us Section -->
|
||||
|
||||
<!-- Call To Action Section -->
|
||||
<section id="call-to-action" class="call-to-action section dark-background">
|
||||
|
||||
<img src="assets/img/cta-bg.jpg" alt="">
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center" data-aos="zoom-in" data-aos-delay="100">
|
||||
<div class="col-xl-10">
|
||||
<div class="text-center">
|
||||
<h3>Call To Action</h3>
|
||||
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<a class="cta-btn" href="#">Call To Action</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section><!-- /Call To Action Section -->
|
||||
|
||||
<!-- Team Section -->
|
||||
<section id="team" class="team section">
|
||||
|
||||
<!-- Section Title -->
|
||||
<div class="container section-title" data-aos="fade-up">
|
||||
<h2>Our Team</h2>
|
||||
<p>Necessitatibus eius consequatur ex aliquid fuga eum quidem sint consectetur velit</p>
|
||||
</div><!-- End Section Title -->
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row gy-4">
|
||||
|
||||
<div class="col-lg-3 col-md-6 d-flex align-items-stretch" data-aos="fade-up" data-aos-delay="100">
|
||||
<div class="team-member">
|
||||
<div class="member-img">
|
||||
<img src="assets/img/team/team-1.jpg" class="img-fluid" alt="">
|
||||
<div class="social">
|
||||
<a href=""><i class="bi bi-twitter-x"></i></a>
|
||||
<a href=""><i class="bi bi-facebook"></i></a>
|
||||
<a href=""><i class="bi bi-instagram"></i></a>
|
||||
<a href=""><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-info">
|
||||
<h4>Walter White</h4>
|
||||
<span>Chief Executive Officer</span>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- End Team Member -->
|
||||
|
||||
<div class="col-lg-3 col-md-6 d-flex align-items-stretch" data-aos="fade-up" data-aos-delay="200">
|
||||
<div class="team-member">
|
||||
<div class="member-img">
|
||||
<img src="assets/img/team/team-2.jpg" class="img-fluid" alt="">
|
||||
<div class="social">
|
||||
<a href=""><i class="bi bi-twitter-x"></i></a>
|
||||
<a href=""><i class="bi bi-facebook"></i></a>
|
||||
<a href=""><i class="bi bi-instagram"></i></a>
|
||||
<a href=""><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-info">
|
||||
<h4>Sarah Jhonson</h4>
|
||||
<span>Product Manager</span>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- End Team Member -->
|
||||
|
||||
<div class="col-lg-3 col-md-6 d-flex align-items-stretch" data-aos="fade-up" data-aos-delay="300">
|
||||
<div class="team-member">
|
||||
<div class="member-img">
|
||||
<img src="assets/img/team/team-3.jpg" class="img-fluid" alt="">
|
||||
<div class="social">
|
||||
<a href=""><i class="bi bi-twitter-x"></i></a>
|
||||
<a href=""><i class="bi bi-facebook"></i></a>
|
||||
<a href=""><i class="bi bi-instagram"></i></a>
|
||||
<a href=""><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-info">
|
||||
<h4>William Anderson</h4>
|
||||
<span>CTO</span>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- End Team Member -->
|
||||
|
||||
<div class="col-lg-3 col-md-6 d-flex align-items-stretch" data-aos="fade-up" data-aos-delay="400">
|
||||
<div class="team-member">
|
||||
<div class="member-img">
|
||||
<img src="assets/img/team/team-4.jpg" class="img-fluid" alt="">
|
||||
<div class="social">
|
||||
<a href=""><i class="bi bi-twitter-x"></i></a>
|
||||
<a href=""><i class="bi bi-facebook"></i></a>
|
||||
<a href=""><i class="bi bi-instagram"></i></a>
|
||||
<a href=""><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-info">
|
||||
<h4>Amanda Jepson</h4>
|
||||
<span>Accountant</span>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- End Team Member -->
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section><!-- /Team Section -->
|
||||
|
||||
</main>
|
||||
|
||||
<footer id="footer" class="footer light-background">
|
||||
|
||||
<div class="footer-top">
|
||||
<div class="container">
|
||||
<div class="row gy-4">
|
||||
<div class="col-lg-5 col-md-12 footer-about">
|
||||
<a href="index.html" class="logo d-flex align-items-center">
|
||||
<span class="sitename">Nova</span>
|
||||
</a>
|
||||
<p>Cras fermentum odio eu feugiat lide par naso tierra. Justo eget nada terra videa magna derita valies darta donna mare fermentum iaculis eu non diam phasellus.</p>
|
||||
<div class="social-links d-flex mt-4">
|
||||
<a href=""><i class="bi bi-twitter-x"></i></a>
|
||||
<a href=""><i class="bi bi-facebook"></i></a>
|
||||
<a href=""><i class="bi bi-instagram"></i></a>
|
||||
<a href=""><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2 col-6 footer-links">
|
||||
<h4>Useful Links</h4>
|
||||
<ul>
|
||||
<li><a href="#">Home</a></li>
|
||||
<li><a href="#">About us</a></li>
|
||||
<li><a href="#">Services</a></li>
|
||||
<li><a href="#">Terms of service</a></li>
|
||||
<li><a href="#">Privacy policy</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2 col-6 footer-links">
|
||||
<h4>Our Services</h4>
|
||||
<ul>
|
||||
<li><a href="#">Web Design</a></li>
|
||||
<li><a href="#">Web Development</a></li>
|
||||
<li><a href="#">Product Management</a></li>
|
||||
<li><a href="#">Marketing</a></li>
|
||||
<li><a href="#">Graphic Design</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 col-md-12 footer-contact text-center text-md-start">
|
||||
<h4>Contact Us</h4>
|
||||
<p>A108 Adam Street</p>
|
||||
<p>New York, NY 535022</p>
|
||||
<p>United States</p>
|
||||
<p class="mt-4"><strong>Phone:</strong> <span>+1 5589 55488 55</span></p>
|
||||
<p><strong>Email:</strong> <span>info@example.com</span></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container copyright text-center">
|
||||
<p>© <span>Copyright</span> <strong class="px-1 sitename">Nova</strong> <span>All Rights Reserved</span></p>
|
||||
<div class="credits">
|
||||
<!-- All the links in the footer should remain intact. -->
|
||||
<!-- You can delete the links only if you've purchased the pro version. -->
|
||||
<!-- Licensing information: https://bootstrapmade.com/license/ -->
|
||||
<!-- Purchase the pro version with working PHP/AJAX contact form: [buy-url] -->
|
||||
Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a> Distributed by <a href="https://themewagon.com">ThemeWagon</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
|
||||
<!-- Scroll Top -->
|
||||
<a href="#" id="scroll-top" class="scroll-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
|
||||
|
||||
<!-- Preloader -->
|
||||
<div id="preloader"></div>
|
||||
|
||||
<!-- Vendor JS Files -->
|
||||
<script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/vendor/php-email-form/validate.js"></script>
|
||||
<script src="assets/vendor/aos/aos.js"></script>
|
||||
<script src="assets/vendor/glightbox/js/glightbox.min.js"></script>
|
||||
<script src="assets/vendor/swiper/swiper-bundle.min.js"></script>
|
||||
<script src="assets/vendor/imagesloaded/imagesloaded.pkgd.min.js"></script>
|
||||
<script src="assets/vendor/isotope-layout/isotope.pkgd.min.js"></script>
|
||||
|
||||
<!-- Main JS File -->
|
||||
<script src="assets/js/main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
2448
public/themes/template3/assets/css/main.css
Normal file
BIN
public/themes/template3/assets/img/about-page-title-bg.jpg
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
public/themes/template3/assets/img/about.jpg
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
public/themes/template3/assets/img/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/themes/template3/assets/img/blog-page-title-bg.jpg
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
public/themes/template3/assets/img/blog/blog-1.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
public/themes/template3/assets/img/blog/blog-2.jpg
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
public/themes/template3/assets/img/blog/blog-3.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
public/themes/template3/assets/img/blog/blog-4.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
public/themes/template3/assets/img/blog/blog-5.jpg
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
public/themes/template3/assets/img/blog/blog-6.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
public/themes/template3/assets/img/blog/blog-author-2.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/themes/template3/assets/img/blog/blog-author-3.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/themes/template3/assets/img/blog/blog-author-4.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/themes/template3/assets/img/blog/blog-author-5.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/themes/template3/assets/img/blog/blog-author-6.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/themes/template3/assets/img/blog/blog-author.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/themes/template3/assets/img/blog/blog-inside-post.jpg
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
public/themes/template3/assets/img/blog/blog-recent-1.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
public/themes/template3/assets/img/blog/blog-recent-2.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/themes/template3/assets/img/blog/blog-recent-3.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
public/themes/template3/assets/img/blog/blog-recent-4.jpg
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
public/themes/template3/assets/img/blog/blog-recent-5.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
public/themes/template3/assets/img/blog/comments-1.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/themes/template3/assets/img/blog/comments-2.jpg
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/themes/template3/assets/img/blog/comments-3.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/themes/template3/assets/img/blog/comments-4.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/themes/template3/assets/img/blog/comments-5.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/themes/template3/assets/img/blog/comments-6.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/themes/template3/assets/img/cards-1.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
public/themes/template3/assets/img/cards-3.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
public/themes/template3/assets/img/cards-4.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/themes/template3/assets/img/cards-5.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/themes/template3/assets/img/contact-bg.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/themes/template3/assets/img/contact-page-title-bg.jpg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
public/themes/template3/assets/img/cta-bg.jpg
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
public/themes/template3/assets/img/favicon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/themes/template3/assets/img/hero-bg.jpg
Normal file
|
After Width: | Height: | Size: 226 KiB |
BIN
public/themes/template3/assets/img/iphone.png
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
public/themes/template3/assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/themes/template3/assets/img/portfolio-page-title-bg.jpg
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
public/themes/template3/assets/img/portfolio/app-1.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
public/themes/template3/assets/img/portfolio/app-2.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/themes/template3/assets/img/portfolio/app-3.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/themes/template3/assets/img/portfolio/books-1.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/themes/template3/assets/img/portfolio/books-2.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/themes/template3/assets/img/portfolio/books-3.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
public/themes/template3/assets/img/portfolio/branding-1.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/themes/template3/assets/img/portfolio/branding-2.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/themes/template3/assets/img/portfolio/branding-3.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/themes/template3/assets/img/portfolio/product-1.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/themes/template3/assets/img/portfolio/product-2.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/themes/template3/assets/img/portfolio/product-3.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
public/themes/template3/assets/img/services-page-title-bg.jpg
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
public/themes/template3/assets/img/services.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/themes/template3/assets/img/team-page-title-bg.jpg
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
public/themes/template3/assets/img/team/team-1.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/themes/template3/assets/img/team/team-2.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/themes/template3/assets/img/team/team-3.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/themes/template3/assets/img/team/team-4.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 22 KiB |
BIN
public/themes/template3/assets/img/why-us-bg.jpg
Normal file
|
After Width: | Height: | Size: 226 KiB |
164
public/themes/template3/assets/js/main.js
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Template Name: Nova
|
||||
* Template URL: https://bootstrapmade.com/nova-bootstrap-business-template/
|
||||
* Updated: Aug 07 2024 with Bootstrap v5.3.3
|
||||
* Author: BootstrapMade.com
|
||||
* License: https://bootstrapmade.com/license/
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Apply .scrolled class to the body as the page is scrolled down
|
||||
*/
|
||||
function toggleScrolled() {
|
||||
const selectBody = document.querySelector('body');
|
||||
const selectHeader = document.querySelector('#header');
|
||||
if (!selectHeader.classList.contains('scroll-up-sticky') && !selectHeader.classList.contains('sticky-top') && !selectHeader.classList.contains('fixed-top')) return;
|
||||
window.scrollY > 100 ? selectBody.classList.add('scrolled') : selectBody.classList.remove('scrolled');
|
||||
}
|
||||
|
||||
document.addEventListener('scroll', toggleScrolled);
|
||||
window.addEventListener('load', toggleScrolled);
|
||||
|
||||
/**
|
||||
* Mobile nav toggle
|
||||
*/
|
||||
const mobileNavToggleBtn = document.querySelector('.mobile-nav-toggle');
|
||||
|
||||
function mobileNavToogle() {
|
||||
document.querySelector('body').classList.toggle('mobile-nav-active');
|
||||
mobileNavToggleBtn.classList.toggle('bi-list');
|
||||
mobileNavToggleBtn.classList.toggle('bi-x');
|
||||
}
|
||||
mobileNavToggleBtn.addEventListener('click', mobileNavToogle);
|
||||
|
||||
/**
|
||||
* Hide mobile nav on same-page/hash links
|
||||
*/
|
||||
document.querySelectorAll('#navmenu a').forEach(navmenu => {
|
||||
navmenu.addEventListener('click', () => {
|
||||
if (document.querySelector('.mobile-nav-active')) {
|
||||
mobileNavToogle();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggle mobile nav dropdowns
|
||||
*/
|
||||
document.querySelectorAll('.navmenu .toggle-dropdown').forEach(navmenu => {
|
||||
navmenu.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
this.parentNode.classList.toggle('active');
|
||||
this.parentNode.nextElementSibling.classList.toggle('dropdown-active');
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Preloader
|
||||
*/
|
||||
const preloader = document.querySelector('#preloader');
|
||||
if (preloader) {
|
||||
window.addEventListener('load', () => {
|
||||
preloader.remove();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll top button
|
||||
*/
|
||||
let scrollTop = document.querySelector('.scroll-top');
|
||||
|
||||
function toggleScrollTop() {
|
||||
if (scrollTop) {
|
||||
window.scrollY > 100 ? scrollTop.classList.add('active') : scrollTop.classList.remove('active');
|
||||
}
|
||||
}
|
||||
scrollTop.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('load', toggleScrollTop);
|
||||
document.addEventListener('scroll', toggleScrollTop);
|
||||
|
||||
/**
|
||||
* Animation on scroll function and init
|
||||
*/
|
||||
function aosInit() {
|
||||
AOS.init({
|
||||
duration: 600,
|
||||
easing: 'ease-in-out',
|
||||
once: true,
|
||||
mirror: false
|
||||
});
|
||||
}
|
||||
window.addEventListener('load', aosInit);
|
||||
|
||||
/**
|
||||
* Initiate glightbox
|
||||
*/
|
||||
const glightbox = GLightbox({
|
||||
selector: '.glightbox'
|
||||
});
|
||||
|
||||
/**
|
||||
* Init swiper sliders
|
||||
*/
|
||||
function initSwiper() {
|
||||
document.querySelectorAll(".init-swiper").forEach(function(swiperElement) {
|
||||
let config = JSON.parse(
|
||||
swiperElement.querySelector(".swiper-config").innerHTML.trim()
|
||||
);
|
||||
|
||||
if (swiperElement.classList.contains("swiper-tab")) {
|
||||
initSwiperWithCustomPagination(swiperElement, config);
|
||||
} else {
|
||||
new Swiper(swiperElement, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("load", initSwiper);
|
||||
|
||||
/**
|
||||
* Init isotope layout and filters
|
||||
*/
|
||||
document.querySelectorAll('.isotope-layout').forEach(function(isotopeItem) {
|
||||
let layout = isotopeItem.getAttribute('data-layout') ?? 'masonry';
|
||||
let filter = isotopeItem.getAttribute('data-default-filter') ?? '*';
|
||||
let sort = isotopeItem.getAttribute('data-sort') ?? 'original-order';
|
||||
|
||||
let initIsotope;
|
||||
imagesLoaded(isotopeItem.querySelector('.isotope-container'), function() {
|
||||
initIsotope = new Isotope(isotopeItem.querySelector('.isotope-container'), {
|
||||
itemSelector: '.isotope-item',
|
||||
layoutMode: layout,
|
||||
filter: filter,
|
||||
sortBy: sort
|
||||
});
|
||||
});
|
||||
|
||||
isotopeItem.querySelectorAll('.isotope-filters li').forEach(function(filters) {
|
||||
filters.addEventListener('click', function() {
|
||||
isotopeItem.querySelector('.isotope-filters .filter-active').classList.remove('filter-active');
|
||||
this.classList.add('filter-active');
|
||||
initIsotope.arrange({
|
||||
filter: this.getAttribute('data-filter')
|
||||
});
|
||||
if (typeof aosInit === 'function') {
|
||||
aosInit();
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
2
public/themes/template3/assets/scss/Readme.txt
Normal file
@ -0,0 +1,2 @@
|
||||
The .scss (Sass) files are only available in the pro version.
|
||||
You can buy it from: https://bootstrapmade.com/nova-bootstrap-business-template/
|
||||
614
public/themes/template3/assets/vendor/aos/aos.cjs.js
vendored
Normal file
@ -0,0 +1,614 @@
|
||||
'use strict';
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
|
||||
var throttle = _interopDefault(require('lodash.throttle'));
|
||||
var debounce = _interopDefault(require('lodash.debounce'));
|
||||
|
||||
var callback = function callback() {};
|
||||
|
||||
function containsAOSNode(nodes) {
|
||||
var i = void 0,
|
||||
currentNode = void 0,
|
||||
result = void 0;
|
||||
|
||||
for (i = 0; i < nodes.length; i += 1) {
|
||||
currentNode = nodes[i];
|
||||
|
||||
if (currentNode.dataset && currentNode.dataset.aos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
result = currentNode.children && containsAOSNode(currentNode.children);
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function check(mutations) {
|
||||
if (!mutations) return;
|
||||
|
||||
mutations.forEach(function (mutation) {
|
||||
var addedNodes = Array.prototype.slice.call(mutation.addedNodes);
|
||||
var removedNodes = Array.prototype.slice.call(mutation.removedNodes);
|
||||
var allNodes = addedNodes.concat(removedNodes);
|
||||
|
||||
if (containsAOSNode(allNodes)) {
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMutationObserver() {
|
||||
return window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
|
||||
}
|
||||
|
||||
function isSupported() {
|
||||
return !!getMutationObserver();
|
||||
}
|
||||
|
||||
function ready(selector, fn) {
|
||||
var doc = window.document;
|
||||
var MutationObserver = getMutationObserver();
|
||||
|
||||
var observer = new MutationObserver(check);
|
||||
callback = fn;
|
||||
|
||||
observer.observe(doc.documentElement, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
removedNodes: true
|
||||
});
|
||||
}
|
||||
|
||||
var observer = { isSupported: isSupported, ready: ready };
|
||||
|
||||
var classCallCheck = function (instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
};
|
||||
|
||||
var createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
var _extends = Object.assign || function (target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
|
||||
for (var key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
/**
|
||||
* Device detector
|
||||
*/
|
||||
|
||||
var fullNameRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
|
||||
var prefixRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||
var fullNameMobileRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
|
||||
var prefixMobileRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||
|
||||
function ua() {
|
||||
return navigator.userAgent || navigator.vendor || window.opera || '';
|
||||
}
|
||||
|
||||
var Detector = function () {
|
||||
function Detector() {
|
||||
classCallCheck(this, Detector);
|
||||
}
|
||||
|
||||
createClass(Detector, [{
|
||||
key: 'phone',
|
||||
value: function phone() {
|
||||
var a = ua();
|
||||
return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
|
||||
}
|
||||
}, {
|
||||
key: 'mobile',
|
||||
value: function mobile() {
|
||||
var a = ua();
|
||||
return !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
|
||||
}
|
||||
}, {
|
||||
key: 'tablet',
|
||||
value: function tablet() {
|
||||
return this.mobile() && !this.phone();
|
||||
}
|
||||
|
||||
// http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c
|
||||
|
||||
}, {
|
||||
key: 'ie11',
|
||||
value: function ie11() {
|
||||
return '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
|
||||
}
|
||||
}]);
|
||||
return Detector;
|
||||
}();
|
||||
|
||||
var detect = new Detector();
|
||||
|
||||
/**
|
||||
* Adds multiple classes on node
|
||||
* @param {DOMNode} node
|
||||
* @param {array} classes
|
||||
*/
|
||||
var addClasses = function addClasses(node, classes) {
|
||||
return classes && classes.forEach(function (className) {
|
||||
return node.classList.add(className);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes multiple classes from node
|
||||
* @param {DOMNode} node
|
||||
* @param {array} classes
|
||||
*/
|
||||
var removeClasses = function removeClasses(node, classes) {
|
||||
return classes && classes.forEach(function (className) {
|
||||
return node.classList.remove(className);
|
||||
});
|
||||
};
|
||||
|
||||
var fireEvent = function fireEvent(eventName, data) {
|
||||
var customEvent = void 0;
|
||||
|
||||
if (detect.ie11()) {
|
||||
customEvent = document.createEvent('CustomEvent');
|
||||
customEvent.initCustomEvent(eventName, true, true, { detail: data });
|
||||
} else {
|
||||
customEvent = new CustomEvent(eventName, {
|
||||
detail: data
|
||||
});
|
||||
}
|
||||
|
||||
return document.dispatchEvent(customEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or remove aos-animate class
|
||||
* @param {node} el element
|
||||
* @param {int} top scrolled distance
|
||||
*/
|
||||
var applyClasses = function applyClasses(el, top) {
|
||||
var options = el.options,
|
||||
position = el.position,
|
||||
node = el.node,
|
||||
data = el.data;
|
||||
|
||||
|
||||
var hide = function hide() {
|
||||
if (!el.animated) return;
|
||||
|
||||
removeClasses(node, options.animatedClassNames);
|
||||
fireEvent('aos:out', node);
|
||||
|
||||
if (el.options.id) {
|
||||
fireEvent('aos:in:' + el.options.id, node);
|
||||
}
|
||||
|
||||
el.animated = false;
|
||||
};
|
||||
|
||||
var show = function show() {
|
||||
if (el.animated) return;
|
||||
|
||||
addClasses(node, options.animatedClassNames);
|
||||
|
||||
fireEvent('aos:in', node);
|
||||
if (el.options.id) {
|
||||
fireEvent('aos:in:' + el.options.id, node);
|
||||
}
|
||||
|
||||
el.animated = true;
|
||||
};
|
||||
|
||||
if (options.mirror && top >= position.out && !options.once) {
|
||||
hide();
|
||||
} else if (top >= position.in) {
|
||||
show();
|
||||
} else if (el.animated && !options.once) {
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll logic - add or remove 'aos-animate' class on scroll
|
||||
*
|
||||
* @param {array} $elements array of elements nodes
|
||||
* @return {void}
|
||||
*/
|
||||
var handleScroll = function handleScroll($elements) {
|
||||
return $elements.forEach(function (el, i) {
|
||||
return applyClasses(el, window.pageYOffset);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get offset of DOM element
|
||||
* like there were no transforms applied on it
|
||||
*
|
||||
* @param {Node} el [DOM element]
|
||||
* @return {Object} [top and left offset]
|
||||
*/
|
||||
var offset = function offset(el) {
|
||||
var _x = 0;
|
||||
var _y = 0;
|
||||
|
||||
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
|
||||
_x += el.offsetLeft - (el.tagName != 'BODY' ? el.scrollLeft : 0);
|
||||
_y += el.offsetTop - (el.tagName != 'BODY' ? el.scrollTop : 0);
|
||||
el = el.offsetParent;
|
||||
}
|
||||
|
||||
return {
|
||||
top: _y,
|
||||
left: _x
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get inline option with a fallback.
|
||||
*
|
||||
* @param {Node} el [Dom element]
|
||||
* @param {String} key [Option key]
|
||||
* @param {String} fallback [Default (fallback) value]
|
||||
* @return {Mixed} [Option set with inline attributes or fallback value if not set]
|
||||
*/
|
||||
|
||||
var getInlineOption = (function (el, key, fallback) {
|
||||
var attr = el.getAttribute('data-aos-' + key);
|
||||
|
||||
if (typeof attr !== 'undefined') {
|
||||
if (attr === 'true') {
|
||||
return true;
|
||||
} else if (attr === 'false') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return attr || fallback;
|
||||
});
|
||||
|
||||
/**
|
||||
* Calculate offset
|
||||
* basing on element's settings like:
|
||||
* - anchor
|
||||
* - offset
|
||||
*
|
||||
* @param {Node} el [Dom element]
|
||||
* @return {Integer} [Final offset that will be used to trigger animation in good position]
|
||||
*/
|
||||
|
||||
var getPositionIn = function getPositionIn(el, defaultOffset, defaultAnchorPlacement) {
|
||||
var windowHeight = window.innerHeight;
|
||||
var anchor = getInlineOption(el, 'anchor');
|
||||
var inlineAnchorPlacement = getInlineOption(el, 'anchor-placement');
|
||||
var additionalOffset = Number(getInlineOption(el, 'offset', inlineAnchorPlacement ? 0 : defaultOffset));
|
||||
var anchorPlacement = inlineAnchorPlacement || defaultAnchorPlacement;
|
||||
var finalEl = el;
|
||||
|
||||
if (anchor && document.querySelectorAll(anchor)) {
|
||||
finalEl = document.querySelectorAll(anchor)[0];
|
||||
}
|
||||
|
||||
var triggerPoint = offset(finalEl).top - windowHeight;
|
||||
|
||||
switch (anchorPlacement) {
|
||||
case 'top-bottom':
|
||||
// Default offset
|
||||
break;
|
||||
case 'center-bottom':
|
||||
triggerPoint += finalEl.offsetHeight / 2;
|
||||
break;
|
||||
case 'bottom-bottom':
|
||||
triggerPoint += finalEl.offsetHeight;
|
||||
break;
|
||||
case 'top-center':
|
||||
triggerPoint += windowHeight / 2;
|
||||
break;
|
||||
case 'center-center':
|
||||
triggerPoint += windowHeight / 2 + finalEl.offsetHeight / 2;
|
||||
break;
|
||||
case 'bottom-center':
|
||||
triggerPoint += windowHeight / 2 + finalEl.offsetHeight;
|
||||
break;
|
||||
case 'top-top':
|
||||
triggerPoint += windowHeight;
|
||||
break;
|
||||
case 'bottom-top':
|
||||
triggerPoint += windowHeight + finalEl.offsetHeight;
|
||||
break;
|
||||
case 'center-top':
|
||||
triggerPoint += windowHeight + finalEl.offsetHeight / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return triggerPoint + additionalOffset;
|
||||
};
|
||||
|
||||
var getPositionOut = function getPositionOut(el, defaultOffset) {
|
||||
var windowHeight = window.innerHeight;
|
||||
var anchor = getInlineOption(el, 'anchor');
|
||||
var additionalOffset = getInlineOption(el, 'offset', defaultOffset);
|
||||
var finalEl = el;
|
||||
|
||||
if (anchor && document.querySelectorAll(anchor)) {
|
||||
finalEl = document.querySelectorAll(anchor)[0];
|
||||
}
|
||||
|
||||
var elementOffsetTop = offset(finalEl).top;
|
||||
|
||||
return elementOffsetTop + finalEl.offsetHeight - additionalOffset;
|
||||
};
|
||||
|
||||
/* Clearing variables */
|
||||
|
||||
var prepare = function prepare($elements, options) {
|
||||
$elements.forEach(function (el, i) {
|
||||
var mirror = getInlineOption(el.node, 'mirror', options.mirror);
|
||||
var once = getInlineOption(el.node, 'once', options.once);
|
||||
var id = getInlineOption(el.node, 'id');
|
||||
var customClassNames = options.useClassNames && el.node.getAttribute('data-aos');
|
||||
|
||||
var animatedClassNames = [options.animatedClassName].concat(customClassNames ? customClassNames.split(' ') : []).filter(function (className) {
|
||||
return typeof className === 'string';
|
||||
});
|
||||
|
||||
if (options.initClassName) {
|
||||
el.node.classList.add(options.initClassName);
|
||||
}
|
||||
|
||||
el.position = {
|
||||
in: getPositionIn(el.node, options.offset, options.anchorPlacement),
|
||||
out: mirror && getPositionOut(el.node, options.offset)
|
||||
};
|
||||
|
||||
el.options = {
|
||||
once: once,
|
||||
mirror: mirror,
|
||||
animatedClassNames: animatedClassNames,
|
||||
id: id
|
||||
};
|
||||
});
|
||||
|
||||
return $elements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate initial array with elements as objects
|
||||
* This array will be extended later with elements attributes values
|
||||
* like 'position'
|
||||
*/
|
||||
var elements = (function () {
|
||||
var elements = document.querySelectorAll('[data-aos]');
|
||||
return Array.prototype.map.call(elements, function (node) {
|
||||
return { node: node };
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* *******************************************************
|
||||
* AOS (Animate on scroll) - wowjs alternative
|
||||
* made to animate elements on scroll in both directions
|
||||
* *******************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Private variables
|
||||
*/
|
||||
var $aosElements = [];
|
||||
var initialized = false;
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
var options = {
|
||||
offset: 120,
|
||||
delay: 0,
|
||||
easing: 'ease',
|
||||
duration: 400,
|
||||
disable: false,
|
||||
once: false,
|
||||
mirror: false,
|
||||
anchorPlacement: 'top-bottom',
|
||||
startEvent: 'DOMContentLoaded',
|
||||
animatedClassName: 'aos-animate',
|
||||
initClassName: 'aos-init',
|
||||
useClassNames: false,
|
||||
disableMutationObserver: false,
|
||||
throttleDelay: 99,
|
||||
debounceDelay: 50
|
||||
};
|
||||
|
||||
// Detect not supported browsers (<=IE9)
|
||||
// http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
|
||||
var isBrowserNotSupported = function isBrowserNotSupported() {
|
||||
return document.all && !window.atob;
|
||||
};
|
||||
|
||||
var initializeScroll = function initializeScroll() {
|
||||
// Extend elements objects in $aosElements with their positions
|
||||
$aosElements = prepare($aosElements, options);
|
||||
// Perform scroll event, to refresh view and show/hide elements
|
||||
handleScroll($aosElements);
|
||||
|
||||
/**
|
||||
* Handle scroll event to animate elements on scroll
|
||||
*/
|
||||
window.addEventListener('scroll', throttle(function () {
|
||||
handleScroll($aosElements, options.once);
|
||||
}, options.throttleDelay));
|
||||
|
||||
return $aosElements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh AOS
|
||||
*/
|
||||
var refresh = function refresh() {
|
||||
var initialize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||
|
||||
// Allow refresh only when it was first initialized on startEvent
|
||||
if (initialize) initialized = true;
|
||||
if (initialized) initializeScroll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hard refresh
|
||||
* create array with new elements and trigger refresh
|
||||
*/
|
||||
var refreshHard = function refreshHard() {
|
||||
$aosElements = elements();
|
||||
|
||||
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||
return disable();
|
||||
}
|
||||
|
||||
refresh();
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable AOS
|
||||
* Remove all attributes to reset applied styles
|
||||
*/
|
||||
var disable = function disable() {
|
||||
$aosElements.forEach(function (el, i) {
|
||||
el.node.removeAttribute('data-aos');
|
||||
el.node.removeAttribute('data-aos-easing');
|
||||
el.node.removeAttribute('data-aos-duration');
|
||||
el.node.removeAttribute('data-aos-delay');
|
||||
|
||||
if (options.initClassName) {
|
||||
el.node.classList.remove(options.initClassName);
|
||||
}
|
||||
|
||||
if (options.animatedClassName) {
|
||||
el.node.classList.remove(options.animatedClassName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if AOS should be disabled based on provided setting
|
||||
*/
|
||||
var isDisabled = function isDisabled(optionDisable) {
|
||||
return optionDisable === true || optionDisable === 'mobile' && detect.mobile() || optionDisable === 'phone' && detect.phone() || optionDisable === 'tablet' && detect.tablet() || typeof optionDisable === 'function' && optionDisable() === true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializing AOS
|
||||
* - Create options merging defaults with user defined options
|
||||
* - Set attributes on <body> as global setting - css relies on it
|
||||
* - Attach preparing elements to options.startEvent,
|
||||
* window resize and orientation change
|
||||
* - Attach function that handle scroll and everything connected to it
|
||||
* to window scroll event and fire once document is ready to set initial state
|
||||
*/
|
||||
var init = function init(settings) {
|
||||
options = _extends(options, settings);
|
||||
|
||||
// Create initial array with elements -> to be fullfilled later with prepare()
|
||||
$aosElements = elements();
|
||||
|
||||
/**
|
||||
* Disable mutation observing if not supported
|
||||
*/
|
||||
if (!options.disableMutationObserver && !observer.isSupported()) {
|
||||
console.info('\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n ');
|
||||
options.disableMutationObserver = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe [aos] elements
|
||||
* If something is loaded by AJAX
|
||||
* it'll refresh plugin automatically
|
||||
*/
|
||||
if (!options.disableMutationObserver) {
|
||||
observer.ready('[data-aos]', refreshHard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't init plugin if option `disable` is set
|
||||
* or when browser is not supported
|
||||
*/
|
||||
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||
return disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global settings on body, based on options
|
||||
* so CSS can use it
|
||||
*/
|
||||
document.querySelector('body').setAttribute('data-aos-easing', options.easing);
|
||||
|
||||
document.querySelector('body').setAttribute('data-aos-duration', options.duration);
|
||||
|
||||
document.querySelector('body').setAttribute('data-aos-delay', options.delay);
|
||||
|
||||
/**
|
||||
* Handle initializing
|
||||
*/
|
||||
if (['DOMContentLoaded', 'load'].indexOf(options.startEvent) === -1) {
|
||||
// Listen to options.startEvent and initialize AOS
|
||||
document.addEventListener(options.startEvent, function () {
|
||||
refresh(true);
|
||||
});
|
||||
} else {
|
||||
window.addEventListener('load', function () {
|
||||
refresh(true);
|
||||
});
|
||||
}
|
||||
|
||||
if (options.startEvent === 'DOMContentLoaded' && ['complete', 'interactive'].indexOf(document.readyState) > -1) {
|
||||
// Initialize AOS if default startEvent was already fired
|
||||
refresh(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh plugin on window resize or orientation change
|
||||
*/
|
||||
window.addEventListener('resize', debounce(refresh, options.debounceDelay, true));
|
||||
|
||||
window.addEventListener('orientationchange', debounce(refresh, options.debounceDelay, true));
|
||||
|
||||
return $aosElements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export Public API
|
||||
*/
|
||||
|
||||
var aos = {
|
||||
init: init,
|
||||
refresh: refresh,
|
||||
refreshHard: refreshHard
|
||||
};
|
||||
|
||||
module.exports = aos;
|
||||
1
public/themes/template3/assets/vendor/aos/aos.css
vendored
Normal file
610
public/themes/template3/assets/vendor/aos/aos.esm.js
vendored
Normal file
@ -0,0 +1,610 @@
|
||||
import throttle from 'lodash.throttle';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
var callback = function callback() {};
|
||||
|
||||
function containsAOSNode(nodes) {
|
||||
var i = void 0,
|
||||
currentNode = void 0,
|
||||
result = void 0;
|
||||
|
||||
for (i = 0; i < nodes.length; i += 1) {
|
||||
currentNode = nodes[i];
|
||||
|
||||
if (currentNode.dataset && currentNode.dataset.aos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
result = currentNode.children && containsAOSNode(currentNode.children);
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function check(mutations) {
|
||||
if (!mutations) return;
|
||||
|
||||
mutations.forEach(function (mutation) {
|
||||
var addedNodes = Array.prototype.slice.call(mutation.addedNodes);
|
||||
var removedNodes = Array.prototype.slice.call(mutation.removedNodes);
|
||||
var allNodes = addedNodes.concat(removedNodes);
|
||||
|
||||
if (containsAOSNode(allNodes)) {
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMutationObserver() {
|
||||
return window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
|
||||
}
|
||||
|
||||
function isSupported() {
|
||||
return !!getMutationObserver();
|
||||
}
|
||||
|
||||
function ready(selector, fn) {
|
||||
var doc = window.document;
|
||||
var MutationObserver = getMutationObserver();
|
||||
|
||||
var observer = new MutationObserver(check);
|
||||
callback = fn;
|
||||
|
||||
observer.observe(doc.documentElement, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
removedNodes: true
|
||||
});
|
||||
}
|
||||
|
||||
var observer = { isSupported: isSupported, ready: ready };
|
||||
|
||||
var classCallCheck = function (instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
};
|
||||
|
||||
var createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
var _extends = Object.assign || function (target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
|
||||
for (var key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
/**
|
||||
* Device detector
|
||||
*/
|
||||
|
||||
var fullNameRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
|
||||
var prefixRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||
var fullNameMobileRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
|
||||
var prefixMobileRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
|
||||
|
||||
function ua() {
|
||||
return navigator.userAgent || navigator.vendor || window.opera || '';
|
||||
}
|
||||
|
||||
var Detector = function () {
|
||||
function Detector() {
|
||||
classCallCheck(this, Detector);
|
||||
}
|
||||
|
||||
createClass(Detector, [{
|
||||
key: 'phone',
|
||||
value: function phone() {
|
||||
var a = ua();
|
||||
return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
|
||||
}
|
||||
}, {
|
||||
key: 'mobile',
|
||||
value: function mobile() {
|
||||
var a = ua();
|
||||
return !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
|
||||
}
|
||||
}, {
|
||||
key: 'tablet',
|
||||
value: function tablet() {
|
||||
return this.mobile() && !this.phone();
|
||||
}
|
||||
|
||||
// http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c
|
||||
|
||||
}, {
|
||||
key: 'ie11',
|
||||
value: function ie11() {
|
||||
return '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
|
||||
}
|
||||
}]);
|
||||
return Detector;
|
||||
}();
|
||||
|
||||
var detect = new Detector();
|
||||
|
||||
/**
|
||||
* Adds multiple classes on node
|
||||
* @param {DOMNode} node
|
||||
* @param {array} classes
|
||||
*/
|
||||
var addClasses = function addClasses(node, classes) {
|
||||
return classes && classes.forEach(function (className) {
|
||||
return node.classList.add(className);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes multiple classes from node
|
||||
* @param {DOMNode} node
|
||||
* @param {array} classes
|
||||
*/
|
||||
var removeClasses = function removeClasses(node, classes) {
|
||||
return classes && classes.forEach(function (className) {
|
||||
return node.classList.remove(className);
|
||||
});
|
||||
};
|
||||
|
||||
var fireEvent = function fireEvent(eventName, data) {
|
||||
var customEvent = void 0;
|
||||
|
||||
if (detect.ie11()) {
|
||||
customEvent = document.createEvent('CustomEvent');
|
||||
customEvent.initCustomEvent(eventName, true, true, { detail: data });
|
||||
} else {
|
||||
customEvent = new CustomEvent(eventName, {
|
||||
detail: data
|
||||
});
|
||||
}
|
||||
|
||||
return document.dispatchEvent(customEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or remove aos-animate class
|
||||
* @param {node} el element
|
||||
* @param {int} top scrolled distance
|
||||
*/
|
||||
var applyClasses = function applyClasses(el, top) {
|
||||
var options = el.options,
|
||||
position = el.position,
|
||||
node = el.node,
|
||||
data = el.data;
|
||||
|
||||
|
||||
var hide = function hide() {
|
||||
if (!el.animated) return;
|
||||
|
||||
removeClasses(node, options.animatedClassNames);
|
||||
fireEvent('aos:out', node);
|
||||
|
||||
if (el.options.id) {
|
||||
fireEvent('aos:in:' + el.options.id, node);
|
||||
}
|
||||
|
||||
el.animated = false;
|
||||
};
|
||||
|
||||
var show = function show() {
|
||||
if (el.animated) return;
|
||||
|
||||
addClasses(node, options.animatedClassNames);
|
||||
|
||||
fireEvent('aos:in', node);
|
||||
if (el.options.id) {
|
||||
fireEvent('aos:in:' + el.options.id, node);
|
||||
}
|
||||
|
||||
el.animated = true;
|
||||
};
|
||||
|
||||
if (options.mirror && top >= position.out && !options.once) {
|
||||
hide();
|
||||
} else if (top >= position.in) {
|
||||
show();
|
||||
} else if (el.animated && !options.once) {
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll logic - add or remove 'aos-animate' class on scroll
|
||||
*
|
||||
* @param {array} $elements array of elements nodes
|
||||
* @return {void}
|
||||
*/
|
||||
var handleScroll = function handleScroll($elements) {
|
||||
return $elements.forEach(function (el, i) {
|
||||
return applyClasses(el, window.pageYOffset);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get offset of DOM element
|
||||
* like there were no transforms applied on it
|
||||
*
|
||||
* @param {Node} el [DOM element]
|
||||
* @return {Object} [top and left offset]
|
||||
*/
|
||||
var offset = function offset(el) {
|
||||
var _x = 0;
|
||||
var _y = 0;
|
||||
|
||||
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
|
||||
_x += el.offsetLeft - (el.tagName != 'BODY' ? el.scrollLeft : 0);
|
||||
_y += el.offsetTop - (el.tagName != 'BODY' ? el.scrollTop : 0);
|
||||
el = el.offsetParent;
|
||||
}
|
||||
|
||||
return {
|
||||
top: _y,
|
||||
left: _x
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get inline option with a fallback.
|
||||
*
|
||||
* @param {Node} el [Dom element]
|
||||
* @param {String} key [Option key]
|
||||
* @param {String} fallback [Default (fallback) value]
|
||||
* @return {Mixed} [Option set with inline attributes or fallback value if not set]
|
||||
*/
|
||||
|
||||
var getInlineOption = (function (el, key, fallback) {
|
||||
var attr = el.getAttribute('data-aos-' + key);
|
||||
|
||||
if (typeof attr !== 'undefined') {
|
||||
if (attr === 'true') {
|
||||
return true;
|
||||
} else if (attr === 'false') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return attr || fallback;
|
||||
});
|
||||
|
||||
/**
|
||||
* Calculate offset
|
||||
* basing on element's settings like:
|
||||
* - anchor
|
||||
* - offset
|
||||
*
|
||||
* @param {Node} el [Dom element]
|
||||
* @return {Integer} [Final offset that will be used to trigger animation in good position]
|
||||
*/
|
||||
|
||||
var getPositionIn = function getPositionIn(el, defaultOffset, defaultAnchorPlacement) {
|
||||
var windowHeight = window.innerHeight;
|
||||
var anchor = getInlineOption(el, 'anchor');
|
||||
var inlineAnchorPlacement = getInlineOption(el, 'anchor-placement');
|
||||
var additionalOffset = Number(getInlineOption(el, 'offset', inlineAnchorPlacement ? 0 : defaultOffset));
|
||||
var anchorPlacement = inlineAnchorPlacement || defaultAnchorPlacement;
|
||||
var finalEl = el;
|
||||
|
||||
if (anchor && document.querySelectorAll(anchor)) {
|
||||
finalEl = document.querySelectorAll(anchor)[0];
|
||||
}
|
||||
|
||||
var triggerPoint = offset(finalEl).top - windowHeight;
|
||||
|
||||
switch (anchorPlacement) {
|
||||
case 'top-bottom':
|
||||
// Default offset
|
||||
break;
|
||||
case 'center-bottom':
|
||||
triggerPoint += finalEl.offsetHeight / 2;
|
||||
break;
|
||||
case 'bottom-bottom':
|
||||
triggerPoint += finalEl.offsetHeight;
|
||||
break;
|
||||
case 'top-center':
|
||||
triggerPoint += windowHeight / 2;
|
||||
break;
|
||||
case 'center-center':
|
||||
triggerPoint += windowHeight / 2 + finalEl.offsetHeight / 2;
|
||||
break;
|
||||
case 'bottom-center':
|
||||
triggerPoint += windowHeight / 2 + finalEl.offsetHeight;
|
||||
break;
|
||||
case 'top-top':
|
||||
triggerPoint += windowHeight;
|
||||
break;
|
||||
case 'bottom-top':
|
||||
triggerPoint += windowHeight + finalEl.offsetHeight;
|
||||
break;
|
||||
case 'center-top':
|
||||
triggerPoint += windowHeight + finalEl.offsetHeight / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return triggerPoint + additionalOffset;
|
||||
};
|
||||
|
||||
var getPositionOut = function getPositionOut(el, defaultOffset) {
|
||||
var windowHeight = window.innerHeight;
|
||||
var anchor = getInlineOption(el, 'anchor');
|
||||
var additionalOffset = getInlineOption(el, 'offset', defaultOffset);
|
||||
var finalEl = el;
|
||||
|
||||
if (anchor && document.querySelectorAll(anchor)) {
|
||||
finalEl = document.querySelectorAll(anchor)[0];
|
||||
}
|
||||
|
||||
var elementOffsetTop = offset(finalEl).top;
|
||||
|
||||
return elementOffsetTop + finalEl.offsetHeight - additionalOffset;
|
||||
};
|
||||
|
||||
/* Clearing variables */
|
||||
|
||||
var prepare = function prepare($elements, options) {
|
||||
$elements.forEach(function (el, i) {
|
||||
var mirror = getInlineOption(el.node, 'mirror', options.mirror);
|
||||
var once = getInlineOption(el.node, 'once', options.once);
|
||||
var id = getInlineOption(el.node, 'id');
|
||||
var customClassNames = options.useClassNames && el.node.getAttribute('data-aos');
|
||||
|
||||
var animatedClassNames = [options.animatedClassName].concat(customClassNames ? customClassNames.split(' ') : []).filter(function (className) {
|
||||
return typeof className === 'string';
|
||||
});
|
||||
|
||||
if (options.initClassName) {
|
||||
el.node.classList.add(options.initClassName);
|
||||
}
|
||||
|
||||
el.position = {
|
||||
in: getPositionIn(el.node, options.offset, options.anchorPlacement),
|
||||
out: mirror && getPositionOut(el.node, options.offset)
|
||||
};
|
||||
|
||||
el.options = {
|
||||
once: once,
|
||||
mirror: mirror,
|
||||
animatedClassNames: animatedClassNames,
|
||||
id: id
|
||||
};
|
||||
});
|
||||
|
||||
return $elements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate initial array with elements as objects
|
||||
* This array will be extended later with elements attributes values
|
||||
* like 'position'
|
||||
*/
|
||||
var elements = (function () {
|
||||
var elements = document.querySelectorAll('[data-aos]');
|
||||
return Array.prototype.map.call(elements, function (node) {
|
||||
return { node: node };
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* *******************************************************
|
||||
* AOS (Animate on scroll) - wowjs alternative
|
||||
* made to animate elements on scroll in both directions
|
||||
* *******************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Private variables
|
||||
*/
|
||||
var $aosElements = [];
|
||||
var initialized = false;
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
var options = {
|
||||
offset: 120,
|
||||
delay: 0,
|
||||
easing: 'ease',
|
||||
duration: 400,
|
||||
disable: false,
|
||||
once: false,
|
||||
mirror: false,
|
||||
anchorPlacement: 'top-bottom',
|
||||
startEvent: 'DOMContentLoaded',
|
||||
animatedClassName: 'aos-animate',
|
||||
initClassName: 'aos-init',
|
||||
useClassNames: false,
|
||||
disableMutationObserver: false,
|
||||
throttleDelay: 99,
|
||||
debounceDelay: 50
|
||||
};
|
||||
|
||||
// Detect not supported browsers (<=IE9)
|
||||
// http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
|
||||
var isBrowserNotSupported = function isBrowserNotSupported() {
|
||||
return document.all && !window.atob;
|
||||
};
|
||||
|
||||
var initializeScroll = function initializeScroll() {
|
||||
// Extend elements objects in $aosElements with their positions
|
||||
$aosElements = prepare($aosElements, options);
|
||||
// Perform scroll event, to refresh view and show/hide elements
|
||||
handleScroll($aosElements);
|
||||
|
||||
/**
|
||||
* Handle scroll event to animate elements on scroll
|
||||
*/
|
||||
window.addEventListener('scroll', throttle(function () {
|
||||
handleScroll($aosElements, options.once);
|
||||
}, options.throttleDelay));
|
||||
|
||||
return $aosElements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh AOS
|
||||
*/
|
||||
var refresh = function refresh() {
|
||||
var initialize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||
|
||||
// Allow refresh only when it was first initialized on startEvent
|
||||
if (initialize) initialized = true;
|
||||
if (initialized) initializeScroll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hard refresh
|
||||
* create array with new elements and trigger refresh
|
||||
*/
|
||||
var refreshHard = function refreshHard() {
|
||||
$aosElements = elements();
|
||||
|
||||
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||
return disable();
|
||||
}
|
||||
|
||||
refresh();
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable AOS
|
||||
* Remove all attributes to reset applied styles
|
||||
*/
|
||||
var disable = function disable() {
|
||||
$aosElements.forEach(function (el, i) {
|
||||
el.node.removeAttribute('data-aos');
|
||||
el.node.removeAttribute('data-aos-easing');
|
||||
el.node.removeAttribute('data-aos-duration');
|
||||
el.node.removeAttribute('data-aos-delay');
|
||||
|
||||
if (options.initClassName) {
|
||||
el.node.classList.remove(options.initClassName);
|
||||
}
|
||||
|
||||
if (options.animatedClassName) {
|
||||
el.node.classList.remove(options.animatedClassName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if AOS should be disabled based on provided setting
|
||||
*/
|
||||
var isDisabled = function isDisabled(optionDisable) {
|
||||
return optionDisable === true || optionDisable === 'mobile' && detect.mobile() || optionDisable === 'phone' && detect.phone() || optionDisable === 'tablet' && detect.tablet() || typeof optionDisable === 'function' && optionDisable() === true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializing AOS
|
||||
* - Create options merging defaults with user defined options
|
||||
* - Set attributes on <body> as global setting - css relies on it
|
||||
* - Attach preparing elements to options.startEvent,
|
||||
* window resize and orientation change
|
||||
* - Attach function that handle scroll and everything connected to it
|
||||
* to window scroll event and fire once document is ready to set initial state
|
||||
*/
|
||||
var init = function init(settings) {
|
||||
options = _extends(options, settings);
|
||||
|
||||
// Create initial array with elements -> to be fullfilled later with prepare()
|
||||
$aosElements = elements();
|
||||
|
||||
/**
|
||||
* Disable mutation observing if not supported
|
||||
*/
|
||||
if (!options.disableMutationObserver && !observer.isSupported()) {
|
||||
console.info('\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n ');
|
||||
options.disableMutationObserver = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe [aos] elements
|
||||
* If something is loaded by AJAX
|
||||
* it'll refresh plugin automatically
|
||||
*/
|
||||
if (!options.disableMutationObserver) {
|
||||
observer.ready('[data-aos]', refreshHard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't init plugin if option `disable` is set
|
||||
* or when browser is not supported
|
||||
*/
|
||||
if (isDisabled(options.disable) || isBrowserNotSupported()) {
|
||||
return disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global settings on body, based on options
|
||||
* so CSS can use it
|
||||
*/
|
||||
document.querySelector('body').setAttribute('data-aos-easing', options.easing);
|
||||
|
||||
document.querySelector('body').setAttribute('data-aos-duration', options.duration);
|
||||
|
||||
document.querySelector('body').setAttribute('data-aos-delay', options.delay);
|
||||
|
||||
/**
|
||||
* Handle initializing
|
||||
*/
|
||||
if (['DOMContentLoaded', 'load'].indexOf(options.startEvent) === -1) {
|
||||
// Listen to options.startEvent and initialize AOS
|
||||
document.addEventListener(options.startEvent, function () {
|
||||
refresh(true);
|
||||
});
|
||||
} else {
|
||||
window.addEventListener('load', function () {
|
||||
refresh(true);
|
||||
});
|
||||
}
|
||||
|
||||
if (options.startEvent === 'DOMContentLoaded' && ['complete', 'interactive'].indexOf(document.readyState) > -1) {
|
||||
// Initialize AOS if default startEvent was already fired
|
||||
refresh(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh plugin on window resize or orientation change
|
||||
*/
|
||||
window.addEventListener('resize', debounce(refresh, options.debounceDelay, true));
|
||||
|
||||
window.addEventListener('orientationchange', debounce(refresh, options.debounceDelay, true));
|
||||
|
||||
return $aosElements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export Public API
|
||||
*/
|
||||
|
||||
var aos = {
|
||||
init: init,
|
||||
refresh: refresh,
|
||||
refreshHard: refreshHard
|
||||
};
|
||||
|
||||
export default aos;
|
||||